diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-03 15:53:34 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-03 15:53:34 +0200 |
commit | 44c617f7b756052c7cd059c96f89b85f0f5ec96c (patch) | |
tree | a9ac504d7ffa09500c9ea17bab963f1016f6fe79 | |
parent | 420e50697cd036a0d8ea1601961bd6974703ade1 (diff) | |
download | lanes-44c617f7b756052c7cd059c96f89b85f0f5ec96c.tar.gz lanes-44c617f7b756052c7cd059c96f89b85f0f5ec96c.tar.bz2 lanes-44c617f7b756052c7cd059c96f89b85f0f5ec96c.zip |
Moved lanes.sleep implementation to the C-side
-rw-r--r-- | docs/index.html | 1 | ||||
-rw-r--r-- | src/cancel.h | 1 | ||||
-rw-r--r-- | src/deep.cpp | 18 | ||||
-rw-r--r-- | src/deep.h | 2 | ||||
-rw-r--r-- | src/lanes.cpp | 38 | ||||
-rw-r--r-- | src/lanes.lua | 26 | ||||
-rw-r--r-- | src/uniquekey.h | 6 | ||||
-rw-r--r-- | tests/cancel.lua | 11 |
8 files changed, 67 insertions, 36 deletions
diff --git a/docs/index.html b/docs/index.html index 98c2abb..c3e8285 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -1767,6 +1767,7 @@ static MyDeepFactory g_MyDeepFactory; | |||
1767 | <li>Include <tt>"deep.h"</tt> and either link against Lanes or statically compile <tt>compat.cpp deep.cpp</tt> into your module if you want to avoid a runtime dependency for users that will use your module without Lanes. | 1767 | <li>Include <tt>"deep.h"</tt> and either link against Lanes or statically compile <tt>compat.cpp deep.cpp</tt> into your module if you want to avoid a runtime dependency for users that will use your module without Lanes. |
1768 | <li>Instanciate your userdata using <tt>yourFactoryObject.pushDeepUserdata()</tt>, instead of the regular <tt>lua_newuserdata()</tt>. Given a <tt>factory</tt>, it sets up the support structures and returns a state-specific proxy userdata for accessing your data. This proxy can also be copied over to other lanes.</li> | 1768 | <li>Instanciate your userdata using <tt>yourFactoryObject.pushDeepUserdata()</tt>, instead of the regular <tt>lua_newuserdata()</tt>. Given a <tt>factory</tt>, it sets up the support structures and returns a state-specific proxy userdata for accessing your data. This proxy can also be copied over to other lanes.</li> |
1769 | <li>Accessing the deep userdata from your C code, use <tt>yourFactoryObject.toDeep()</tt> instead of the regular <tt>lua_touserdata()</tt>.</li> | 1769 | <li>Accessing the deep userdata from your C code, use <tt>yourFactoryObject.toDeep()</tt> instead of the regular <tt>lua_touserdata()</tt>.</li> |
1770 | <li>To push an existing proxy on the stack, use <tt>DeepPrelude::push(L)</tt>.</li> | ||
1770 | </ol> | 1771 | </ol> |
1771 | 1772 | ||
1772 | <p> | 1773 | <p> |
diff --git a/src/cancel.h b/src/cancel.h index bac8b05..d60903c 100644 --- a/src/cancel.h +++ b/src/cancel.h | |||
@@ -26,6 +26,7 @@ class Lane; // forward | |||
26 | enum class CancelRequest | 26 | enum class CancelRequest |
27 | { | 27 | { |
28 | None, // no pending cancel request | 28 | None, // no pending cancel request |
29 | // TODO: add a Wake mode: user wants to wake the waiting lindas (in effect resulting in a timeout before the initial operation duration) | ||
29 | Soft, // user wants the lane to cancel itself manually on cancel_test() | 30 | Soft, // user wants the lane to cancel itself manually on cancel_test() |
30 | Hard // user wants the lane to be interrupted (meaning code won't return from those functions) from inside linda:send/receive calls | 31 | Hard // user wants the lane to be interrupted (meaning code won't return from those functions) from inside linda:send/receive calls |
31 | }; | 32 | }; |
diff --git a/src/deep.cpp b/src/deep.cpp index 34cc4b4..76397ae 100644 --- a/src/deep.cpp +++ b/src/deep.cpp | |||
@@ -191,7 +191,7 @@ void DeepFactory::PushDeepProxy(DestState const L_, DeepPrelude* const prelude_, | |||
191 | STACK_GROW(L_, 7); | 191 | STACK_GROW(L_, 7); |
192 | 192 | ||
193 | // a new full userdata, fitted with the specified number of uservalue slots (always 1 for Lua < 5.4) | 193 | // a new full userdata, fitted with the specified number of uservalue slots (always 1 for Lua < 5.4) |
194 | DeepPrelude** const _proxy{ lua_newuserdatauv<DeepPrelude*>(L_, nuv_) }; // L_: DPC proxy | 194 | DeepPrelude** const _proxy{ lua_newuserdatauv<DeepPrelude*>(L_, nuv_) }; // L_: DPC proxy |
195 | LUA_ASSERT(L_, _proxy); | 195 | LUA_ASSERT(L_, _proxy); |
196 | *_proxy = prelude_; | 196 | *_proxy = prelude_; |
197 | prelude_->refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data | 197 | prelude_->refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data |
@@ -268,10 +268,10 @@ void DeepFactory::PushDeepProxy(DestState const L_, DeepPrelude* const prelude_, | |||
268 | } | 268 | } |
269 | } | 269 | } |
270 | } | 270 | } |
271 | STACK_CHECK(L_, 3); // DPC proxy metatable | 271 | STACK_CHECK(L_, 3); // L_: DPC proxy metatable |
272 | LUA_ASSERT(L_, lua_type_as_enum(L_, -2) == LuaType::USERDATA); | 272 | LUA_ASSERT(L_, lua_type_as_enum(L_, -2) == LuaType::USERDATA); |
273 | LUA_ASSERT(L_, lua_istable(L_, -1)); | 273 | LUA_ASSERT(L_, lua_istable(L_, -1)); |
274 | lua_setmetatable(L_, -2); // DPC proxy | 274 | lua_setmetatable(L_, -2); // L_: DPC proxy |
275 | 275 | ||
276 | // If we're here, we obviously had to create a new proxy, so cache it. | 276 | // If we're here, we obviously had to create a new proxy, so cache it. |
277 | lua_pushlightuserdata(L_, prelude_); // L_: DPC proxy deep | 277 | lua_pushlightuserdata(L_, prelude_); // L_: DPC proxy deep |
@@ -375,3 +375,15 @@ DeepPrelude* DeepFactory::toDeep(lua_State* const L_, int const index_) const | |||
375 | DeepPrelude** const _proxy{ lua_tofulluserdata<DeepPrelude*>(L_, index_) }; | 375 | DeepPrelude** const _proxy{ lua_tofulluserdata<DeepPrelude*>(L_, index_) }; |
376 | return *_proxy; | 376 | return *_proxy; |
377 | } | 377 | } |
378 | |||
379 | // ################################################################################################# | ||
380 | |||
381 | void DeepPrelude::push(lua_State* L_) const | ||
382 | { | ||
383 | STACK_CHECK_START_REL(L_, 0); | ||
384 | kDeepProxyCacheRegKey.getSubTableMode(L_, "v"); // L_: DPC | ||
385 | lua_pushlightuserdata(L_, const_cast<DeepPrelude*>(this)); // L_: DPC this | ||
386 | lua_rawget(L_, -2); // L_: DPC deep | ||
387 | lua_remove(L_, -2); // L_: deep | ||
388 | STACK_CHECK(L_, 1); | ||
389 | } | ||
@@ -43,6 +43,8 @@ struct DeepPrelude | |||
43 | : factory{ factory_ } | 43 | : factory{ factory_ } |
44 | { | 44 | { |
45 | } | 45 | } |
46 | |||
47 | void push(lua_State* L_) const; | ||
46 | }; | 48 | }; |
47 | 49 | ||
48 | // ################################################################################################# | 50 | // ################################################################################################# |
diff --git a/src/lanes.cpp b/src/lanes.cpp index 41450f3..25f44d9 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp | |||
@@ -161,6 +161,33 @@ LUAG_FUNC(set_thread_affinity) | |||
161 | 161 | ||
162 | // ################################################################################################# | 162 | // ################################################################################################# |
163 | 163 | ||
164 | LUAG_FUNC(sleep) | ||
165 | { | ||
166 | extern LUAG_FUNC(linda_receive); | ||
167 | |||
168 | Universe* const _U{ Universe::Get(L_) }; | ||
169 | lua_settop(L_, 1); | ||
170 | lua_pushcfunction(L_, LG_linda_receive); // L_: duration|nil receive() | ||
171 | STACK_CHECK_START_REL(L_, 0); // we pushed the function we intend to call, now prepare the arguments | ||
172 | _U->timerLinda->push(L_); // L_: duration|nil receive() timerLinda | ||
173 | if (lua_tostringview(L_, 1) == "indefinitely") { | ||
174 | lua_pushnil(L_); // L_: duration? receive() timerLinda nil | ||
175 | } else if (lua_isnoneornil(L_, 1)) { | ||
176 | lua_pushnumber(L_, 0); // L_: duration? receive() timerLinda 0 | ||
177 | } else if (!lua_isnumber(L_, 1)) { | ||
178 | raise_luaL_argerror(L_, 1, "invalid duration"); | ||
179 | } | ||
180 | else { | ||
181 | lua_pushnumber(L_, lua_tonumber(L_, 1)); // L_: duration? receive() timerLinda duration | ||
182 | } | ||
183 | std::ignore = lua_pushstringview(L_, "ac100de1-a696-4619-b2f0-a26de9d58ab8"); // L_: duration? receive() timerLinda duration key | ||
184 | STACK_CHECK(L_, 3); // 3 arguments ready | ||
185 | lua_call(L_, 3, LUA_MULTRET); // timerLinda:receive(duration,key) // L_: duration? result... | ||
186 | return lua_gettop(L_) - 1; | ||
187 | } | ||
188 | |||
189 | // ################################################################################################# | ||
190 | |||
164 | // --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required | 191 | // --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required |
165 | // with lanes.require, that will call the regular 'require', then populate the lookup database in the source lane | 192 | // with lanes.require, that will call the regular 'require', then populate the lookup database in the source lane |
166 | // module = lanes.require( "modname") | 193 | // module = lanes.require( "modname") |
@@ -592,15 +619,16 @@ extern LUAG_FUNC(linda); | |||
592 | namespace { | 619 | namespace { |
593 | namespace local { | 620 | namespace local { |
594 | static struct luaL_Reg const sLanesFunctions[] = { | 621 | static struct luaL_Reg const sLanesFunctions[] = { |
622 | { Universe::kFinally, Universe::InitializeFinalizer }, | ||
595 | { "linda", LG_linda }, | 623 | { "linda", LG_linda }, |
596 | { "now_secs", LG_now_secs }, | ||
597 | { "wakeup_conv", LG_wakeup_conv }, | ||
598 | { "set_thread_priority", LG_set_thread_priority }, | ||
599 | { "set_thread_affinity", LG_set_thread_affinity }, | ||
600 | { "nameof", LG_nameof }, | 624 | { "nameof", LG_nameof }, |
625 | { "now_secs", LG_now_secs }, | ||
601 | { "register", LG_register }, | 626 | { "register", LG_register }, |
602 | { Universe::kFinally, Universe::InitializeFinalizer }, | ||
603 | { "set_singlethreaded", LG_set_singlethreaded }, | 627 | { "set_singlethreaded", LG_set_singlethreaded }, |
628 | { "set_thread_priority", LG_set_thread_priority }, | ||
629 | { "set_thread_affinity", LG_set_thread_affinity }, | ||
630 | { "sleep", LG_sleep }, | ||
631 | { "wakeup_conv", LG_wakeup_conv }, | ||
604 | { nullptr, nullptr } | 632 | { nullptr, nullptr } |
605 | }; | 633 | }; |
606 | } // namespace local | 634 | } // namespace local |
diff --git a/src/lanes.lua b/src/lanes.lua index f5e81d4..4f11033 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -644,26 +644,6 @@ local configure_timers = function() | |||
644 | end -- configure_timers() | 644 | end -- configure_timers() |
645 | 645 | ||
646 | -- ################################################################################################# | 646 | -- ################################################################################################# |
647 | -- ###################################### lanes.sleep() ############################################ | ||
648 | -- ################################################################################################# | ||
649 | |||
650 | -- nil, "timeout" = sleep([seconds_]) | ||
651 | -- | ||
652 | -- PUBLIC LANES API | ||
653 | local sleep = function(seconds_) | ||
654 | local type = type(seconds_) | ||
655 | if type == "string" then | ||
656 | seconds_ = (seconds_ ~= 'indefinitely') and tonumber(seconds_) or nil | ||
657 | elseif type == "nil" then | ||
658 | seconds_ = 0 | ||
659 | elseif type ~= "number" then | ||
660 | error("invalid duration " .. string_format("%q", tostring(seconds_))) | ||
661 | end | ||
662 | -- receive data on a channel no-one ever sends anything, thus blocking for the specified duration | ||
663 | return timerLinda:receive(seconds_, "ac100de1-a696-4619-b2f0-a26de9d58ab8") | ||
664 | end -- sleep() | ||
665 | |||
666 | -- ################################################################################################# | ||
667 | -- ##################################### lanes.genlock() ########################################### | 647 | -- ##################################### lanes.genlock() ########################################### |
668 | -- ################################################################################################# | 648 | -- ################################################################################################# |
669 | -- These functions are just surface sugar, but make solutions easier to read. | 649 | -- These functions are just surface sugar, but make solutions easier to read. |
@@ -799,22 +779,22 @@ local configure = function(settings_) | |||
799 | 779 | ||
800 | -- activate full interface | 780 | -- activate full interface |
801 | lanes.cancel_error = core.cancel_error | 781 | lanes.cancel_error = core.cancel_error |
782 | lanes.finally = core.finally | ||
802 | lanes.linda = core.linda | 783 | lanes.linda = core.linda |
803 | lanes.nameof = core.nameof | 784 | lanes.nameof = core.nameof |
804 | lanes.now_secs = core.now_secs | 785 | lanes.now_secs = core.now_secs |
805 | lanes.null = core.null | 786 | lanes.null = core.null |
806 | lanes.require = core.require | ||
807 | lanes.register = core.register | 787 | lanes.register = core.register |
808 | lanes.finally = core.finally | 788 | lanes.require = core.require |
809 | lanes.set_singlethreaded = core.set_singlethreaded | 789 | lanes.set_singlethreaded = core.set_singlethreaded |
810 | lanes.set_thread_affinity = core.set_thread_affinity | 790 | lanes.set_thread_affinity = core.set_thread_affinity |
811 | lanes.set_thread_priority = core.set_thread_priority | 791 | lanes.set_thread_priority = core.set_thread_priority |
792 | lanes.sleep = core.sleep | ||
812 | lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false | 793 | lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false |
813 | 794 | ||
814 | lanes.gen = gen | 795 | lanes.gen = gen |
815 | lanes.genatomic = genatomic | 796 | lanes.genatomic = genatomic |
816 | lanes.genlock = genlock | 797 | lanes.genlock = genlock |
817 | lanes.sleep = sleep | ||
818 | lanes.timer = timer | 798 | lanes.timer = timer |
819 | lanes.timer_lane = timer_lane | 799 | lanes.timer_lane = timer_lane |
820 | lanes.timers = timers | 800 | lanes.timers = timers |
diff --git a/src/uniquekey.h b/src/uniquekey.h index cfc8ab0..7e86cbe 100644 --- a/src/uniquekey.h +++ b/src/uniquekey.h | |||
@@ -59,7 +59,7 @@ class RegistryUniqueKey | |||
59 | } | 59 | } |
60 | // --------------------------------------------------------------------------------------------- | 60 | // --------------------------------------------------------------------------------------------- |
61 | template <typename OP> | 61 | template <typename OP> |
62 | void setValue(lua_State* L_, OP operation_) const | 62 | void setValue(lua_State* const L_, OP operation_) const |
63 | { | 63 | { |
64 | // Note we can't check stack consistency because operation is not always a push (could be insert, replace, whatever) | 64 | // Note we can't check stack consistency because operation is not always a push (could be insert, replace, whatever) |
65 | pushKey(L_); // ... key | 65 | pushKey(L_); // ... key |
@@ -91,7 +91,7 @@ class RegistryUniqueKey | |||
91 | } | 91 | } |
92 | // --------------------------------------------------------------------------------------------- | 92 | // --------------------------------------------------------------------------------------------- |
93 | // equivalent to luaL_getsubtable | 93 | // equivalent to luaL_getsubtable |
94 | [[nodiscard]] bool getSubTable(lua_State* const L_, int narr_, int nrec_) const | 94 | [[nodiscard]] bool getSubTable(lua_State* const L_, int const narr_, int const nrec_) const |
95 | { | 95 | { |
96 | STACK_CHECK_START_REL(L_, 0); | 96 | STACK_CHECK_START_REL(L_, 0); |
97 | pushValue(L_); // L_: {}|nil | 97 | pushValue(L_); // L_: {}|nil |
@@ -108,7 +108,7 @@ class RegistryUniqueKey | |||
108 | return false; | 108 | return false; |
109 | } | 109 | } |
110 | // --------------------------------------------------------------------------------------------- | 110 | // --------------------------------------------------------------------------------------------- |
111 | void getSubTableMode(lua_State* L_, const char* mode_) const | 111 | void getSubTableMode(lua_State* const L_, char const* const mode_) const |
112 | { | 112 | { |
113 | STACK_CHECK_START_REL(L_, 0); | 113 | STACK_CHECK_START_REL(L_, 0); |
114 | if (!getSubTable(L_, 0, 0)) { // L_: {} | 114 | if (!getSubTable(L_, 0, 0)) { // L_: {} |
diff --git a/tests/cancel.lua b/tests/cancel.lua index b75d085..92ab439 100644 --- a/tests/cancel.lua +++ b/tests/cancel.lua | |||
@@ -10,8 +10,15 @@ end | |||
10 | local lanes = require "lanes" .configure{ with_timers = false} | 10 | local lanes = require "lanes" .configure{ with_timers = false} |
11 | 11 | ||
12 | local SLEEP = function(...) | 12 | local SLEEP = function(...) |
13 | local k, v = lanes.sleep(...) | 13 | -- just for fun: start a lane that will do the sleeping for us |
14 | assert(k == nil and v == "timeout") | 14 | local sleeperBody = function(...) |
15 | local lanes = require "lanes" | ||
16 | local k, v = lanes.sleep(...) | ||
17 | assert(k == nil and v == "timeout") | ||
18 | end | ||
19 | local sleeper = lanes.gen("*", sleeperBody)(...) | ||
20 | -- then wait for the lane to terminate | ||
21 | sleeper:join() | ||
15 | end | 22 | end |
16 | 23 | ||
17 | local linda = lanes.linda() | 24 | local linda = lanes.linda() |