aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-06-03 15:53:34 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-06-03 15:53:34 +0200
commit44c617f7b756052c7cd059c96f89b85f0f5ec96c (patch)
treea9ac504d7ffa09500c9ea17bab963f1016f6fe79
parent420e50697cd036a0d8ea1601961bd6974703ade1 (diff)
downloadlanes-44c617f7b756052c7cd059c96f89b85f0f5ec96c.tar.gz
lanes-44c617f7b756052c7cd059c96f89b85f0f5ec96c.tar.bz2
lanes-44c617f7b756052c7cd059c96f89b85f0f5ec96c.zip
Moved lanes.sleep implementation to the C-side
-rw-r--r--docs/index.html1
-rw-r--r--src/cancel.h1
-rw-r--r--src/deep.cpp18
-rw-r--r--src/deep.h2
-rw-r--r--src/lanes.cpp38
-rw-r--r--src/lanes.lua26
-rw-r--r--src/uniquekey.h6
-rw-r--r--tests/cancel.lua11
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
26enum class CancelRequest 26enum 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
381void 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}
diff --git a/src/deep.h b/src/deep.h
index 1710aba..e15f07b 100644
--- a/src/deep.h
+++ b/src/deep.h
@@ -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
164LUAG_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);
592namespace { 619namespace {
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()
644end -- configure_timers() 644end -- configure_timers()
645 645
646-- ################################################################################################# 646-- #################################################################################################
647-- ###################################### lanes.sleep() ############################################
648-- #################################################################################################
649
650-- nil, "timeout" = sleep([seconds_])
651--
652-- PUBLIC LANES API
653local 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")
664end -- 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
10local lanes = require "lanes" .configure{ with_timers = false} 10local lanes = require "lanes" .configure{ with_timers = false}
11 11
12local SLEEP = function(...) 12local 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()
15end 22end
16 23
17local linda = lanes.linda() 24local linda = lanes.linda()