From 6809e2d887d3f3f298b4e324a7580c55ac47562d Mon Sep 17 00:00:00 2001 From: ruki Date: Wed, 27 Aug 2025 23:23:00 +0800 Subject: move thread to base --- core/src/tbox/tbox | 2 +- tests/modules/thread/mutex.lua | 11 +- tests/modules/thread/sleep.lua | 4 +- xmake/core/base/thread.lua | 264 +++++++++++++++++++++ xmake/core/main.lua | 2 +- .../sandbox/modules/import/core/base/thread.lua | 122 ++++++++++ .../sandbox/modules/import/core/thread/thread.lua | 122 ---------- xmake/core/thread/thread.lua | 264 --------------------- 8 files changed, 395 insertions(+), 396 deletions(-) create mode 100644 xmake/core/base/thread.lua create mode 100644 xmake/core/sandbox/modules/import/core/base/thread.lua delete mode 100644 xmake/core/sandbox/modules/import/core/thread/thread.lua delete mode 100644 xmake/core/thread/thread.lua diff --git a/core/src/tbox/tbox b/core/src/tbox/tbox index 7af204577..91a110d06 160000 --- a/core/src/tbox/tbox +++ b/core/src/tbox/tbox @@ -1 +1 @@ -Subproject commit 7af204577394bb0364eef245f33e7cea6eed0596 +Subproject commit 91a110d061d6486a7a0826919e0c60caf7586a3a diff --git a/tests/modules/thread/mutex.lua b/tests/modules/thread/mutex.lua index 472b228db..0f1954c8a 100644 --- a/tests/modules/thread/mutex.lua +++ b/tests/modules/thread/mutex.lua @@ -1,8 +1,7 @@ -import("core.thread.thread") -import("core.thread.mutex") +import("core.base.thread") function callback(mutex) - import("core.thread.thread") + import("core.base.thread") print("%s: starting ..", thread.running()) local dt = os.mclock() for i = 1, 10 do @@ -16,9 +15,9 @@ function callback(mutex) end function main() - local m = mutex.new() - local t0 = thread.start_named("thread_0", callback, m) - local t1 = thread.start_named("thread_1", callback, m) + local mutex = thread.mutex() + local t0 = thread.start_named("thread_0", callback, mutex) + local t1 = thread.start_named("thread_1", callback, mutex) t0:wait(-1) t1:wait(-1) end diff --git a/tests/modules/thread/sleep.lua b/tests/modules/thread/sleep.lua index 294c04fea..2e21aa3d2 100644 --- a/tests/modules/thread/sleep.lua +++ b/tests/modules/thread/sleep.lua @@ -1,7 +1,7 @@ -import("core.thread.thread") +import("core.base.thread") function callback(id) - import("core.thread.thread") + import("core.base.thread") print("%s: %d starting ..", thread.running(), id) local dt = os.mclock() for i = 1, 10 do diff --git a/xmake/core/base/thread.lua b/xmake/core/base/thread.lua new file mode 100644 index 000000000..bdf1c0380 --- /dev/null +++ b/xmake/core/base/thread.lua @@ -0,0 +1,264 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-present, Xmake Open Source Community. +-- +-- @author ruki +-- @file thread.lua +-- + +-- define module +local thread = thread or {} +local _instance = _instance or {} + +-- load modules +local io = require("base/io") +local libc = require("base/libc") +local bytes = require("base/bytes") +local table = require("base/table") +local string = require("base/string") +local sandbox = require("sandbox/sandbox") + +-- the thread status +thread.STATUS_READY = 1 +thread.STATUS_RUNNING = 2 +thread.STATUS_SUSPENDED = 3 +thread.STATUS_DEAD = 4 + +-- new a thread +function _instance.new(callback, opt) + opt = opt or {} + local instance = table.inherit(_instance) + instance._NAME = opt.name or "anonymous" + instance._ARGV = opt.argv + instance._CALLBACK = callback + instance._STACKSIZE = opt.stacksize or 0 + instance._STATUS = thread.STATUS_READY + setmetatable(instance, _instance) + return instance +end + +-- get thread name +function _instance:name() + return self._NAME +end + +-- get cdata of thread +function _instance:cdata() + return self._HANLDE +end + +-- get thread status +function _instance:status() + return self._STATUS +end + +-- is ready? +function _instance:is_ready() + return self:status() == thread.STATUS_READY +end + +-- is running? +function _instance:is_running() + return self:status() == thread.STATUS_RUNNING +end + +-- is suspended? +function _instance:is_suspended() + return self:status() == thread.STATUS_SUSPENDED +end + +-- is dead? +function _instance:is_dead() + return self:status() == thread.STATUS_DEAD +end + +-- start thread +function _instance:start() + if not self:is_ready() then + return nil, string.format("%s: cannot start non-ready thread!", self) + end + assert(not self:cdata()) + + -- serialize and pass callback and arguments to this thread + -- we do not use string.serialize to serialize callback, because it's slower (deserialize) + -- and we cannot strip function debug info, we need to reserve _ENV, and other upvalue names + local callback = string._dump(self._CALLBACK) + local callinfo = {name = self:name(), argv = self._ARGV} + callinfo = string.serialize(callinfo, {strip = true, indent = false}) + + -- init and start thread + local handle, errors = thread.thread_init(self:name(), callback, callinfo, self._STACKSIZE) + if not handle then + return nil, errors or string.format("%s: failed to create thread!", self) + end + + self._HANLDE = handle + self._STATUS = thread.STATUS_RUNNING + return true +end + +-- suspend thread +function _instance:suspend() + if not self:is_running() then + return nil, string.format("%s: cannot suspend non-running thread!", self) + end + assert(self:cdata()) + + local ok, errors = thread.thread_suspend(self:cdata()) + if not ok then + return nil, errors or string.format("%s: failed to suspend thread!", self) + end + + self._STATUS = thread.STATUS_SUSPENDED + return true +end + +-- resume thread +function _instance:resume() + if not self:is_suspended() then + return nil, string.format("%s: cannot suspend non-suspended thread!", self) + end + assert(self:cdata()) + + local ok, errors = thread.thread_resume(self:cdata()) + if not ok then + return nil, errors or string.format("%s: failed to resume thread!", self) + end + + self._STATUS = thread.STATUS_RUNNING + return true +end + +-- wait thread +function _instance:wait(timeout) + if self:is_dead() then + return 1 + elseif self:is_ready() then + return -1, string.format("%s: cannot wait ready thread!", self) + end + assert(self:cdata()) + + local ok, errors = thread.thread_wait(self:cdata(), timeout) + if ok < 0 then + return -1, errors or string.format("%s: failed to resume thread!", self) + end + + if ok > 0 then + self._STATUS = thread.STATUS_DEAD + end + return ok +end + +-- tostring(thread) +function _instance:__tostring() + local status_strs = self._STATUS_STRS + if not status_strs then + status_strs = { + [thread.STATUS_READY] = "ready", + [thread.STATUS_RUNNING] = "running", + [thread.STATUS_SUSPENDED] = "suspended", + [thread.STATUS_DEAD] = "dead" + } + self._STATUS_STRS = status_strs + end + return string.format("", self:name(), status_strs[self:status()]) +end + +-- gc(thread) +function _instance:__gc() + if self:cdata() and self:is_dead() and io.thread_exit(self:cdata()) then + self._HANLDE = nil + end +end + +-- new a thread +-- +-- @param callback the thread callback +-- @param opt the thread options, e.g. {name = "", argv = {}, stacksize = 8192} +-- +-- @return the thread instance +-- +function thread.new(callback, opt) + if callback == nil then + return nil, "invalid thread, callback is nil" + end + return _instance.new(callback, opt) +end + +-- get the running thread name +function thread.running() + return thread._RUNNING +end + +-- run thread +function thread._run_thread(callback_str, callinfo_str) + + -- load callback info + local callinfo + local argv + local threadname + if callinfo_str then + local result, errors = string.deserialize(callinfo_str) + if not result then + return false, string.format("invalid thread callinfo, %s!", errors or "unknown") + end + callinfo = result + if callinfo then + argv = callinfo.argv + threadname = callinfo.name + end + end + + -- load callback + local callback + local fenvs = {} + if callback_str then + local script, errors = load(callback_str, "=(thread)", "b", fenvs) + if not script then + return false, string.format("cannot load thread(%s) callback, %s!", threadname or "unknown", errors or "unknown") + end + for i = 1, math.huge do + local upname, upvalue = debug.getupvalue(script, i) + if upname == nil or upname == "" then + break + end + if upvalue == nil then + return false, string.format("we cannot access upvalue(%s) in thread(%s) callback!", upname, threadname or "unknown") + end + end + callback = script + end + if not callback then + return false, "no thread callback" + end + + -- bind sandbox +-- local sandbox_inst, errors = sandbox.new(callback, { +-- filter = interp:filter(), rootdir = interp:rootdir(), namespace = interp:namespace()}) + local sandbox_inst, errors = sandbox.new(callback) + if not sandbox_inst then + return false, errors + end + + -- save the running thread name + thread._RUNNING = threadname + + -- do callback + return sandbox.load(sandbox_inst:script(), table.unpack(argv or {})) +end + +-- return module +return thread + diff --git a/xmake/core/main.lua b/xmake/core/main.lua index d1b1112a0..3f0a4484f 100644 --- a/xmake/core/main.lua +++ b/xmake/core/main.lua @@ -41,7 +41,7 @@ local project = require("project/project") local localcache = require("cache/localcache") local profiler = require("base/profiler") local debugger = require("base/debugger") -local thread = require("thread/thread") +local thread = require("base/thread") -- init the option menu local menu = diff --git a/xmake/core/sandbox/modules/import/core/base/thread.lua b/xmake/core/sandbox/modules/import/core/base/thread.lua new file mode 100644 index 000000000..f56466ddf --- /dev/null +++ b/xmake/core/sandbox/modules/import/core/base/thread.lua @@ -0,0 +1,122 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-present, Xmake Open Source Community. +-- +-- @author ruki +-- @file thread.lua +-- + +-- load modules +local utils = require("base/utils") +local string = require("base/string") +local thread = require("base/thread") +local raise = require("sandbox/modules/raise") + +-- define module +local sandbox_core_base_thread = sandbox_core_base_thread or {} +local sandbox_core_base_thread_instance = sandbox_core_base_thread_instance or {} + +-- export the thread status +sandbox_core_base_thread.STATUS_READY = thread.STATUS_READY +sandbox_core_base_thread.STATUS_RUNNING = thread.STATUS_RUNNING +sandbox_core_base_thread.STATUS_SUSPENDED = thread.STATUS_SUSPENDED +sandbox_core_base_thread.STATUS_DEAD = thread.STATUS_DEAD + +-- wrap thread +function _thread_wrap(instance) + + -- hook thread interfaces + local hooked = {} + for name, func in pairs(sandbox_core_base_thread_instance) do + if not name:startswith("_") and type(func) == "function" then + hooked["_" .. name] = instance["_" .. name] or instance[name] + hooked[name] = func + end + end + for name, func in pairs(hooked) do + instance[name] = func + end + return instance +end + +-- start thread +function sandbox_core_base_thread_instance.start(instance) + local ok, errors = instance:_start() + if not ok then + raise(errors) + end + return instance +end + +-- suspend thread +function sandbox_core_base_thread_instance.suspend(instance) + local ok, errors = instance:_suspend() + if not ok then + raise(errors) + end +end + +-- resume thread +function sandbox_core_base_thread_instance.resume(instance) + local ok, errors = instance:_resume() + if not ok then + raise(errors) + end +end + +-- wait thread +function sandbox_core_base_thread_instance.wait(instance, timeout) + local ok, errors = instance:_wait(timeout) + if ok < 0 then + raise(errors) + end +end + +-- new thread +function sandbox_core_base_thread.new(callback, opt) + local instance, errors = thread.new(callback, opt) + if not instance then + raise(errors) + end + return _thread_wrap(instance) +end + +-- start a thread +function sandbox_core_base_thread.start(callback, ...) + return sandbox_core_base_thread.start_withopt(callback, {argv = table.pack(...)}) +end + +-- start a named thread +function sandbox_core_base_thread.start_named(name, callback, ...) + return sandbox_core_base_thread.start_withopt(callback, {name = name, argv = table.pack(...)}) +end + +-- start a thread with options +function sandbox_core_base_thread.start_withopt(callback, opt) + return sandbox_core_base_thread.new(callback, opt):start() +end + +-- get the running thread +function sandbox_core_base_thread.running() + local instance, errors = thread.running() + if not instance then + raise(errors) + end + return instance +end + +-- return module +return sandbox_core_base_thread + diff --git a/xmake/core/sandbox/modules/import/core/thread/thread.lua b/xmake/core/sandbox/modules/import/core/thread/thread.lua deleted file mode 100644 index f90ec339f..000000000 --- a/xmake/core/sandbox/modules/import/core/thread/thread.lua +++ /dev/null @@ -1,122 +0,0 @@ ---!A cross-platform build utility based on Lua --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. --- --- Copyright (C) 2015-present, Xmake Open Source Community. --- --- @author ruki --- @file thread.lua --- - --- load modules -local utils = require("base/utils") -local string = require("base/string") -local thread = require("thread/thread") -local raise = require("sandbox/modules/raise") - --- define module -local sandbox_core_thread = sandbox_core_thread or {} -local sandbox_core_thread_instance = sandbox_core_thread_instance or {} - --- export the thread status -sandbox_core_thread.STATUS_READY = thread.STATUS_READY -sandbox_core_thread.STATUS_RUNNING = thread.STATUS_RUNNING -sandbox_core_thread.STATUS_SUSPENDED = thread.STATUS_SUSPENDED -sandbox_core_thread.STATUS_DEAD = thread.STATUS_DEAD - --- wrap thread -function _thread_wrap(instance) - - -- hook thread interfaces - local hooked = {} - for name, func in pairs(sandbox_core_thread_instance) do - if not name:startswith("_") and type(func) == "function" then - hooked["_" .. name] = instance["_" .. name] or instance[name] - hooked[name] = func - end - end - for name, func in pairs(hooked) do - instance[name] = func - end - return instance -end - --- start thread -function sandbox_core_thread_instance.start(instance) - local ok, errors = instance:_start() - if not ok then - raise(errors) - end - return instance -end - --- suspend thread -function sandbox_core_thread_instance.suspend(instance) - local ok, errors = instance:_suspend() - if not ok then - raise(errors) - end -end - --- resume thread -function sandbox_core_thread_instance.resume(instance) - local ok, errors = instance:_resume() - if not ok then - raise(errors) - end -end - --- wait thread -function sandbox_core_thread_instance.wait(instance, timeout) - local ok, errors = instance:_wait(timeout) - if ok < 0 then - raise(errors) - end -end - --- new thread -function sandbox_core_thread.new(callback, opt) - local instance, errors = thread.new(callback, opt) - if not instance then - raise(errors) - end - return _thread_wrap(instance) -end - --- start a thread -function sandbox_core_thread.start(callback, ...) - return sandbox_core_thread.start_withopt(callback, {argv = table.pack(...)}) -end - --- start a named thread -function sandbox_core_thread.start_named(name, callback, ...) - return sandbox_core_thread.start_withopt(callback, {name = name, argv = table.pack(...)}) -end - --- start a thread with options -function sandbox_core_thread.start_withopt(callback, opt) - return sandbox_core_thread.new(callback, opt):start() -end - --- get the running thread -function sandbox_core_thread.running() - local instance, errors = thread.running() - if not instance then - raise(errors) - end - return instance -end - --- return module -return sandbox_core_thread - diff --git a/xmake/core/thread/thread.lua b/xmake/core/thread/thread.lua deleted file mode 100644 index bdf1c0380..000000000 --- a/xmake/core/thread/thread.lua +++ /dev/null @@ -1,264 +0,0 @@ ---!A cross-platform build utility based on Lua --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. --- --- Copyright (C) 2015-present, Xmake Open Source Community. --- --- @author ruki --- @file thread.lua --- - --- define module -local thread = thread or {} -local _instance = _instance or {} - --- load modules -local io = require("base/io") -local libc = require("base/libc") -local bytes = require("base/bytes") -local table = require("base/table") -local string = require("base/string") -local sandbox = require("sandbox/sandbox") - --- the thread status -thread.STATUS_READY = 1 -thread.STATUS_RUNNING = 2 -thread.STATUS_SUSPENDED = 3 -thread.STATUS_DEAD = 4 - --- new a thread -function _instance.new(callback, opt) - opt = opt or {} - local instance = table.inherit(_instance) - instance._NAME = opt.name or "anonymous" - instance._ARGV = opt.argv - instance._CALLBACK = callback - instance._STACKSIZE = opt.stacksize or 0 - instance._STATUS = thread.STATUS_READY - setmetatable(instance, _instance) - return instance -end - --- get thread name -function _instance:name() - return self._NAME -end - --- get cdata of thread -function _instance:cdata() - return self._HANLDE -end - --- get thread status -function _instance:status() - return self._STATUS -end - --- is ready? -function _instance:is_ready() - return self:status() == thread.STATUS_READY -end - --- is running? -function _instance:is_running() - return self:status() == thread.STATUS_RUNNING -end - --- is suspended? -function _instance:is_suspended() - return self:status() == thread.STATUS_SUSPENDED -end - --- is dead? -function _instance:is_dead() - return self:status() == thread.STATUS_DEAD -end - --- start thread -function _instance:start() - if not self:is_ready() then - return nil, string.format("%s: cannot start non-ready thread!", self) - end - assert(not self:cdata()) - - -- serialize and pass callback and arguments to this thread - -- we do not use string.serialize to serialize callback, because it's slower (deserialize) - -- and we cannot strip function debug info, we need to reserve _ENV, and other upvalue names - local callback = string._dump(self._CALLBACK) - local callinfo = {name = self:name(), argv = self._ARGV} - callinfo = string.serialize(callinfo, {strip = true, indent = false}) - - -- init and start thread - local handle, errors = thread.thread_init(self:name(), callback, callinfo, self._STACKSIZE) - if not handle then - return nil, errors or string.format("%s: failed to create thread!", self) - end - - self._HANLDE = handle - self._STATUS = thread.STATUS_RUNNING - return true -end - --- suspend thread -function _instance:suspend() - if not self:is_running() then - return nil, string.format("%s: cannot suspend non-running thread!", self) - end - assert(self:cdata()) - - local ok, errors = thread.thread_suspend(self:cdata()) - if not ok then - return nil, errors or string.format("%s: failed to suspend thread!", self) - end - - self._STATUS = thread.STATUS_SUSPENDED - return true -end - --- resume thread -function _instance:resume() - if not self:is_suspended() then - return nil, string.format("%s: cannot suspend non-suspended thread!", self) - end - assert(self:cdata()) - - local ok, errors = thread.thread_resume(self:cdata()) - if not ok then - return nil, errors or string.format("%s: failed to resume thread!", self) - end - - self._STATUS = thread.STATUS_RUNNING - return true -end - --- wait thread -function _instance:wait(timeout) - if self:is_dead() then - return 1 - elseif self:is_ready() then - return -1, string.format("%s: cannot wait ready thread!", self) - end - assert(self:cdata()) - - local ok, errors = thread.thread_wait(self:cdata(), timeout) - if ok < 0 then - return -1, errors or string.format("%s: failed to resume thread!", self) - end - - if ok > 0 then - self._STATUS = thread.STATUS_DEAD - end - return ok -end - --- tostring(thread) -function _instance:__tostring() - local status_strs = self._STATUS_STRS - if not status_strs then - status_strs = { - [thread.STATUS_READY] = "ready", - [thread.STATUS_RUNNING] = "running", - [thread.STATUS_SUSPENDED] = "suspended", - [thread.STATUS_DEAD] = "dead" - } - self._STATUS_STRS = status_strs - end - return string.format("", self:name(), status_strs[self:status()]) -end - --- gc(thread) -function _instance:__gc() - if self:cdata() and self:is_dead() and io.thread_exit(self:cdata()) then - self._HANLDE = nil - end -end - --- new a thread --- --- @param callback the thread callback --- @param opt the thread options, e.g. {name = "", argv = {}, stacksize = 8192} --- --- @return the thread instance --- -function thread.new(callback, opt) - if callback == nil then - return nil, "invalid thread, callback is nil" - end - return _instance.new(callback, opt) -end - --- get the running thread name -function thread.running() - return thread._RUNNING -end - --- run thread -function thread._run_thread(callback_str, callinfo_str) - - -- load callback info - local callinfo - local argv - local threadname - if callinfo_str then - local result, errors = string.deserialize(callinfo_str) - if not result then - return false, string.format("invalid thread callinfo, %s!", errors or "unknown") - end - callinfo = result - if callinfo then - argv = callinfo.argv - threadname = callinfo.name - end - end - - -- load callback - local callback - local fenvs = {} - if callback_str then - local script, errors = load(callback_str, "=(thread)", "b", fenvs) - if not script then - return false, string.format("cannot load thread(%s) callback, %s!", threadname or "unknown", errors or "unknown") - end - for i = 1, math.huge do - local upname, upvalue = debug.getupvalue(script, i) - if upname == nil or upname == "" then - break - end - if upvalue == nil then - return false, string.format("we cannot access upvalue(%s) in thread(%s) callback!", upname, threadname or "unknown") - end - end - callback = script - end - if not callback then - return false, "no thread callback" - end - - -- bind sandbox --- local sandbox_inst, errors = sandbox.new(callback, { --- filter = interp:filter(), rootdir = interp:rootdir(), namespace = interp:namespace()}) - local sandbox_inst, errors = sandbox.new(callback) - if not sandbox_inst then - return false, errors - end - - -- save the running thread name - thread._RUNNING = threadname - - -- do callback - return sandbox.load(sandbox_inst:script(), table.unpack(argv or {})) -end - --- return module -return thread - -- cgit v1.3.1