summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2025-08-27 23:27:52 +0800
committerruki <[email protected]>2025-08-28 11:35:54 +0800
commit46dd9ecf582e7d30d5ce7eb18f248506f85bbe21 (patch)
treecc8481903ed576f0d2b38db7f89f55db25706050
parent6809e2d887d3f3f298b4e324a7580c55ac47562d (diff)
add mutex to thread
-rw-r--r--core/src/xmake/engine.c22
-rw-r--r--core/src/xmake/thread/mutex_trylock.c49
-rw-r--r--xmake/core/base/thread.lua183
-rw-r--r--xmake/core/sandbox/modules/import/core/base/thread.lua47
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