summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2024-01-28 23:13:00 +0800
committerGitHub <[email protected]>2024-01-28 23:13:00 +0800
commit1d4a3e7645143f98ea1089b63153834f7c913d4b (patch)
tree60ac1d7e55fbea740a0594ce0e81987886d1ccca
parentf383a6e46a9072c639d6df97bba161b8ec6add7c (diff)
parent2727f450c5bdbd5b440de5fb44d4afcb6fcea2eb (diff)
Merge pull request #4648 from xmake-io/co
lock and unlock coroutine #4645
-rw-r--r--xmake/core/base/scheduler.lua109
-rw-r--r--xmake/core/sandbox/modules/import/core/base/scheduler.lua16
-rw-r--r--xmake/core/sandbox/modules/import/lib/detect/find_program.lua22
-rw-r--r--xmake/core/sandbox/modules/import/lib/detect/find_programver.lua22
-rw-r--r--xmake/modules/lib/detect/features.lua14
-rw-r--r--xmake/modules/lib/detect/has_flags.lua14
6 files changed, 146 insertions, 51 deletions
diff --git a/xmake/core/base/scheduler.lua b/xmake/core/base/scheduler.lua
index 52a896728..12808a162 100644
--- a/xmake/core/base/scheduler.lua
+++ b/xmake/core/base/scheduler.lua
@@ -434,7 +434,29 @@ end
-- resume the given coroutine
function scheduler:co_resume(co, ...)
- return coroutine.resume(co:thread(), ...)
+
+ -- do resume
+ local ok, errors = coroutine.resume(co:thread(), ...)
+
+ local running = self:co_running()
+ if running then
+
+ -- has the current directory been changed? restore it
+ local curdir = self._CO_CURDIR_HASH
+ local olddir = self._CO_CURDIRS and self._CO_CURDIRS[running] or nil
+ if olddir and curdir ~= olddir[1] then -- hash changed?
+ os.cd(olddir[2])
+ end
+
+ -- has the current environments been changed? restore it
+ local curenvs = self._CO_CURENVS_HASH
+ local oldenvs = self._CO_CURENVS and self._CO_CURENVS[running] or nil
+ if oldenvs and curenvs ~= oldenvs[1] and running:is_isolated() then -- hash changed?
+ os.setenvs(oldenvs[2])
+ end
+ end
+
+ return ok, errors
end
-- suspend the current coroutine
@@ -443,7 +465,7 @@ function scheduler:co_suspend(...)
-- suspend it
local results = table.pack(coroutine.yield(...))
- -- Has the current directory been changed? restore it
+ -- has the current directory been changed? restore it
local running = assert(self:co_running())
local curdir = self._CO_CURDIR_HASH
local olddir = self._CO_CURDIRS and self._CO_CURDIRS[running] or nil
@@ -451,7 +473,7 @@ function scheduler:co_suspend(...)
os.cd(olddir[2])
end
- -- Has the current environments been changed? restore it
+ -- has the current environments been changed? restore it
local curenvs = self._CO_CURENVS_HASH
local oldenvs = self._CO_CURENVS and self._CO_CURENVS[running] or nil
if oldenvs and curenvs ~= oldenvs[1] and running:is_isolated() then -- hash changed?
@@ -499,6 +521,87 @@ function scheduler:co_sleep(ms)
return true
end
+-- lock the current coroutine
+function scheduler:co_lock(lockname)
+
+ -- get the running coroutine
+ local running = self:co_running()
+ if not running then
+ return false, "we must call co_lock() in coroutine with scheduler!"
+ end
+
+ -- is stopped?
+ if not self._STARTED then
+ return false, "the scheduler is stopped!"
+ end
+
+ -- do lock
+ local co_locked_tasks = self._CO_LOCKED_TASKS
+ if co_locked_tasks == nil then
+ co_locked_tasks = {}
+ self._CO_LOCKED_TASKS = co_locked_tasks
+ end
+ while true do
+
+ -- try to lock it
+ if co_locked_tasks[lockname] == nil then
+ co_locked_tasks[lockname] = running
+ return true
+ -- has been locked by the current coroutine
+ elseif co_locked_tasks[lockname] == running then
+ return true
+ end
+
+ -- register timeout task to timer
+ local function timer_callback (cancel)
+ if co_locked_tasks[lockname] == nil then
+ if running:is_suspended() then
+ return self:co_resume(running)
+ end
+ else
+ self:_timer():post(timer_callback, 500)
+ end
+ return true
+ end
+ self:_timer():post(timer_callback, 500)
+
+ -- wait
+ self:co_suspend()
+ end
+ return true
+end
+
+-- unlock the current coroutine
+function scheduler:co_unlock(lockname)
+
+ -- get the running coroutine
+ local running = self:co_running()
+ if not running then
+ return false, "we must call co_unlock() in coroutine with scheduler!"
+ end
+
+ -- is stopped?
+ if not self._STARTED then
+ return false, "the scheduler is stopped!"
+ end
+
+ -- do unlock
+ local co_locked_tasks = self._CO_LOCKED_TASKS
+ if co_locked_tasks == nil then
+ co_locked_tasks = {}
+ self._CO_LOCKED_TASKS = co_locked_tasks
+ end
+ if co_locked_tasks[lockname] == nil then
+ return false, string.format("we need to call lock(%s) first before calling unlock(%s)", lockname, lockname)
+ end
+ if co_locked_tasks[lockname] == running then
+ co_locked_tasks[lockname] = nil
+ else
+ return false, string.format("unlock(%s) is called in other %s", lockname, running)
+ end
+ return true
+end
+
-- get the given coroutine group
function scheduler:co_group(name)
return self._CO_GROUPS and self._CO_GROUPS[name]
diff --git a/xmake/core/sandbox/modules/import/core/base/scheduler.lua b/xmake/core/sandbox/modules/import/core/base/scheduler.lua
index 50832d88c..e69ee4187 100644
--- a/xmake/core/sandbox/modules/import/core/base/scheduler.lua
+++ b/xmake/core/sandbox/modules/import/core/base/scheduler.lua
@@ -84,6 +84,22 @@ function sandbox_core_base_scheduler.co_sleep(ms)
end
end
+-- lock the current coroutine
+function sandbox_core_base_scheduler.co_lock(lockname)
+ local ok, errors = scheduler:co_lock(lockname)
+ if not ok then
+ raise(errors)
+ end
+end
+
+-- unlock the current coroutine
+function sandbox_core_base_scheduler.co_unlock(lockname)
+ local ok, errors = scheduler:co_unlock(lockname)
+ if not ok then
+ raise(errors)
+ end
+end
+
-- get coroutine group with the given name
function sandbox_core_base_scheduler.co_group(name)
return scheduler:co_group(name)
diff --git a/xmake/core/sandbox/modules/import/lib/detect/find_program.lua b/xmake/core/sandbox/modules/import/lib/detect/find_program.lua
index c0755cbf3..6b8aeb13a 100644
--- a/xmake/core/sandbox/modules/import/lib/detect/find_program.lua
+++ b/xmake/core/sandbox/modules/import/lib/detect/find_program.lua
@@ -37,9 +37,6 @@ local raise = require("sandbox/modules/raise")
local vformat = require("sandbox/modules/vformat")
local scheduler = require("sandbox/modules/import/core/base/scheduler")
--- globals
-local checking = nil
-
-- do check
function sandbox_lib_detect_find_program._do_check(program, opt)
@@ -272,16 +269,6 @@ end
-- @endcode
--
function sandbox_lib_detect_find_program.main(name, opt)
-
- -- @note avoid detect the same program in the same time leading to deadlock if running in the coroutine (e.g. ccache)
- local coroutine_running = scheduler.co_running()
- if coroutine_running then
- while checking ~= nil and checking == name do
- scheduler.co_yield()
- end
- end
-
- -- init options
opt = opt or {}
-- init cachekey
@@ -290,9 +277,15 @@ function sandbox_lib_detect_find_program.main(name, opt)
cachekey = cachekey .. "_" .. opt.cachekey
end
+ -- @see https://github.com/xmake-io/xmake/issues/4645
+ -- @note avoid detect the same program in the same time leading to deadlock if running in the coroutine (e.g. ccache)
+ local lockname = cachekey .. name
+ scheduler.co_lock(lockname)
+
-- attempt to get result from cache first
local result = detectcache:get2(cachekey, name)
if result ~= nil and not opt.force then
+ scheduler.co_unlock(lockname)
return result and result or nil
end
@@ -309,11 +302,9 @@ function sandbox_lib_detect_find_program.main(name, opt)
end
-- find executable program
- checking = coroutine_running and name or nil
profiler:enter("find_program", name)
result = sandbox_lib_detect_find_program._find(name, paths, opt)
profiler:leave("find_program", name)
- checking = nil
-- cache result
detectcache:set2(cachekey, name, result and result or false)
@@ -327,6 +318,7 @@ function sandbox_lib_detect_find_program.main(name, opt)
utils.cprint("checking for %s ... ${color.nothing}${text.nothing}", name)
end
end
+ scheduler.co_unlock(lockname)
return result
end
diff --git a/xmake/core/sandbox/modules/import/lib/detect/find_programver.lua b/xmake/core/sandbox/modules/import/lib/detect/find_programver.lua
index 988180ecb..c8ca91826 100644
--- a/xmake/core/sandbox/modules/import/lib/detect/find_programver.lua
+++ b/xmake/core/sandbox/modules/import/lib/detect/find_programver.lua
@@ -35,9 +35,6 @@ local sandbox = require("sandbox/sandbox")
local raise = require("sandbox/modules/raise")
local scheduler = require("sandbox/modules/import/core/base/scheduler")
--- globals
-local checking = nil
-
-- find program version
--
-- @param program the program
@@ -56,32 +53,27 @@ local checking = nil
-- @endcode
--
function sandbox_lib_detect_find_programver.main(program, opt)
-
- -- init options
opt = opt or {}
- -- @note avoid detect the same program in the same time leading to deadlock if running in the coroutine (e.g. ccache)
- local coroutine_running = scheduler.co_running()
- if coroutine_running then
- while checking ~= nil and checking == program do
- scheduler.co_yield()
- end
- end
-
-- init cachekey
local cachekey = "find_programver"
if opt.cachekey then
cachekey = cachekey .. "_" .. opt.cachekey
end
+ -- @see https://github.com/xmake-io/xmake/issues/4645
+ -- @note avoid detect the same program in the same time leading to deadlock if running in the coroutine (e.g. ccache)
+ local lockname = cachekey .. program
+ scheduler.co_lock(lockname)
+
-- attempt to get result from cache first
local result = detectcache:get2(cachekey, program)
if result ~= nil and not opt.force then
+ scheduler.co_unlock(lockname)
return result and result or nil
end
-- attempt to get version output info
- checking = coroutine_running and program or nil
profiler:enter("find_programver", program)
local ok = false
local outdata = nil
@@ -96,7 +88,6 @@ function sandbox_lib_detect_find_programver.main(program, opt)
else
ok, outdata = os.iorunv(program, {command or "--version"}, {envs = opt.envs})
end
- checking = nil
profiler:leave("find_programver", program)
-- find version info
@@ -119,6 +110,7 @@ function sandbox_lib_detect_find_programver.main(program, opt)
-- save result
detectcache:set2(cachekey, program, result and result or false)
detectcache:save()
+ scheduler.co_unlock(lockname)
return result
end
diff --git a/xmake/modules/lib/detect/features.lua b/xmake/modules/lib/detect/features.lua
index 8b9bde45b..7a00b04b4 100644
--- a/xmake/modules/lib/detect/features.lua
+++ b/xmake/modules/lib/detect/features.lua
@@ -57,29 +57,25 @@ function main(name, opt)
_g._RESULTS = _g._RESULTS or {}
local results = _g._RESULTS
- -- @note avoid detect the same program in the same time if running in the coroutine (e.g. ccache)
- local coroutine_running = scheduler.co_running()
- if coroutine_running then
- while _g._checking ~= nil and _g._checking == key do
- scheduler.co_yield()
- end
- end
+ -- @see https://github.com/xmake-io/xmake/issues/4645
+ -- @note avoid detect the same program in the same time leading to deadlock if running in the coroutine (e.g. ccache)
+ scheduler.co_lock(key)
-- get result from the cache first
local result = results[key]
if result ~= nil then
+ scheduler.co_unlock(key)
return result
end
-- detect.tools.xxx.features(opt)?
- _g._checking = coroutine_running and key or nil
local features = import("detect.tools." .. tool.name .. ".features", {try = true})
if features then
result = features(opt)
end
- _g._checking = nil
result = result or {}
results[key] = result
+ scheduler.co_unlock(key)
return result
end
diff --git a/xmake/modules/lib/detect/has_flags.lua b/xmake/modules/lib/detect/has_flags.lua
index dc11db11c..abfae3a57 100644
--- a/xmake/modules/lib/detect/has_flags.lua
+++ b/xmake/modules/lib/detect/has_flags.lua
@@ -80,13 +80,9 @@ function main(name, flags, opt)
.. (tool.version or "") .. "_" .. (opt.toolkind or "")
.. "_" .. (opt.flagkind or "") .. "_" .. table.concat(opt.sysflags, " ") .. "_" .. opt.flagskey
- -- @note avoid detect the same program in the same time if running in the coroutine (e.g. ccache)
- local coroutine_running = scheduler.co_running()
- if coroutine_running then
- while _g._checking ~= nil and _g._checking == key do
- scheduler.co_yield()
- end
- end
+ -- @see https://github.com/xmake-io/xmake/issues/4645
+ -- @note avoid detect the same program in the same time leading to deadlock if running in the coroutine (e.g. ccache)
+ scheduler.co_lock(key)
-- attempt to get result from cache first
local cacheinfo = detectcache:get("lib.detect.has_flags")
@@ -98,6 +94,7 @@ function main(name, flags, opt)
end
local result = cacheinfo[key]
if result ~= nil and not opt.force then
+ scheduler.co_unlock(key)
return result
end
@@ -122,7 +119,6 @@ function main(name, flags, opt)
profiler.enter("has_flags", tool.name, checkflags[1])
-- detect.tools.xxx.has_flags(flags, opt)?
- _g._checking = coroutine_running and key or nil
local hasflags = import("detect.tools." .. tool.name .. ".has_flags", {try = true})
local errors = nil
if hasflags then
@@ -133,7 +129,6 @@ function main(name, flags, opt)
if opt.on_check then
result, errors = opt.on_check(result, errors)
end
- _g._checking = nil
result = result or false
-- stop profile
@@ -156,6 +151,7 @@ function main(name, flags, opt)
cacheinfo[key] = result
detectcache:set("lib.detect.has_flags", cacheinfo)
detectcache:save()
+ scheduler.co_unlock(key)
return result
end