diff options
| author | ruki <[email protected]> | 2025-08-28 22:42:25 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2025-08-28 11:35:54 +0800 |
| commit | a3ff692d028873e06cc9827e1ff72dc9ec8b54d4 (patch) | |
| tree | 75815a4402733834ebbd728cb05c7f134d2da51b | |
| parent | 09854e59988b3c1269c97dc95ed4f6132bb851f8 (diff) | |
add thread event
| -rw-r--r-- | core/src/xmake/engine.c | 12 | ||||
| -rw-r--r-- | core/src/xmake/thread/event_exit.c | 55 | ||||
| -rw-r--r-- | core/src/xmake/thread/event_incref.c | 46 | ||||
| -rw-r--r-- | core/src/xmake/thread/event_init.c | 70 | ||||
| -rw-r--r-- | core/src/xmake/thread/event_post.c | 46 | ||||
| -rw-r--r-- | core/src/xmake/thread/event_wait.c | 47 | ||||
| -rw-r--r-- | core/src/xmake/thread/prefix.h | 17 | ||||
| -rw-r--r-- | tests/modules/thread/event.lua | 25 | ||||
| -rw-r--r-- | xmake/core/base/thread.lua | 129 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/core/base/thread.lua | 46 |
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 |
