diff options
| author | ruki <[email protected]> | 2024-01-28 23:13:00 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-01-28 23:13:00 +0800 |
| commit | 1d4a3e7645143f98ea1089b63153834f7c913d4b (patch) | |
| tree | 60ac1d7e55fbea740a0594ce0e81987886d1ccca | |
| parent | f383a6e46a9072c639d6df97bba161b8ec6add7c (diff) | |
| parent | 2727f450c5bdbd5b440de5fb44d4afcb6fcea2eb (diff) | |
Merge pull request #4648 from xmake-io/co
lock and unlock coroutine #4645
| -rw-r--r-- | xmake/core/base/scheduler.lua | 109 | ||||
| -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 | 22 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/lib/detect/find_programver.lua | 22 | ||||
| -rw-r--r-- | xmake/modules/lib/detect/features.lua | 14 | ||||
| -rw-r--r-- | xmake/modules/lib/detect/has_flags.lua | 14 |
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 |
