diff options
| author | ruki <[email protected]> | 2025-08-27 23:27:52 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2025-08-28 11:35:54 +0800 |
| commit | 46dd9ecf582e7d30d5ce7eb18f248506f85bbe21 (patch) | |
| tree | cc8481903ed576f0d2b38db7f89f55db25706050 | |
| parent | 6809e2d887d3f3f298b4e324a7580c55ac47562d (diff) | |
add mutex to thread
| -rw-r--r-- | core/src/xmake/engine.c | 22 | ||||
| -rw-r--r-- | core/src/xmake/thread/mutex_trylock.c | 49 | ||||
| -rw-r--r-- | xmake/core/base/thread.lua | 183 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/core/base/thread.lua | 47 |
4 files changed, 271 insertions, 30 deletions
diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c index 2540dff07..1c5bc1a79 100644 --- a/core/src/xmake/engine.c +++ b/core/src/xmake/engine.c @@ -350,6 +350,7 @@ tb_int_t xm_thread_resume(lua_State* lua); tb_int_t xm_thread_mutex_init(lua_State* lua); tb_int_t xm_thread_mutex_exit(lua_State* lua); tb_int_t xm_thread_mutex_lock(lua_State* lua); +tb_int_t xm_thread_mutex_trylock(lua_State* lua); tb_int_t xm_thread_mutex_unlock(lua_State* lua); // open cjson @@ -641,16 +642,17 @@ static luaL_Reg const g_utils_functions[] = // the thread functions static luaL_Reg const g_thread_functions[] = { - { "thread_init", xm_thread_init } -, { "thread_exit", xm_thread_exit } -, { "thread_wait", xm_thread_wait } -, { "thread_resume", xm_thread_resume } -, { "thread_suspend", xm_thread_suspend } -, { "mutex_init", xm_thread_mutex_init } -, { "mutex_exit", xm_thread_mutex_exit } -, { "mutex_lock", xm_thread_mutex_lock } -, { "mutex_unlock", xm_thread_mutex_unlock } -, { tb_null, tb_null } + { "thread_init", xm_thread_init } +, { "thread_exit", xm_thread_exit } +, { "thread_wait", xm_thread_wait } +, { "thread_resume", xm_thread_resume } +, { "thread_suspend", xm_thread_suspend } +, { "mutex_init", xm_thread_mutex_init } +, { "mutex_exit", xm_thread_mutex_exit } +, { "mutex_lock", xm_thread_mutex_lock } +, { "mutex_trylock", xm_thread_mutex_trylock } +, { "mutex_unlock", xm_thread_mutex_unlock } +, { tb_null, tb_null } }; // the lua global instance for signal handler diff --git a/core/src/xmake/thread/mutex_trylock.c b/core/src/xmake/thread/mutex_trylock.c new file mode 100644 index 000000000..3c884b40c --- /dev/null +++ b/core/src/xmake/thread/mutex_trylock.c @@ -0,0 +1,49 @@ +/*!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_mutex_trylock.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "thread_mutex" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ +tb_int_t xm_thread_mutex_trylock(lua_State* lua) +{ + tb_assert_and_check_return_val(lua, 0); + + if (!xm_lua_ispointer(lua, 1)) + return 0; + + xm_thread_mutex_t* thread_mutex = (xm_thread_mutex_t*)xm_lua_topointer(lua, 1); + tb_check_return_val(thread_mutex && thread_mutex->handle, 0); + + lua_pushboolean(lua, tb_mutex_enter_try(thread_mutex->handle)); + return 1; +} + diff --git a/xmake/core/base/thread.lua b/xmake/core/base/thread.lua index bdf1c0380..413bdb422 100644 --- a/xmake/core/base/thread.lua +++ b/xmake/core/base/thread.lua @@ -19,8 +19,9 @@ -- -- define module -local thread = thread or {} -local _instance = _instance or {} +local thread = thread or {} +local _thread = _thread or {} +local _mutex = _mutex or {} -- load modules local io = require("base/io") @@ -37,55 +38,55 @@ thread.STATUS_SUSPENDED = 3 thread.STATUS_DEAD = 4 -- new a thread -function _instance.new(callback, opt) +function _thread.new(callback, opt) opt = opt or {} - local instance = table.inherit(_instance) + local instance = table.inherit(_thread) 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) + setmetatable(instance, _thread) return instance end -- get thread name -function _instance:name() +function _thread:name() return self._NAME end -- get cdata of thread -function _instance:cdata() +function _thread:cdata() return self._HANLDE end -- get thread status -function _instance:status() +function _thread:status() return self._STATUS end -- is ready? -function _instance:is_ready() +function _thread:is_ready() return self:status() == thread.STATUS_READY end -- is running? -function _instance:is_running() +function _thread:is_running() return self:status() == thread.STATUS_RUNNING end -- is suspended? -function _instance:is_suspended() +function _thread:is_suspended() return self:status() == thread.STATUS_SUSPENDED end -- is dead? -function _instance:is_dead() +function _thread:is_dead() return self:status() == thread.STATUS_DEAD end -- start thread -function _instance:start() +function _thread:start() if not self:is_ready() then return nil, string.format("%s: cannot start non-ready thread!", self) end @@ -110,7 +111,7 @@ function _instance:start() end -- suspend thread -function _instance:suspend() +function _thread:suspend() if not self:is_running() then return nil, string.format("%s: cannot suspend non-running thread!", self) end @@ -126,7 +127,7 @@ function _instance:suspend() end -- resume thread -function _instance:resume() +function _thread:resume() if not self:is_suspended() then return nil, string.format("%s: cannot suspend non-suspended thread!", self) end @@ -142,7 +143,7 @@ function _instance:resume() end -- wait thread -function _instance:wait(timeout) +function _thread:wait(timeout) if self:is_dead() then return 1 elseif self:is_ready() then @@ -162,7 +163,7 @@ function _instance:wait(timeout) end -- tostring(thread) -function _instance:__tostring() +function _thread:__tostring() local status_strs = self._STATUS_STRS if not status_strs then status_strs = { @@ -177,12 +178,144 @@ function _instance:__tostring() end -- gc(thread) -function _instance:__gc() - if self:cdata() and self:is_dead() and io.thread_exit(self:cdata()) then +function _thread:__gc() + if self:cdata() and self:is_dead() and thread.thread_exit(self:cdata()) then self._HANLDE = nil end end +-- new an mutex +function _mutex.new(name, lock) + local mutex = table.inherit(_mutex) + mutex._NAME = name + mutex._LOCK = lock + mutex._LOCKED_NUM = 0 + setmetatable(mutex, _mutex) + return mutex +end + +-- get the mutex name +function _mutex:name() + return self._NAME +end + +-- get the cdata +function _mutex:cdata() + return self._LOCK +end + +-- is locked? +function _mutex:islocked() + return self._LOCKED_NUM > 0 +end + +-- lock file +-- +-- @param opt the argument option, {shared = true} +-- +-- @return ok, errors +-- +function _mutex:lock(opt) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return false, errors + end + + -- lock it + if self._LOCKED_NUM > 0 or thread.mutex_lock(self:cdata(), opt) then + self._LOCKED_NUM = self._LOCKED_NUM + 1 + return true + else + return false, string.format("%s: lock failed!", self) + end +end + +-- try to lock file +-- +-- @param opt the argument option, {shared = true} +-- +-- @return ok, errors +-- +function _mutex:trylock(opt) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return false, errors + end + + -- try lock it + if self._LOCKED_NUM > 0 or thread.mutex_trylock(self:cdata(), opt) then + self._LOCKED_NUM = self._LOCKED_NUM + 1 + return true + else + return false, string.format("%s: trylock failed!", self) + end +end + +-- unlock file +function _mutex:unlock(opt) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return false, errors + end + + -- unlock it + if self._LOCKED_NUM > 1 or (self._LOCKED_NUM > 0 and thread.mutex_unlock(self:cdata())) then + if self._LOCKED_NUM > 0 then + self._LOCKED_NUM = self._LOCKED_NUM - 1 + else + self._LOCKED_NUM = 0 + end + return true + else + return false, string.format("%s: unlock failed!", self) + end +end + +-- close mutex +function _mutex:close() + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return false, errors + end + + -- close it + ok = thread.mutex_exit(self:cdata()) + if ok then + self._LOCK = nil + self._LOCKED_NUM = 0 + end + return ok +end + +-- ensure the file is opened +function _mutex:_ensure_opened() + if not self:cdata() then + return false, string.format("%s: has been closed!", self) + end + return true +end + +-- tostring(mutex) +function _mutex:__tostring() + return "<mutex: " .. (self:name() or tostring(self:cdata())) .. ">" +end + +-- gc(mutex) +function _mutex:__gc() + if self:cdata() and thread.mutex_exit(self:cdata()) then + self._LOCK = nil + self._LOCKED_NUM = 0 + end +end + -- new a thread -- -- @param callback the thread callback @@ -194,7 +327,7 @@ function thread.new(callback, opt) if callback == nil then return nil, "invalid thread, callback is nil" end - return _instance.new(callback, opt) + return _thread.new(callback, opt) end -- get the running thread name @@ -259,6 +392,16 @@ function thread._run_thread(callback_str, callinfo_str) return sandbox.load(sandbox_inst:script(), table.unpack(argv or {})) end +-- open a mutex +function thread.mutex(name) + local mutex = thread.mutex_init() + if mutex then + return _mutex.new(name, mutex) + else + return nil, string.format("cannot open mutex: %s", os.strerror()) + end +end + -- return module return thread diff --git a/xmake/core/sandbox/modules/import/core/base/thread.lua b/xmake/core/sandbox/modules/import/core/base/thread.lua index f56466ddf..3c4bfa6d5 100644 --- a/xmake/core/sandbox/modules/import/core/base/thread.lua +++ b/xmake/core/sandbox/modules/import/core/base/thread.lua @@ -27,6 +27,7 @@ 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 {} +local sandbox_core_base_thread_mutex = sandbox_core_base_thread_mutex or {} -- export the thread status sandbox_core_base_thread.STATUS_READY = thread.STATUS_READY @@ -84,6 +85,30 @@ function sandbox_core_base_thread_instance.wait(instance, timeout) end end +-- lock mutex +function sandbox_core_base_thread_mutex.lock(mutex) + local ok, errors = mutex:_lock() + if not ok then + raise(errors) + end +end + +-- unlock mutex +function sandbox_core_base_thread_mutex.unlock(mutex) + local ok, errors = mutex:_unlock() + if not ok then + raise(errors) + end +end + +-- close mutex +function sandbox_core_base_thread_mutex.close(mutex) + local ok, errors = mutex:_close() + if not ok then + raise(errors) + end +end + -- new thread function sandbox_core_base_thread.new(callback, opt) local instance, errors = thread.new(callback, opt) @@ -117,6 +142,28 @@ function sandbox_core_base_thread.running() return instance end +-- open a mutex +function sandbox_core_base_thread.mutex(name) + local mutex, errors = thread.mutex(name) + if not mutex then + raise(errors) + end + + -- hook filemutex interfaces + local hooked = {} + for name, func in pairs(sandbox_core_base_thread_mutex) do + if not name:startswith("_") and type(func) == "function" then + hooked["_" .. name] = mutex["_" .. name] or mutex[name] + hooked[name] = func + end + end + for name, func in pairs(hooked) do + mutex[name] = func + end + return mutex +end + + -- return module return sandbox_core_base_thread |
