aboutsummaryrefslogtreecommitdiff
path: root/src/linda.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/linda.cpp510
1 files changed, 258 insertions, 252 deletions
diff --git a/src/linda.cpp b/src/linda.cpp
index 1119d71..1f4b19d 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -47,7 +47,7 @@ namespace {
47 { 47 {
48 STACK_CHECK_START_REL(L_, 0); 48 STACK_CHECK_START_REL(L_, 0);
49 for (StackIndex const _i : std::ranges::iota_view{ start_, StackIndex{ end_ + 1 } }) { 49 for (StackIndex const _i : std::ranges::iota_view{ start_, StackIndex{ end_ + 1 } }) {
50 LuaType const _t{ luaG_type(L_, _i) }; 50 LuaType const _t{ luaW_type(L_, _i) };
51 switch (_t) { 51 switch (_t) {
52 case LuaType::BOOLEAN: 52 case LuaType::BOOLEAN:
53 case LuaType::NUMBER: 53 case LuaType::NUMBER:
@@ -62,7 +62,7 @@ namespace {
62 62
63 case LuaType::LIGHTUSERDATA: 63 case LuaType::LIGHTUSERDATA:
64 { 64 {
65 static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel }; 65 static constexpr std::array<std::reference_wrapper<UniqueKey const>, 2> kKeysToCheck{ kCancelError, kNilSentinel };
66 for (UniqueKey const& _key : kKeysToCheck) { 66 for (UniqueKey const& _key : kKeysToCheck) {
67 if (_key.equals(L_, _i)) { 67 if (_key.equals(L_, _i)) {
68 raise_luaL_error(L_, "argument #%d: can't use %s as a slot", _i, _key.debugName.data()); 68 raise_luaL_error(L_, "argument #%d: can't use %s as a slot", _i, _key.debugName.data());
@@ -109,13 +109,13 @@ namespace {
109 { 109 {
110 Linda* const _linda{ ToLinda<OPT>(L_, idx_) }; 110 Linda* const _linda{ ToLinda<OPT>(L_, idx_) };
111 if (_linda != nullptr) { 111 if (_linda != nullptr) {
112 luaG_pushstring(L_, "Linda: "); 112 luaW_pushstring(L_, "Linda: ");
113 std::string_view const _lindaName{ _linda->getName() }; 113 std::string_view const _lindaName{ _linda->getName() };
114 if (!_lindaName.empty()) { 114 if (!_lindaName.empty()) {
115 luaG_pushstring(L_, _lindaName); 115 luaW_pushstring(L_, _lindaName);
116 } else { 116 } else {
117 // obfuscate the pointer so that we can't read the value with our eyes out of a script 117 // obfuscate the pointer so that we can't read the value with our eyes out of a script
118 luaG_pushstring(L_, "%p", _linda->obfuscated()); 118 luaW_pushstring(L_, "%p", _linda->obfuscated());
119 } 119 }
120 lua_concat(L_, 2); 120 lua_concat(L_, 2);
121 return 1; 121 return 1;
@@ -123,6 +123,8 @@ namespace {
123 return 0; 123 return 0;
124 } 124 }
125 125
126 // #############################################################################################
127
126 // a helper to process the timeout argument of linda:send() and linda:receive() 128 // a helper to process the timeout argument of linda:send() and linda:receive()
127 [[nodiscard]] 129 [[nodiscard]]
128 static auto ProcessTimeoutArg(lua_State* const L_) 130 static auto ProcessTimeoutArg(lua_State* const L_)
@@ -130,7 +132,7 @@ namespace {
130 StackIndex _key_i{ 2 }; // index of first slot, if timeout not there 132 StackIndex _key_i{ 2 }; // index of first slot, if timeout not there
131 133
132 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; 134 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
133 if (luaG_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion 135 if (luaW_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion
134 lua_Duration const _duration{ lua_tonumber(L_, 2) }; 136 lua_Duration const _duration{ lua_tonumber(L_, 2) };
135 if (_duration.count() >= 0.0) { 137 if (_duration.count() >= 0.0) {
136 _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(_duration); 138 _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(_duration);
@@ -145,6 +147,160 @@ namespace {
145 } 147 }
146 148
147 // ############################################################################################# 149 // #############################################################################################
150 static bool WaitInternal([[maybe_unused]] lua_State* const L_, Lane* const lane_, Linda* const linda_, Keeper* const keeper_, std::condition_variable& waitingOn_, std::chrono::time_point<std::chrono::steady_clock> until_)
151 {
152 Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings
153 if (lane_ != nullptr) {
154 // change status of lane to "waiting"
155 _prev_status = lane_->status.load(std::memory_order_acquire); // Running, most likely
156 LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case
157 LUA_ASSERT(L_, lane_->waiting_on == nullptr);
158 lane_->waiting_on = &waitingOn_;
159 lane_->status.store(Lane::Waiting, std::memory_order_release);
160 }
161
162 // wait until the final target date by small increments, interrupting regularly so that we can check for cancel requests,
163 // in case some timing issue caused a cancel request to be issued, and the condvar signalled, before we actually wait for it
164 auto const [_forceTryAgain, _until_check_cancel] = std::invoke([until_, wakePeriod = linda_->getWakePeriod()] {
165 auto _until_check_cancel{ std::chrono::time_point<std::chrono::steady_clock>::max() };
166 if (wakePeriod.count() > 0.0f) {
167 _until_check_cancel = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(wakePeriod);
168 }
169 bool const _forceTryAgain{ _until_check_cancel < until_ };
170 return std::make_tuple(_forceTryAgain, _forceTryAgain ? _until_check_cancel : until_);
171 });
172
173 // operation can't complete: wake when it is signalled to be possible, or when timeout is reached
174 std::unique_lock<std::mutex> _guard{ keeper_->mutex, std::adopt_lock };
175 std::cv_status const _status{ waitingOn_.wait_until(_guard, _until_check_cancel) };
176 _guard.release(); // we don't want to unlock the mutex on exit!
177 bool const _try_again{ _forceTryAgain || (_status == std::cv_status::no_timeout) }; // detect spurious wakeups
178 if (lane_ != nullptr) {
179 lane_->waiting_on = nullptr;
180 lane_->status.store(_prev_status, std::memory_order_release);
181 }
182 return _try_again;
183 }
184
185 // #############################################################################################
186
187 // the implementation for linda:receive() and linda:receive_batched()
188 static int ReceiveInternal(lua_State* const L_, bool const batched_)
189 {
190 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
191
192 auto const [_key_i, _until] = ProcessTimeoutArg(L_);
193
194 keeper_api_t _selected_keeper_receive{ nullptr };
195 int _expected_pushed_min{ 0 }, _expected_pushed_max{ 0 };
196 // are we in batched mode?
197 if (batched_) {
198 // make sure the keys are of a valid type
199 CheckKeyTypes(L_, _key_i, _key_i);
200 // receive multiple values from a single slot
201 _selected_keeper_receive = KEEPER_API(receive_batched);
202 // we expect a user-defined amount of return value
203 _expected_pushed_min = (int) luaL_checkinteger(L_, _key_i + 1);
204 if (_expected_pushed_min < 1) {
205 raise_luaL_argerror(L_, StackIndex{ _key_i + 1 }, "bad min count");
206 }
207 _expected_pushed_max = (int) luaL_optinteger(L_, _key_i + 2, _expected_pushed_min);
208 // don't forget to count the slot in addition to the values
209 ++_expected_pushed_min;
210 ++_expected_pushed_max;
211 if (_expected_pushed_min > _expected_pushed_max) {
212 raise_luaL_argerror(L_, StackIndex{ _key_i + 2 }, "batched min/max error");
213 }
214 } else {
215 // make sure the keys are of a valid type
216 CheckKeyTypes(L_, _key_i, StackIndex{ lua_gettop(L_) });
217 // receive a single value, checking multiple slots
218 _selected_keeper_receive = KEEPER_API(receive);
219 // we expect a single (value, slot) pair of returned values
220 _expected_pushed_min = _expected_pushed_max = 2;
221 }
222
223 Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue<Lane>(L_) };
224 Keeper* const _keeper{ _linda->whichKeeper() };
225 KeeperState const _K{ _keeper ? _keeper->K : KeeperState{ static_cast<lua_State*>(nullptr) } };
226 if (_K == nullptr)
227 return 0;
228
229 CancelRequest _cancel{ CancelRequest::None };
230 KeeperCallResult _pushed{};
231
232 STACK_CHECK_START_REL(_K, 0);
233 for (bool _try_again{ true };;) {
234 if (_lane != nullptr) {
235 _cancel = _lane->cancelRequest.load(std::memory_order_relaxed);
236 }
237 _cancel = (_cancel != CancelRequest::None)
238 ? _cancel
239 : ((_linda->cancelStatus == Linda::Cancelled) ? CancelRequest::Soft : CancelRequest::None);
240
241 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything
242 if (!_try_again || _cancel != CancelRequest::None) {
243 _pushed.emplace(0);
244 break;
245 }
246
247 // all arguments of receive() but the first are passed to the keeper's receive function
248 STACK_CHECK(_K, 0);
249 _pushed = keeper_call(_K, _selected_keeper_receive, L_, _linda, _key_i);
250 if (!_pushed.has_value()) {
251 break;
252 }
253 if (_pushed.value() > 0) {
254 LUA_ASSERT(L_, _pushed.value() >= _expected_pushed_min && _pushed.value() <= _expected_pushed_max);
255 if (kRestrictedChannel.equals(L_, StackIndex{ kIdxTop })) {
256 raise_luaL_error(L_, "Key is restricted");
257 }
258 _linda->readHappened.notify_all();
259 break;
260 }
261
262 if (std::chrono::steady_clock::now() >= _until) {
263 break; /* instant timeout */
264 }
265
266 // nothing received, wait until timeout or signalled that we should try again
267 _try_again = WaitInternal(L_, _lane, _linda, _keeper, _linda->writeHappened, _until);
268 }
269 STACK_CHECK(_K, 0);
270
271 if (!_pushed.has_value()) {
272 raise_luaL_error(L_, "tried to copy unsupported types");
273 }
274
275 switch (_cancel) {
276 case CancelRequest::None:
277 {
278 int const _nbPushed{ _pushed.value() };
279 if (_nbPushed == 0) {
280 // not enough data in the linda slot to fulfill the request, return nil, "timeout"
281 lua_pushnil(L_);
282 luaW_pushstring(L_, "timeout");
283 return 2;
284 }
285 return _nbPushed;
286 }
287
288 case CancelRequest::Soft:
289 // if user wants to soft-cancel, the call returns nil, kCancelError
290 lua_pushnil(L_);
291 kCancelError.pushKey(L_);
292 return 2;
293
294 case CancelRequest::Hard:
295 // raise an error interrupting execution only in case of hard cancel
296 raise_cancel_error(L_); // raises an error and doesn't return
297
298 default:
299 raise_luaL_error(L_, "internal error: unknown cancel request");
300 }
301 }
302
303 // #############################################################################################
148 // ############################################################################################# 304 // #############################################################################################
149} // namespace 305} // namespace
150// ################################################################################################# 306// #################################################################################################
@@ -158,9 +314,10 @@ LUAG_FUNC(linda);
158// ################################################################################################# 314// #################################################################################################
159// ################################################################################################# 315// #################################################################################################
160 316
161Linda::Linda(Universe* const U_, LindaGroup const group_, std::string_view const& name_) 317Linda::Linda(Universe* const U_, std::string_view const& name_, lua_Duration const wake_period_, LindaGroup const group_)
162: DeepPrelude{ LindaFactory::Instance } 318: DeepPrelude{ LindaFactory::Instance }
163, U{ U_ } 319, U{ U_ }
320, wakePeriod{ wake_period_ }
164, keeperIndex{ group_ % U_->keepers.getNbKeepers() } 321, keeperIndex{ group_ % U_->keepers.getNbKeepers() }
165{ 322{
166 setName(name_); 323 setName(name_);
@@ -181,6 +338,7 @@ Keeper* Linda::acquireKeeper() const
181 Keeper* const _keeper{ whichKeeper() }; 338 Keeper* const _keeper{ whichKeeper() };
182 if (_keeper) { 339 if (_keeper) {
183 _keeper->mutex.lock(); 340 _keeper->mutex.lock();
341 keeperOperationCount.fetch_add(1, std::memory_order_seq_cst);
184 } 342 }
185 return _keeper; 343 return _keeper;
186} 344}
@@ -192,13 +350,17 @@ Linda* Linda::CreateTimerLinda(lua_State* const L_)
192 STACK_CHECK_START_REL(L_, 0); // L_: 350 STACK_CHECK_START_REL(L_, 0); // L_:
193 // Initialize 'timerLinda'; a common Linda object shared by all states 351 // Initialize 'timerLinda'; a common Linda object shared by all states
194 lua_pushcfunction(L_, LG_linda); // L_: lanes.linda 352 lua_pushcfunction(L_, LG_linda); // L_: lanes.linda
195 luaG_pushstring(L_, "lanes-timer"); // L_: lanes.linda "lanes-timer" 353 lua_createtable(L_, 0, 3); // L_: lanes.linda {}
196 lua_pushinteger(L_, 0); // L_: lanes.linda "lanes-timer" 0 354 luaW_pushstring(L_, "lanes-timer"); // L_: lanes.linda {} "lanes-timer"
197 lua_call(L_, 2, 1); // L_: linda 355 luaW_setfield(L_, StackIndex{ -2 }, std::string_view{ "name" }); // L_: lanes.linda { .name="lanes-timer" }
356 lua_pushinteger(L_, 0); // L_: lanes.linda { .name="lanes-timer" } 0
357 luaW_setfield(L_, StackIndex{ -2 }, std::string_view{ "group" }); // L_: lanes.linda { .name="lanes-timer" .group = 0 }
358 // note that wake_period is not set (will default to the value in the universe)
359 lua_call(L_, 1, 1); // L_: linda
198 STACK_CHECK(L_, 1); 360 STACK_CHECK(L_, 1);
199 361
200 // Proxy userdata contents is only a 'DeepPrelude*' pointer 362 // Proxy userdata contents is only a 'DeepPrelude*' pointer
201 auto const _timerLinda{ *luaG_tofulluserdata<Linda*>(L_, kIdxTop) }; 363 auto const _timerLinda{ *luaW_tofulluserdata<Linda*>(L_, kIdxTop) };
202 // increment refcount so that this linda remains alive as long as the universe exists. 364 // increment refcount so that this linda remains alive as long as the universe exists.
203 _timerLinda->refcount.fetch_add(1, std::memory_order_relaxed); 365 _timerLinda->refcount.fetch_add(1, std::memory_order_relaxed);
204 lua_pop(L_, 1); // L_: 366 lua_pop(L_, 1); // L_:
@@ -261,7 +423,6 @@ int Linda::ProtectedCall(lua_State* const L_, lua_CFunction const f_)
261 // doing LindaFactory::deleteDeepObjectInternal -> keeper_call(clear) 423 // doing LindaFactory::deleteDeepObjectInternal -> keeper_call(clear)
262 lua_gc(L_, LUA_GCSTOP, 0); 424 lua_gc(L_, LUA_GCSTOP, 0);
263 425
264 LUA_ASSERT_CODE(auto const _koip{ _linda->startKeeperOperation(L_) });
265 // if we didn't do anything wrong, the keeper stack should be clean 426 // if we didn't do anything wrong, the keeper stack should be clean
266 LUA_ASSERT(L_, lua_gettop(_K) == 0); 427 LUA_ASSERT(L_, lua_gettop(_K) == 0);
267 428
@@ -269,7 +430,7 @@ int Linda::ProtectedCall(lua_State* const L_, lua_CFunction const f_)
269 lua_pushcfunction(L_, f_); 430 lua_pushcfunction(L_, f_);
270 lua_insert(L_, 1); 431 lua_insert(L_, 1);
271 // do a protected call 432 // do a protected call
272 LuaError const _rc{ lua_pcall(L_, lua_gettop(L_) - 1, LUA_MULTRET, 0) }; 433 LuaError const _rc{ ToLuaError(lua_pcall(L_, lua_gettop(L_) - 1, LUA_MULTRET, 0)) };
273 // whatever happens, the keeper state stack must be empty when we are done 434 // whatever happens, the keeper state stack must be empty when we are done
274 lua_settop(_K, 0); 435 lua_settop(_K, 0);
275 436
@@ -291,7 +452,7 @@ int Linda::ProtectedCall(lua_State* const L_, lua_CFunction const f_)
291 452
292void Linda::pushCancelString(lua_State* L_) const 453void Linda::pushCancelString(lua_State* L_) const
293{ 454{
294 luaG_pushstring(L_, cancelStatus == Status::Cancelled ? "cancelled" : "active"); 455 luaW_pushstring(L_, cancelStatus == Status::Cancelled ? "cancelled" : "active");
295} 456}
296 457
297// ################################################################################################# 458// #################################################################################################
@@ -300,6 +461,7 @@ void Linda::releaseKeeper(Keeper* const keeper_) const
300{ 461{
301 if (keeper_) { // can be nullptr if we tried to acquire during shutdown 462 if (keeper_) { // can be nullptr if we tried to acquire during shutdown
302 assert(keeper_ == whichKeeper()); 463 assert(keeper_ == whichKeeper());
464 keeperOperationCount.fetch_sub(1, std::memory_order_seq_cst);
303 keeper_->mutex.unlock(); 465 keeper_->mutex.unlock();
304 } 466 }
305} 467}
@@ -343,7 +505,7 @@ void Linda::setName(std::string_view const& name_)
343LUAG_FUNC(linda_cancel) 505LUAG_FUNC(linda_cancel)
344{ 506{
345 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; 507 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
346 std::string_view const _who{ luaG_optstring(L_, StackIndex{ 2 }, "both") }; 508 std::string_view const _who{ luaW_optstring(L_, StackIndex{ 2 }, "both") };
347 // make sure we got 2 arguments: the linda and the cancellation mode 509 // make sure we got 2 arguments: the linda and the cancellation mode
348 luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments"); 510 luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments");
349 511
@@ -431,13 +593,13 @@ static int linda_index_string(lua_State* L_)
431 Linda* const _linda{ ToLinda<false>(L_, kIdxSelf) }; 593 Linda* const _linda{ ToLinda<false>(L_, kIdxSelf) };
432 LUA_ASSERT(L_, lua_gettop(L_) == 2); // L_: linda "key" 594 LUA_ASSERT(L_, lua_gettop(L_) == 2); // L_: linda "key"
433 595
434 std::string_view const _keystr{ luaG_tostring(L_, kIdxKey) }; 596 std::string_view const _keystr{ luaW_tostring(L_, kIdxKey) };
435 lua_settop(L_, 2); // keep only our original arguments on the stack 597 lua_settop(L_, 2); // keep only our original arguments on the stack
436 598
437 // look in metatable first 599 // look in metatable first
438 lua_getmetatable(L_, kIdxSelf); // L_: linda "key" mt 600 lua_getmetatable(L_, kIdxSelf); // L_: linda "key" mt
439 lua_replace(L_, -3); // L_: mt "key" 601 lua_replace(L_, -3); // L_: mt "key"
440 if (luaG_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // found something? // L_: mt value 602 if (luaW_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // found something? // L_: mt value
441 return 1; // done 603 return 1; // done
442 } 604 }
443 605
@@ -457,12 +619,12 @@ static LUAG_FUNC(linda_index)
457 static constexpr StackIndex kIdxKey{ 2 }; 619 static constexpr StackIndex kIdxKey{ 2 };
458 LUA_ASSERT(L_, lua_gettop(L_) == 2); 620 LUA_ASSERT(L_, lua_gettop(L_) == 2);
459 621
460 switch (luaG_type(L_, kIdxKey)) { 622 switch (luaW_type(L_, kIdxKey)) {
461 case LuaType::STRING: 623 case LuaType::STRING:
462 return linda_index_string(L_); // stack modification is undefined, returned value is at the top 624 return linda_index_string(L_); // stack modification is undefined, returned value is at the top
463 625
464 default: // unknown key 626 default: // unknown key
465 raise_luaL_error(L_, "Unsupported linda indexing key type %s", luaG_typename(L_, kIdxKey).data()); 627 raise_luaL_error(L_, "Unsupported linda indexing key type %s", luaW_typename(L_, kIdxKey).data());
466 } 628 }
467} 629}
468 630
@@ -605,7 +767,7 @@ LUAG_FUNC(linda_limit)
605 int const _nargs{ lua_gettop(L_) }; 767 int const _nargs{ lua_gettop(L_) };
606 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments"); 768 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments");
607 // make sure we got a numeric limit, or "unlimited", (or nothing) 769 // make sure we got a numeric limit, or "unlimited", (or nothing)
608 bool const _unlimited{ luaG_tostring(L_, StackIndex{ 3 }) == "unlimited" }; 770 bool const _unlimited{ luaW_tostring(L_, StackIndex{ 3 }) == "unlimited" };
609 LindaLimit const _val{ _unlimited ? std::numeric_limits<LindaLimit::type>::max() : static_cast<LindaLimit::type>(luaL_optinteger(L_, 3, 0)) }; 771 LindaLimit const _val{ _unlimited ? std::numeric_limits<LindaLimit::type>::max() : static_cast<LindaLimit::type>(luaL_optinteger(L_, 3, 0)) };
610 if (_val < 0) { 772 if (_val < 0) {
611 raise_luaL_argerror(L_, StackIndex{ 3 }, "limit must be >= 0"); 773 raise_luaL_argerror(L_, StackIndex{ 3 }, "limit must be >= 0");
@@ -616,23 +778,23 @@ LUAG_FUNC(linda_limit)
616 KeeperCallResult _pushed; 778 KeeperCallResult _pushed;
617 if (_linda->cancelStatus == Linda::Active) { 779 if (_linda->cancelStatus == Linda::Active) {
618 if (_unlimited) { 780 if (_unlimited) {
619 LUA_ASSERT(L_, lua_gettop(L_) == 3 && luaG_tostring(L_, StackIndex{ 3 }) == "unlimited"); 781 LUA_ASSERT(L_, lua_gettop(L_) == 3 && luaW_tostring(L_, StackIndex{ 3 }) == "unlimited");
620 // inside the Keeper, unlimited is signified with a -1 limit (can't use nil because of nil kNilSentinel conversions!) 782 // inside the Keeper, unlimited is signified with a -1 limit (can't use nil because of nil kNilSentinel conversions!)
621 lua_pop(L_, 1); // L_: linda slot 783 lua_pop(L_, 1); // L_: linda slot
622 lua_pushinteger(L_, -1); // L_: linda slot nil 784 lua_pushinteger(L_, -1); // L_: linda slot nil
623 } 785 }
624 Keeper* const _keeper{ _linda->whichKeeper() }; 786 Keeper* const _keeper{ _linda->whichKeeper() };
625 _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, StackIndex{ 2 }); 787 _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, StackIndex{ 2 });
626 LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 2) && luaG_type(L_, kIdxTop) == LuaType::STRING); 788 LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 2) && luaW_type(L_, kIdxTop) == LuaType::STRING);
627 if (_nargs == 3) { // 3 args: setting the limit 789 if (_nargs == 3) { // 3 args: setting the limit
628 // changing the limit: no error, boolean value saying if we should wake blocked writer threads 790 // changing the limit: no error, boolean value saying if we should wake blocked writer threads
629 LUA_ASSERT(L_, luaG_type(L_, StackIndex{ -2 }) == LuaType::BOOLEAN); // L_: bool string 791 LUA_ASSERT(L_, luaW_type(L_, StackIndex{ -2 }) == LuaType::BOOLEAN); // L_: bool string
630 if (lua_toboolean(L_, -2)) { 792 if (lua_toboolean(L_, -2)) {
631 _linda->readHappened.notify_all(); // To be done from within the 'K' locking area 793 _linda->readHappened.notify_all(); // To be done from within the 'K' locking area
632 } 794 }
633 } else { // 2 args: reading the limit 795 } else { // 2 args: reading the limit
634 // reading the limit: a number >=0 or "unlimited" 796 // reading the limit: a number >=0 or "unlimited"
635 LUA_ASSERT(L_, luaG_type(L_, StackIndex{ -2 }) == LuaType::NUMBER || luaG_tostring(L_, StackIndex{ -2 }) == "unlimited"); 797 LUA_ASSERT(L_, luaW_type(L_, StackIndex{ -2 }) == LuaType::NUMBER || luaW_tostring(L_, StackIndex{ -2 }) == "unlimited");
636 } 798 }
637 } else { // linda is cancelled 799 } else { // linda is cancelled
638 // do nothing and return nil,lanes.cancel_error 800 // do nothing and return nil,lanes.cancel_error
@@ -650,153 +812,25 @@ LUAG_FUNC(linda_limit)
650// ################################################################################################# 812// #################################################################################################
651 813
652/* 814/*
653 * 2 modes of operation 815 * [val, slot] = linda:receive([timeout_secs_num=nil], key_num|str|bool|lightuserdata [, ...] )
654 * [val, slot]= linda:receive([timeout_secs_num=nil], key_num|str|bool|lightuserdata [, ...] )
655 * Consumes a single value from the Linda, in any slot. 816 * Consumes a single value from the Linda, in any slot.
656 * Returns: received value (which is consumed from the slot), and the slot which had it 817 * Returns: received value (which is consumed from the slot), and the slot which had it
657
658 * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, min_COUNT[, max_COUNT])
659 * Consumes between min_COUNT and max_COUNT values from the linda, from a single slot.
660 * returns the actual consumed values, or nil if there weren't enough values to consume
661 */ 818 */
662LUAG_FUNC(linda_receive) 819LUAG_FUNC(linda_receive)
663{ 820{
664 static constexpr lua_CFunction _receive{ 821 return Linda::ProtectedCall(L_, [](lua_State* const L_) { return ReceiveInternal(L_, false); });
665 +[](lua_State* const L_) { 822}
666 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
667
668 auto [_key_i, _until] = ProcessTimeoutArg(L_);
669
670 keeper_api_t _selected_keeper_receive{ nullptr };
671 int _expected_pushed_min{ 0 }, _expected_pushed_max{ 0 };
672 // are we in batched mode?
673 if (kLindaBatched.equals(L_, _key_i)) {
674 // no need to pass linda.batched in the keeper state
675 ++_key_i;
676 // make sure the keys are of a valid type
677 CheckKeyTypes(L_, _key_i, _key_i);
678 // receive multiple values from a single slot
679 _selected_keeper_receive = KEEPER_API(receive_batched);
680 // we expect a user-defined amount of return value
681 _expected_pushed_min = (int) luaL_checkinteger(L_, _key_i + 1);
682 if (_expected_pushed_min < 1) {
683 raise_luaL_argerror(L_, StackIndex{ _key_i + 1 }, "bad min count");
684 }
685 _expected_pushed_max = (int) luaL_optinteger(L_, _key_i + 2, _expected_pushed_min);
686 // don't forget to count the slot in addition to the values
687 ++_expected_pushed_min;
688 ++_expected_pushed_max;
689 if (_expected_pushed_min > _expected_pushed_max) {
690 raise_luaL_argerror(L_, StackIndex{ _key_i + 2 }, "batched min/max error");
691 }
692 } else {
693 // make sure the keys are of a valid type
694 CheckKeyTypes(L_, _key_i, StackIndex{ lua_gettop(L_) });
695 // receive a single value, checking multiple slots
696 _selected_keeper_receive = KEEPER_API(receive);
697 // we expect a single (value, slot) pair of returned values
698 _expected_pushed_min = _expected_pushed_max = 2;
699 }
700
701 Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue<Lane>(L_) };
702 Keeper* const _keeper{ _linda->whichKeeper() };
703 KeeperState const _K{ _keeper ? _keeper->K : KeeperState{ static_cast<lua_State*>(nullptr) } };
704 if (_K == nullptr)
705 return 0;
706
707 CancelRequest _cancel{ CancelRequest::None };
708 KeeperCallResult _pushed{};
709
710 STACK_CHECK_START_REL(_K, 0);
711 for (bool _try_again{ true };;) {
712 if (_lane != nullptr) {
713 _cancel = _lane->cancelRequest.load(std::memory_order_relaxed);
714 }
715 _cancel = (_cancel != CancelRequest::None)
716 ? _cancel
717 : ((_linda->cancelStatus == Linda::Cancelled) ? CancelRequest::Soft : CancelRequest::None);
718 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything
719 if (!_try_again || _cancel != CancelRequest::None) {
720 _pushed.emplace(0);
721 break;
722 }
723
724 // all arguments of receive() but the first are passed to the keeper's receive function
725 STACK_CHECK(_K, 0);
726 _pushed = keeper_call(_K, _selected_keeper_receive, L_, _linda, _key_i);
727 if (!_pushed.has_value()) {
728 break;
729 }
730 if (_pushed.value() > 0) {
731 LUA_ASSERT(L_, _pushed.value() >= _expected_pushed_min && _pushed.value() <= _expected_pushed_max);
732 if (kRestrictedChannel.equals(L_, StackIndex{ kIdxTop })) {
733 raise_luaL_error(L_, "Key is restricted");
734 }
735 _linda->readHappened.notify_all();
736 break;
737 }
738
739 if (std::chrono::steady_clock::now() >= _until) {
740 break; /* instant timeout */
741 }
742
743 // nothing received, wait until timeout or signalled that we should try again
744 {
745 Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings
746 if (_lane != nullptr) {
747 // change status of lane to "waiting"
748 _prev_status = _lane->status.load(std::memory_order_acquire); // Running, most likely
749 LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case
750 LUA_ASSERT(L_, _lane->waiting_on == nullptr);
751 _lane->waiting_on = &_linda->writeHappened;
752 _lane->status.store(Lane::Waiting, std::memory_order_release);
753 }
754 // not enough data to read: wakeup when data was sent, or when timeout is reached
755 std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock };
756 std::cv_status const _status{ _linda->writeHappened.wait_until(_guard, _until) };
757 _guard.release(); // we don't want to unlock the mutex on exit!
758 _try_again = (_status == std::cv_status::no_timeout); // detect spurious wakeups
759 if (_lane != nullptr) {
760 _lane->waiting_on = nullptr;
761 _lane->status.store(_prev_status, std::memory_order_release);
762 }
763 }
764 }
765 STACK_CHECK(_K, 0);
766
767 if (!_pushed.has_value()) {
768 raise_luaL_error(L_, "tried to copy unsupported types");
769 }
770
771 switch (_cancel) {
772 case CancelRequest::None:
773 {
774 int const _nbPushed{ _pushed.value() };
775 if (_nbPushed == 0) {
776 // not enough data in the linda slot to fulfill the request, return nil, "timeout"
777 lua_pushnil(L_);
778 luaG_pushstring(L_, "timeout");
779 return 2;
780 }
781 return _nbPushed;
782 }
783
784 case CancelRequest::Soft:
785 // if user wants to soft-cancel, the call returns nil, kCancelError
786 lua_pushnil(L_);
787 kCancelError.pushKey(L_);
788 return 2;
789 823
790 case CancelRequest::Hard: 824// #################################################################################################
791 // raise an error interrupting execution only in case of hard cancel
792 raise_cancel_error(L_); // raises an error and doesn't return
793 825
794 default: 826/*
795 raise_luaL_error(L_, "internal error: unknown cancel request"); 827 * [val1, ... valCOUNT] = linda_receive_batched( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata, min_COUNT[, max_COUNT])
796 } 828 * Consumes between min_COUNT and max_COUNT values from the linda, from a single slot.
797 } 829 * returns the actual consumed values, or nil if there weren't enough values to consume
798 }; 830 */
799 return Linda::ProtectedCall(L_, _receive); 831LUAG_FUNC(linda_receive_batched)
832{
833 return Linda::ProtectedCall(L_, [](lua_State* const L_) { return ReceiveInternal(L_, true); });
800} 834}
801 835
802// ################################################################################################# 836// #################################################################################################
@@ -816,7 +850,7 @@ LUAG_FUNC(linda_restrict)
816 int const _nargs{ lua_gettop(L_) }; 850 int const _nargs{ lua_gettop(L_) };
817 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments"); 851 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments");
818 // make sure we got a known restrict mode, (or nothing) 852 // make sure we got a known restrict mode, (or nothing)
819 std::string_view const _mode{ luaG_tostring(L_, StackIndex{ 3 }) }; 853 std::string_view const _mode{ luaW_tostring(L_, StackIndex{ 3 }) };
820 if (!_mode.empty() && (_mode != "none" && _mode != "set/get" && _mode != "send/receive")) { 854 if (!_mode.empty() && (_mode != "none" && _mode != "set/get" && _mode != "send/receive")) {
821 raise_luaL_argerror(L_, StackIndex{ 3 }, "unknown restrict mode"); 855 raise_luaL_argerror(L_, StackIndex{ 3 }, "unknown restrict mode");
822 } 856 }
@@ -828,7 +862,7 @@ LUAG_FUNC(linda_restrict)
828 Keeper* const _keeper{ _linda->whichKeeper() }; 862 Keeper* const _keeper{ _linda->whichKeeper() };
829 _pushed = keeper_call(_keeper->K, KEEPER_API(restrict), L_, _linda, StackIndex{ 2 }); 863 _pushed = keeper_call(_keeper->K, KEEPER_API(restrict), L_, _linda, StackIndex{ 2 });
830 // we should get a single return value: the string describing the previous restrict mode 864 // we should get a single return value: the string describing the previous restrict mode
831 LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 1) && luaG_type(L_, kIdxTop) == LuaType::STRING); 865 LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 1) && luaW_type(L_, kIdxTop) == LuaType::STRING);
832 } else { // linda is cancelled 866 } else { // linda is cancelled
833 // do nothing and return nil,lanes.cancel_error 867 // do nothing and return nil,lanes.cancel_error
834 lua_pushnil(L_); 868 lua_pushnil(L_);
@@ -889,6 +923,7 @@ LUAG_FUNC(linda_send)
889 _cancel = (_cancel != CancelRequest::None) 923 _cancel = (_cancel != CancelRequest::None)
890 ? _cancel 924 ? _cancel
891 : ((_linda->cancelStatus == Linda::Cancelled) ? CancelRequest::Soft : CancelRequest::None); 925 : ((_linda->cancelStatus == Linda::Cancelled) ? CancelRequest::Soft : CancelRequest::None);
926
892 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything 927 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything
893 if (!_try_again || _cancel != CancelRequest::None) { 928 if (!_try_again || _cancel != CancelRequest::None) {
894 _pushed.emplace(0); 929 _pushed.emplace(0);
@@ -921,26 +956,7 @@ LUAG_FUNC(linda_send)
921 } 956 }
922 957
923 // storage limit hit, wait until timeout or signalled that we should try again 958 // storage limit hit, wait until timeout or signalled that we should try again
924 { 959 _try_again = WaitInternal(L_, _lane, _linda, _keeper, _linda->readHappened, _until);
925 Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings
926 if (_lane != nullptr) {
927 // change status of lane to "waiting"
928 _prev_status = _lane->status.load(std::memory_order_acquire); // Running, most likely
929 LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case
930 LUA_ASSERT(L_, _lane->waiting_on == nullptr);
931 _lane->waiting_on = &_linda->readHappened;
932 _lane->status.store(Lane::Waiting, std::memory_order_release);
933 }
934 // could not send because no room: wait until some data was read before trying again, or until timeout is reached
935 std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock };
936 std::cv_status const status{ _linda->readHappened.wait_until(_guard, _until) };
937 _guard.release(); // we don't want to unlock the mutex on exit!
938 _try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups
939 if (_lane != nullptr) {
940 _lane->waiting_on = nullptr;
941 _lane->status.store(_prev_status, std::memory_order_release);
942 }
943 }
944 } 960 }
945 STACK_CHECK(_K, 0); 961 STACK_CHECK(_K, 0);
946 962
@@ -966,7 +982,7 @@ LUAG_FUNC(linda_send)
966 } else { 982 } else {
967 // not enough room in the Linda slot to fulfill the request, return nil, "timeout" 983 // not enough room in the Linda slot to fulfill the request, return nil, "timeout"
968 lua_pushnil(L_); 984 lua_pushnil(L_);
969 luaG_pushstring(L_, "timeout"); 985 luaW_pushstring(L_, "timeout");
970 return 2; 986 return 2;
971 } 987 }
972 } 988 }
@@ -1001,7 +1017,7 @@ LUAG_FUNC(linda_set)
1001 if (kRestrictedChannel.equals(L_, kIdxTop)) { 1017 if (kRestrictedChannel.equals(L_, kIdxTop)) {
1002 raise_luaL_error(L_, "Key is restricted"); 1018 raise_luaL_error(L_, "Key is restricted");
1003 } 1019 }
1004 LUA_ASSERT(L_, _pushed.value() == 2 && luaG_type(L_, kIdxTop) == LuaType::STRING && luaG_type(L_, StackIndex{ -2 }) == LuaType::BOOLEAN); 1020 LUA_ASSERT(L_, _pushed.value() == 2 && luaW_type(L_, kIdxTop) == LuaType::STRING && luaW_type(L_, StackIndex{ -2 }) == LuaType::BOOLEAN);
1005 1021
1006 if (_has_data) { 1022 if (_has_data) {
1007 // we put some data in the slot, tell readers that they should wake 1023 // we put some data in the slot, tell readers that they should wake
@@ -1063,7 +1079,7 @@ LUAG_FUNC(linda_towatch)
1063LUAG_FUNC(linda_wake) 1079LUAG_FUNC(linda_wake)
1064{ 1080{
1065 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; 1081 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
1066 std::string_view const _who{ luaG_optstring(L_, StackIndex{ 2 }, "both") }; 1082 std::string_view const _who{ luaW_optstring(L_, StackIndex{ 2 }, "both") };
1067 // make sure we got 2 arguments: the linda and the wake targets 1083 // make sure we got 2 arguments: the linda and the wake targets
1068 luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments"); 1084 luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments");
1069 1085
@@ -1102,6 +1118,7 @@ namespace {
1102 { "get", LG_linda_get }, 1118 { "get", LG_linda_get },
1103 { "limit", LG_linda_limit }, 1119 { "limit", LG_linda_limit },
1104 { "receive", LG_linda_receive }, 1120 { "receive", LG_linda_receive },
1121 { "receive_batched", LG_linda_receive_batched },
1105 { "restrict", LG_linda_restrict }, 1122 { "restrict", LG_linda_restrict },
1106 { "send", LG_linda_send }, 1123 { "send", LG_linda_send },
1107 { "set", LG_linda_set }, 1124 { "set", LG_linda_set },
@@ -1118,88 +1135,77 @@ namespace {
1118// ################################################################################################# 1135// #################################################################################################
1119 1136
1120/* 1137/*
1121 * ud = lanes.linda( [name[,group[,close_handler]]]) 1138 * ud = lanes.linda{.name = <string>, .group = <number>, .close_handler = <callable>, .wake_period = <number>}
1122 * 1139 *
1123 * returns a linda object, or raises an error if creation failed 1140 * returns a linda object, or raises an error if creation failed
1124 */ 1141 */
1125LUAG_FUNC(linda) 1142LUAG_FUNC(linda)
1126{ 1143{
1127 static constexpr StackIndex kLastArg{ LUA_VERSION_NUM >= 504 ? 3 : 2 }; 1144 // unpack the received table on the stack, putting name wake_period group close_handler in that order
1128 StackIndex const _top{ lua_gettop(L_) }; 1145 StackIndex const _top{ lua_gettop(L_) };
1129 luaL_argcheck(L_, _top <= kLastArg, _top, "too many arguments"); 1146 luaL_argcheck(L_, _top <= 1, _top, "too many arguments");
1130 StackIndex _closeHandlerIdx{}; 1147 if (_top == 0) {
1131 StackIndex _nameIdx{}; 1148 lua_settop(L_, 3); // L_: nil nil nil
1132 StackIndex _groupIdx{}; 1149 }
1133 for (StackIndex const _i : std::ranges::iota_view{ StackIndex{ 1 }, StackIndex{ _top + 1 }}) { 1150 else if (!lua_istable(L_, kIdxTop)) {
1134 switch (luaG_type(L_, _i)) { 1151 luaL_argerror(L_, 1, "expecting a table");
1152 } else {
1153 auto* const _U{ Universe::Get(L_) };
1154 lua_getfield(L_, 1, "wake_period"); // L_: {} wake_period
1155 if (lua_isnil(L_, kIdxTop)) {
1156 lua_pop(L_, 1);
1157 lua_pushnumber(L_, _U->lindaWakePeriod.count());
1158 } else if (luaW_type(L_, kIdxTop) == LuaType::STRING) {
1159 if (luaW_tostring(L_, kIdxTop) != "never") {
1160 luaL_argerror(L_, 1, "invalid wake_period");
1161 } else {
1162 lua_pop(L_, 1);
1163 lua_pushnumber(L_, 0);
1164 }
1165 }
1166 else {
1167 luaL_argcheck(L_, luaL_optnumber(L_, 2, 0) > 0, 1, "wake_period must be > 0");
1168 }
1169
1170 lua_getfield(L_, 1, "group"); // L_: {} wake_period group
1171 int const _nbKeepers{ _U->keepers.getNbKeepers() };
1172 if (lua_isnil(L_, kIdxTop)) {
1173 luaL_argcheck(L_, _nbKeepers < 2, 0, "Group is mandatory in multiple Keeper scenarios");
1174 } else {
1175 int const _group{ static_cast<int>(lua_tointeger(L_, kIdxTop)) };
1176 luaL_argcheck(L_, _group >= 0 && _group < _nbKeepers, 1, "group out of range");
1177 }
1178
1135#if LUA_VERSION_NUM >= 504 // to-be-closed support starts with Lua 5.4 1179#if LUA_VERSION_NUM >= 504 // to-be-closed support starts with Lua 5.4
1136 case LuaType::FUNCTION: 1180 lua_getfield(L_, 1, "close_handler"); // L_: {} wake_period group close_handler
1137 luaL_argcheck(L_, _closeHandlerIdx == 0, _i, "More than one __close handler"); 1181 LuaType const _handlerType{ luaW_type(L_, kIdxTop) };
1138 _closeHandlerIdx = _i; 1182 if (_handlerType == LuaType::NIL) {
1139 break; 1183 lua_pop(L_, 1); // L_: {} wake_period group
1140 1184 } else if (_handlerType == LuaType::USERDATA || _handlerType == LuaType::TABLE) {
1141 case LuaType::USERDATA: 1185 luaL_argcheck(L_, luaL_getmetafield(L_, kIdxTop, "__call") != 0, 1, "__close handler is not callable");
1142 case LuaType::TABLE:
1143 luaL_argcheck(L_, _closeHandlerIdx == 0, _i, "More than one __close handler");
1144 luaL_argcheck(L_, luaL_getmetafield(L_, _i, "__call") != 0, _i, "__close handler is not callable");
1145 lua_pop(L_, 1); // luaL_getmetafield() pushed the field, we need to pop it 1186 lua_pop(L_, 1); // luaL_getmetafield() pushed the field, we need to pop it
1146 _closeHandlerIdx = _i; 1187 } else {
1147 break; 1188 luaL_argcheck(L_, _handlerType == LuaType::FUNCTION, 1, "__close handler is not a function");
1189 }
1148#endif // LUA_VERSION_NUM >= 504 1190#endif // LUA_VERSION_NUM >= 504
1149 1191
1150 case LuaType::STRING: 1192 auto const _nameType{ luaW_getfield(L_, StackIndex{ 1 }, "name") }; // L_: {} wake_period group [close_handler] name
1151 luaL_argcheck(L_, _nameIdx == 0, _i, "More than one name"); 1193 luaL_argcheck(L_, _nameType == LuaType::NIL || _nameType == LuaType::STRING, 1, "name is not a string");
1152 _nameIdx = _i; 1194 lua_replace(L_, 1); // L_: name wake_period group [close_handler]
1153 break;
1154
1155 case LuaType::NUMBER:
1156 luaL_argcheck(L_, _groupIdx == 0, _i, "More than one group");
1157 _groupIdx = _i;
1158 break;
1159
1160 default:
1161 luaL_argcheck(L_, false, _i, "Bad argument type (should be a string, a number, or a callable type)");
1162 }
1163 }
1164
1165 int const _nbKeepers{ Universe::Get(L_)->keepers.getNbKeepers() };
1166 if (!_groupIdx) {
1167 luaL_argcheck(L_, _nbKeepers < 2, 0, "Group is mandatory in multiple Keeper scenarios");
1168 } else {
1169 int const _group{ static_cast<int>(lua_tointeger(L_, _groupIdx)) };
1170 luaL_argcheck(L_, _group >= 0 && _group < _nbKeepers, _groupIdx, "Group out of range");
1171 } 1195 }
1172 1196
1173 // done with argument checking, let's proceed 1197 // done with argument checking, let's proceed
1174 if constexpr (LUA_VERSION_NUM >= 504) { 1198 if (lua_gettop(L_) == 4) {
1175 // make sure we have kMaxArgs arguments on the stack for processing, with name, group, and handler, in that order
1176 lua_settop(L_, kLastArg); // L_: a b c
1177 // If either index is 0, lua_settop() adjusted the stack with a nil in slot kLastArg
1178 lua_pushvalue(L_, _closeHandlerIdx ? _closeHandlerIdx : kLastArg); // L_: a b c close_handler
1179 lua_pushvalue(L_, _groupIdx ? _groupIdx : kLastArg); // L_: a b c close_handler group
1180 lua_pushvalue(L_, _nameIdx ? _nameIdx : kLastArg); // L_: a b c close_handler group name
1181 lua_replace(L_, 1); // L_: name b c close_handler group
1182 lua_replace(L_, 2); // L_: name group c close_handler
1183 lua_replace(L_, 3); // L_: name group close_handler
1184
1185 // if we have a __close handler, we need a uservalue slot to store it 1199 // if we have a __close handler, we need a uservalue slot to store it
1186 UserValueCount const _nuv{ _closeHandlerIdx ? 1 : 0 }; 1200 LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, UserValueCount{ 1 }); // L_: name wake_period group [close_handler] linda
1187 LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, _nuv); // L_: name group close_handler linda 1201 lua_replace(L_, 3); // L_: name wake_period linda close_handler
1188 if (_closeHandlerIdx != 0) { 1202 lua_setiuservalue(L_, StackIndex{ 3 }, UserValueIndex{ 1 }); // L_: name wake_period linda
1189 lua_replace(L_, 2); // L_: name linda close_handler
1190 lua_setiuservalue(L_, StackIndex{ 2 }, UserValueIndex{ 1 }); // L_: name linda
1191 }
1192 // depending on whether we have a handler or not, the stack is not in the same state at this point 1203 // depending on whether we have a handler or not, the stack is not in the same state at this point
1193 // just make sure we have our Linda at the top 1204 // just make sure we have our Linda at the top
1194 LUA_ASSERT(L_, ToLinda<true>(L_, kIdxTop)); 1205 LUA_ASSERT(L_, ToLinda<true>(L_, kIdxTop));
1195 return 1; 1206 return 1;
1196 } else { // no to-be-closed support 1207 } else { // no to-be-closed support
1197 // ensure we have name, group in that order on the stack 1208 LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, UserValueCount{ 0 }); // L_: name wake_period group linda
1198 if (_nameIdx > _groupIdx) {
1199 lua_insert(L_, 1); // L_: name group
1200 }
1201 LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, UserValueCount{ 0 }); // L_: name group linda
1202 return 1; 1209 return 1;
1203 } 1210 }
1204
1205} 1211}