summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2024-01-25 00:44:24 +0800
committerruki <[email protected]>2024-01-25 12:10:22 +0800
commitfba862bb13f8780dd01ce85be8ac7c5709289b6d (patch)
tree6a5224ad975348b8a8a480d421423c4f309d56cc
parent21aeb8d32ec1892904cc34a57e9c2c3d83b9f1bd (diff)
lock and unlock coroutine #4645
-rw-r--r--xmake/core/base/scheduler.lua80
-rw-r--r--xmake/core/sandbox/modules/import/core/base/scheduler.lua16
-rw-r--r--xmake/core/sandbox/modules/import/lib/detect/find_program.lua20
-rw-r--r--xmake/core/sandbox/modules/import/lib/detect/find_programver.lua20
-rw-r--r--xmake/modules/lib/detect/has_flags.lua15
5 files changed, 111 insertions, 40 deletions
diff --git a/xmake/core/base/scheduler.lua b/xmake/core/base/scheduler.lua
index 52a896728..8256bdc43 100644
--- a/xmake/core/base/scheduler.lua
+++ b/xmake/core/base/scheduler.lua
@@ -499,6 +499,86 @@ 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
+ if co_locked_tasks[lockname] == nil then
+ co_locked_tasks[lockname] = running
+ return true
+ end
+
+ -- this lock has been occupied, we need to wait it
+ local co_waiting_tasks = self._CO_WAITING_TASKS
+ if co_waiting_tasks == nil then
+ co_waiting_tasks = {}
+ self._CO_WAITING_TASKS = co_waiting_tasks
+ end
+ co_waiting_tasks[lockname] = co_waiting_tasks[lockname] or {}
+ table.insert(co_waiting_tasks[lockname], running)
+
+ -- 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
+
+ -- 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
+ local co_waiting_tasks = self._CO_WAITING_TASKS
+ if co_waiting_tasks then
+ local waiting_tasks = co_waiting_tasks[lockname]
+ if waiting_tasks and #waiting_tasks > 0 then
+ co_waiting_tasks[lockname] = nil
+ for _, co_task in ipairs(waiting_tasks) do
+ local ok, errors = self:co_resume(co_task)
+ if not ok then
+ return false, errors
+ end
+ end
+ end
+ end
+ 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..84cd061ad 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
@@ -296,6 +283,10 @@ function sandbox_lib_detect_find_program.main(name, opt)
return result and result or nil
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(cachekey)
+
-- get paths from the opt.envs.PATH
-- @note the wrong `pathes` word will be discarded, but the interface parameters will still be compatible
local envs = opt.envs
@@ -309,11 +300,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 +316,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(cachekey)
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..2d2c158d6 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,18 +53,8 @@ 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
@@ -80,8 +67,11 @@ function sandbox_lib_detect_find_programver.main(program, opt)
return result and result or nil
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(cachekey)
+
-- 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 +86,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 +108,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(cachekey)
return result
end
diff --git a/xmake/modules/lib/detect/has_flags.lua b/xmake/modules/lib/detect/has_flags.lua
index dc11db11c..e45a7a5b3 100644
--- a/xmake/modules/lib/detect/has_flags.lua
+++ b/xmake/modules/lib/detect/has_flags.lua
@@ -80,14 +80,6 @@ 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
-
-- attempt to get result from cache first
local cacheinfo = detectcache:get("lib.detect.has_flags")
if not cacheinfo then
@@ -101,6 +93,10 @@ function main(name, flags, opt)
return result
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)
+
-- generate all checked flags
local checkflags = table.join(flags, opt.sysflags)
@@ -122,7 +118,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 +128,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 +150,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