summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2025-08-28 22:42:25 +0800
committerruki <[email protected]>2025-08-28 11:35:54 +0800
commita3ff692d028873e06cc9827e1ff72dc9ec8b54d4 (patch)
tree75815a4402733834ebbd728cb05c7f134d2da51b
parent09854e59988b3c1269c97dc95ed4f6132bb851f8 (diff)
add thread event
-rw-r--r--core/src/xmake/engine.c12
-rw-r--r--core/src/xmake/thread/event_exit.c55
-rw-r--r--core/src/xmake/thread/event_incref.c46
-rw-r--r--core/src/xmake/thread/event_init.c70
-rw-r--r--core/src/xmake/thread/event_post.c46
-rw-r--r--core/src/xmake/thread/event_wait.c47
-rw-r--r--core/src/xmake/thread/prefix.h17
-rw-r--r--tests/modules/thread/event.lua25
-rw-r--r--xmake/core/base/thread.lua129
-rw-r--r--xmake/core/sandbox/modules/import/core/base/thread.lua46
10 files changed, 475 insertions, 18 deletions
diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c
index 8dd4f5656..24b95d1fd 100644
--- a/core/src/xmake/engine.c
+++ b/core/src/xmake/engine.c
@@ -354,6 +354,13 @@ tb_int_t xm_thread_mutex_trylock(lua_State* lua);
tb_int_t xm_thread_mutex_unlock(lua_State* lua);
tb_int_t xm_thread_mutex_incref(lua_State* lua);
+// the thread/event functions
+tb_int_t xm_thread_event_init(lua_State* lua);
+tb_int_t xm_thread_event_exit(lua_State* lua);
+tb_int_t xm_thread_event_post(lua_State* lua);
+tb_int_t xm_thread_event_wait(lua_State* lua);
+tb_int_t xm_thread_event_incref(lua_State* lua);
+
// open cjson
__tb_extern_c_enter__
tb_int_t luaopen_cjson(lua_State *l);
@@ -654,6 +661,11 @@ static luaL_Reg const g_thread_functions[] =
, { "mutex_trylock", xm_thread_mutex_trylock }
, { "mutex_unlock", xm_thread_mutex_unlock }
, { "mutex_incref", xm_thread_mutex_incref }
+, { "event_init", xm_thread_event_init }
+, { "event_exit", xm_thread_event_exit }
+, { "event_post", xm_thread_event_post }
+, { "event_wait", xm_thread_event_wait }
+, { "event_incref", xm_thread_event_incref }
, { tb_null, tb_null }
};
diff --git a/core/src/xmake/thread/event_exit.c b/core/src/xmake/thread/event_exit.c
new file mode 100644
index 000000000..54e977068
--- /dev/null
+++ b/core/src/xmake/thread/event_exit.c
@@ -0,0 +1,55 @@
+/*!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_event_exit.c
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "thread_event"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+tb_int_t xm_thread_event_exit(lua_State* lua)
+{
+ tb_assert_and_check_return_val(lua, 0);
+
+ xm_thread_event_t* thread_event = xm_thread_event_get(lua, 1);
+ tb_assert_and_check_return_val(thread_event && thread_event->handle, 0);
+
+ if (tb_atomic_fetch_and_sub(&thread_event->refn, 1) == 1)
+ {
+ if (thread_event->handle)
+ {
+ tb_event_exit(thread_event->handle);
+ thread_event->handle = tb_null;
+ }
+ tb_free(thread_event);
+ }
+ lua_pushboolean(lua, tb_true);
+ return 1;
+}
+
diff --git a/core/src/xmake/thread/event_incref.c b/core/src/xmake/thread/event_incref.c
new file mode 100644
index 000000000..ff8b658d9
--- /dev/null
+++ b/core/src/xmake/thread/event_incref.c
@@ -0,0 +1,46 @@
+/*!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_event_incref.c
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "thread_event"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+tb_int_t xm_thread_event_incref(lua_State* lua)
+{
+ tb_assert_and_check_return_val(lua, 0);
+
+ xm_thread_event_t* thread_event = xm_thread_event_get(lua, 1);
+ tb_assert_and_check_return_val(thread_event && thread_event->handle, 0);
+
+ lua_pushboolean(lua, tb_atomic_fetch_and_add(&thread_event->refn, 1) >= 1);
+ return 1;
+}
+
diff --git a/core/src/xmake/thread/event_init.c b/core/src/xmake/thread/event_init.c
new file mode 100644
index 000000000..942537718
--- /dev/null
+++ b/core/src/xmake/thread/event_init.c
@@ -0,0 +1,70 @@
+/*!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 event_init.c
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "thread_event"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+tb_int_t xm_thread_event_init(lua_State* lua)
+{
+ tb_assert_and_check_return_val(lua, 0);
+
+ tb_bool_t ok = tb_false;
+ xm_thread_event_t* thread_event = tb_null;
+ do
+ {
+ thread_event = tb_malloc0_type(xm_thread_event_t);
+ tb_assert_and_check_break(thread_event);
+
+ thread_event->refn = 1;
+ thread_event->handle = tb_event_init();
+ tb_assert_and_check_break(thread_event->handle);
+
+ xm_lua_pushpointer(lua, (tb_pointer_t)thread_event);
+ ok = tb_true;
+
+ } while (0);
+
+ if (!ok)
+ {
+ if (thread_event)
+ {
+ if (thread_event->handle)
+ {
+ tb_event_exit(thread_event->handle);
+ thread_event->handle = tb_null;
+ }
+ tb_free(thread_event);
+ }
+ lua_pushnil(lua);
+ }
+ return 1;
+}
diff --git a/core/src/xmake/thread/event_post.c b/core/src/xmake/thread/event_post.c
new file mode 100644
index 000000000..0b8e5dabd
--- /dev/null
+++ b/core/src/xmake/thread/event_post.c
@@ -0,0 +1,46 @@
+/*!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_event_lock.c
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "thread_event"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+tb_int_t xm_thread_event_post(lua_State* lua)
+{
+ tb_assert_and_check_return_val(lua, 0);
+
+ xm_thread_event_t* thread_event = xm_thread_event_get(lua, 1);
+ tb_assert_and_check_return_val(thread_event && thread_event->handle, 0);
+
+ lua_pushboolean(lua, tb_event_post(thread_event->handle));
+ return 1;
+}
+
diff --git a/core/src/xmake/thread/event_wait.c b/core/src/xmake/thread/event_wait.c
new file mode 100644
index 000000000..0831036c6
--- /dev/null
+++ b/core/src/xmake/thread/event_wait.c
@@ -0,0 +1,47 @@
+/*!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_event_unlock.c
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "thread_event"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+tb_int_t xm_thread_event_wait(lua_State* lua)
+{
+ tb_assert_and_check_return_val(lua, 0);
+
+ xm_thread_event_t* thread_event = xm_thread_event_get(lua, 1);
+ tb_assert_and_check_return_val(thread_event && thread_event->handle, 0);
+
+ tb_long_t timeout = (tb_long_t)luaL_checknumber(lua, 2);
+ lua_pushinteger(lua, tb_event_wait(thread_event->handle, timeout));
+ return 1;
+}
+
diff --git a/core/src/xmake/thread/prefix.h b/core/src/xmake/thread/prefix.h
index 62e1c7729..6c25b234e 100644
--- a/core/src/xmake/thread/prefix.h
+++ b/core/src/xmake/thread/prefix.h
@@ -39,6 +39,14 @@ typedef struct __xm_thread_t
}xm_thread_t;
+// the thread event type
+typedef struct __xm_thread_event_t
+{
+ tb_event_ref_t handle;
+ tb_atomic_t refn;
+
+}xm_thread_event_t;
+
// the thread mutex type
typedef struct __xm_thread_mutex_t
{
@@ -47,6 +55,15 @@ typedef struct __xm_thread_mutex_t
}xm_thread_mutex_t;
+// get the thread event from arguments
+static __tb_inline__ xm_thread_event_t* xm_thread_event_get(lua_State* lua, tb_int_t index)
+{
+ xm_thread_event_t* thread_event = tb_null;
+ if (xm_lua_isinteger(lua, index)) thread_event = (xm_thread_event_t*)(tb_size_t)(tb_long_t)lua_tointeger(lua, index);
+ else if (xm_lua_ispointer(lua, index)) thread_event = (xm_thread_event_t*)xm_lua_topointer(lua, index);
+ return thread_event;
+}
+
// get the thread mutex from arguments
static __tb_inline__ xm_thread_mutex_t* xm_thread_mutex_get(lua_State* lua, tb_int_t index)
{
diff --git a/tests/modules/thread/event.lua b/tests/modules/thread/event.lua
new file mode 100644
index 000000000..fde1511ee
--- /dev/null
+++ b/tests/modules/thread/event.lua
@@ -0,0 +1,25 @@
+import("core.base.thread")
+
+function callback(event)
+ import("core.base.thread")
+ print("%s: starting ..", thread.running())
+ while true do
+ print("%s: waiting ..", thread.running())
+ if event:wait(-1) > 0 then
+ print("%s: triggered", thread.running())
+ end
+ end
+end
+
+function main()
+ local event = thread.event()
+ local t = thread.start_named("keyboard", callback, event)
+ while true do
+ local ch = io.read()
+ if ch then
+ event:post()
+ end
+ end
+ t:wait(-1)
+end
+
diff --git a/xmake/core/base/thread.lua b/xmake/core/base/thread.lua
index 24686ba49..5428dc3f2 100644
--- a/xmake/core/base/thread.lua
+++ b/xmake/core/base/thread.lua
@@ -22,6 +22,7 @@
local thread = thread or {}
local _thread = _thread or {}
local _mutex = _mutex or {}
+local _event = _event or {}
-- load modules
local io = require("base/io")
@@ -96,9 +97,13 @@ function _thread:start()
local argv = {}
for _, arg in ipairs(self._ARGV) do
-- is mutex? we can only pass cdata address
- if type(arg) == "table" and arg._LOCK and arg.cdata then
+ if type(arg) == "table" and arg._MUTEX and arg.cdata then
thread.mutex_incref(arg:cdata())
arg = {mutex = true, name = arg:name(), caddr = libc.dataptr(arg:cdata())}
+ -- is event? we can only pass cdata address
+ elseif type(arg) == "table" and arg._EVENT and arg.cdata then
+ thread.event_incref(arg:cdata())
+ arg = {event = true, name = arg:name(), caddr = libc.dataptr(arg:cdata())}
end
table.insert(argv, arg)
end
@@ -196,10 +201,10 @@ function _thread:__gc()
end
-- new an mutex
-function _mutex.new(name, lock)
+function _mutex.new(name, cdata)
local mutex = table.inherit(_mutex)
mutex._NAME = name
- mutex._LOCK = lock
+ mutex._MUTEX = cdata
mutex._LOCKED_NUM = 0
setmetatable(mutex, _mutex)
return mutex
@@ -212,7 +217,7 @@ end
-- get the cdata
function _mutex:cdata()
- return self._LOCK
+ return self._MUTEX
end
-- is locked?
@@ -220,19 +225,16 @@ function _mutex:islocked()
return self._LOCKED_NUM > 0
end
--- lock file
+-- lock mutex
--
-- @return ok, errors
--
function _mutex:lock()
-
- -- 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()) then
self._LOCKED_NUM = self._LOCKED_NUM + 1
return true
@@ -241,21 +243,18 @@ function _mutex:lock()
end
end
--- try to lock file
+-- try to lock mutex
--
-- @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
@@ -264,16 +263,13 @@ function _mutex:trylock(opt)
end
end
--- unlock file
+-- unlock mutex
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
@@ -298,7 +294,7 @@ function _mutex:close()
-- close it
ok = thread.mutex_exit(self:cdata())
if ok then
- self._LOCK = nil
+ self._MUTEX = nil
self._LOCKED_NUM = 0
end
return ok
@@ -320,7 +316,92 @@ end
-- gc(mutex)
function _mutex:__gc()
if self:cdata() and thread.mutex_exit(self:cdata()) then
- self._LOCK = nil
+ self._MUTEX = nil
+ self._LOCKED_NUM = 0
+ end
+end
+
+-- new an event
+function _event.new(name, cdata)
+ local event = table.inherit(_event)
+ event._NAME = name
+ event._EVENT = cdata
+ setmetatable(event, _event)
+ return event
+end
+
+-- get the event name
+function _event:name()
+ return self._NAME
+end
+
+-- get the cdata
+function _event:cdata()
+ return self._EVENT
+end
+
+-- post event
+--
+-- @return ok, errors
+--
+function _event:post()
+ local ok, errors = self:_ensure_opened()
+ if not ok then
+ return false, errors
+ end
+
+ if not thread.event_post(self:cdata()) then
+ return false, string.format("%s: post failed!", self)
+ end
+ return true
+end
+
+-- wait event
+function _event:wait(timeout)
+ local ok, errors = self:_ensure_opened()
+ if not ok then
+ return false, errors
+ end
+
+ local ok, errors = thread.event_wait(self:cdata(), timeout)
+ if ok < 0 then
+ return false, string.format("%s: wait failed, errors: %s!", self, errors or "unknown")
+ end
+ return ok
+end
+
+-- close event
+function _event:close()
+ local ok, errors = self:_ensure_opened()
+ if not ok then
+ return false, errors
+ end
+
+ ok = thread.event_exit(self:cdata())
+ if ok then
+ self._MUTEX = nil
+ self._LOCKED_NUM = 0
+ end
+ return ok
+end
+
+-- ensure the file is opened
+function _event:_ensure_opened()
+ if not self:cdata() then
+ return false, string.format("%s: has been closed!", self)
+ end
+ return true
+end
+
+-- tostring(event)
+function _event:__tostring()
+ return "<event: " .. (self:name() or tostring(self:cdata())) .. ">"
+end
+
+-- gc(event)
+function _event:__gc()
+ if self:cdata() and thread.event_exit(self:cdata()) then
+ self._MUTEX = nil
self._LOCKED_NUM = 0
end
end
@@ -403,6 +484,8 @@ function thread._run_thread(callback_str, callinfo_str)
for _, arg in ipairs(argv) do
if type(arg) == "table" and arg.mutex and arg.caddr then
arg = _mutex.new(arg.name, libc.ptraddr(arg.caddr))
+ elseif type(arg) == "table" and arg.event and arg.caddr then
+ arg = _event.new(arg.name, libc.ptraddr(arg.caddr))
end
table.insert(newargv, arg)
end
@@ -423,6 +506,16 @@ function thread.mutex(name)
end
end
+-- open a event
+function thread.event(name)
+ local event = thread.event_init()
+ if event then
+ return _event.new(name, event)
+ else
+ return nil, string.format("cannot open event: %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 3c4bfa6d5..d3b5d4ae4 100644
--- a/xmake/core/sandbox/modules/import/core/base/thread.lua
+++ b/xmake/core/sandbox/modules/import/core/base/thread.lua
@@ -28,6 +28,7 @@ local raise = require("sandbox/modules/raise")
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 {}
+local sandbox_core_base_thread_event = sandbox_core_base_thread_event or {}
-- export the thread status
sandbox_core_base_thread.STATUS_READY = thread.STATUS_READY
@@ -109,6 +110,31 @@ function sandbox_core_base_thread_mutex.close(mutex)
end
end
+-- post event
+function sandbox_core_base_thread_event.post(event)
+ local ok, errors = event:_post()
+ if not ok then
+ raise(errors)
+ end
+end
+
+-- wait event
+function sandbox_core_base_thread_event.wait(event, timeout)
+ local ok, errors = event:_wait(timeout)
+ if ok < 0 then
+ raise(errors)
+ end
+ return ok
+end
+
+-- close event
+function sandbox_core_base_thread_event.close(event)
+ local ok, errors = event:_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)
@@ -163,6 +189,26 @@ function sandbox_core_base_thread.mutex(name)
return mutex
end
+-- open a event
+function sandbox_core_base_thread.event(name)
+ local event, errors = thread.event(name)
+ if not event then
+ raise(errors)
+ end
+
+ -- hook fileevent interfaces
+ local hooked = {}
+ for name, func in pairs(sandbox_core_base_thread_event) do
+ if not name:startswith("_") and type(func) == "function" then
+ hooked["_" .. name] = event["_" .. name] or event[name]
+ hooked[name] = func
+ end
+ end
+ for name, func in pairs(hooked) do
+ event[name] = func
+ end
+ return event
+end
-- return module
return sandbox_core_base_thread