diff options
| author | ruki <[email protected]> | 2024-01-25 00:44:24 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2024-01-25 12:10:22 +0800 |
| commit | fba862bb13f8780dd01ce85be8ac7c5709289b6d (patch) | |
| tree | 6a5224ad975348b8a8a480d421423c4f309d56cc | |
| parent | 21aeb8d32ec1892904cc34a57e9c2c3d83b9f1bd (diff) | |
lock and unlock coroutine #4645
| -rw-r--r-- | xmake/core/base/scheduler.lua | 80 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/core/base/scheduler.lua | 16 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/lib/detect/find_program.lua | 20 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/lib/detect/find_programver.lua | 20 | ||||
| -rw-r--r-- | xmake/modules/lib/detect/has_flags.lua | 15 |
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 |
