aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-05-20 12:05:28 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-05-20 12:05:28 +0200
commit4d0d86f197eda7215a259286d8791efe27df8bde (patch)
tree85d7fab9c16192239d039ae56bc08d0913c15130 /src
parent9b24577be41e2933bcb0bace0be85374dbf216c5 (diff)
downloadlanes-4d0d86f197eda7215a259286d8791efe27df8bde.tar.gz
lanes-4d0d86f197eda7215a259286d8791efe27df8bde.tar.bz2
lanes-4d0d86f197eda7215a259286d8791efe27df8bde.zip
Alpha-sort Linda's Lua API
Diffstat (limited to 'src')
-rw-r--r--src/linda.cpp594
1 files changed, 298 insertions, 296 deletions
diff --git a/src/linda.cpp b/src/linda.cpp
index 8ec167d..01b089e 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -105,6 +105,7 @@ template <bool OPT>
105} 105}
106 106
107// ################################################################################################# 107// #################################################################################################
108// #################################### Linda implementation #######################################
108// ################################################################################################# 109// #################################################################################################
109 110
110// Any hashing will do that maps pointers to [0..Universe::nb_keepers[ consistently. 111// Any hashing will do that maps pointers to [0..Universe::nb_keepers[ consistently.
@@ -205,133 +206,192 @@ void Linda::setName(char const* name_, size_t len_)
205// ################################################################################################# 206// #################################################################################################
206 207
207/* 208/*
208 * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...) 209 * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none")
209 *
210 * Send one or more values to a Linda. If there is a limit, all values must fit.
211 * 210 *
212 * Returns: 'true' if the value was queued 211 * Signal linda so that waiting threads wake up as if their own lane was cancelled
213 * 'false' for timeout (only happens when the queue size is limited)
214 * nil, kCancelError if cancelled
215 */ 212 */
216LUAG_FUNC(linda_send) 213LUAG_FUNC(linda_cancel)
217{ 214{
218 auto _send = [](lua_State* L_) { 215 Linda* const _linda{ ToLinda<false>(L_, 1) };
219 Linda* const _linda{ ToLinda<false>(L_, 1) }; 216 char const* _who{ luaL_optstring(L_, 2, "both") };
220 int _key_i{ 2 }; // index of first key, if timeout not there 217 // make sure we got 3 arguments: the linda, a key and a limit
218 luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments");
221 219
222 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; 220 _linda->cancelRequest = CancelRequest::Soft;
223 if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion 221 if (strcmp(_who, "both") == 0) { // tell everyone writers to wake up
224 lua_Duration const _duration{ lua_tonumber(L_, 2) }; 222 _linda->writeHappened.notify_all();
225 if (_duration.count() >= 0.0) { 223 _linda->readHappened.notify_all();
226 _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(_duration); 224 } else if (strcmp(_who, "none") == 0) { // reset flag
227 } else { 225 _linda->cancelRequest = CancelRequest::None;
228 raise_luaL_argerror(L_, 2, "duration cannot be < 0"); 226 } else if (strcmp(_who, "read") == 0) { // tell blocked readers to wake up
229 } 227 _linda->writeHappened.notify_all();
230 ++_key_i; 228 } else if (strcmp(_who, "write") == 0) { // tell blocked writers to wake up
231 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key 229 _linda->readHappened.notify_all();
232 ++_key_i; 230 } else {
233 } 231 raise_luaL_error(L_, "unknown wake hint '%s'", _who);
232 }
233 return 0;
234}
234 235
235 // make sure the key is of a valid type 236// #################################################################################################
236 check_key_types(L_, _key_i, _key_i);
237 237
238 STACK_GROW(L_, 1); 238/*
239 * string = linda:__concat( a, b)
240 *
241 * Return the concatenation of a pair of items, one of them being a linda
242 *
243 * Useful for concatenation or debugging purposes
244 */
245LUAG_FUNC(linda_concat)
246{ // L_: linda1? linda2?
247 bool _atLeastOneLinda{ false };
248 // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both.
249 if (LindaToString<true>(L_, 1)) {
250 _atLeastOneLinda = true;
251 lua_replace(L_, 1);
252 }
253 if (LindaToString<true>(L_, 2)) {
254 _atLeastOneLinda = true;
255 lua_replace(L_, 2);
256 }
257 if (!_atLeastOneLinda) { // should not be possible
258 raise_luaL_error(L_, "internal error: linda_concat called on non-Linda");
259 }
260 lua_concat(L_, 2);
261 return 1;
262}
239 263
240 // make sure there is something to send 264// #################################################################################################
241 if (lua_gettop(L_) == _key_i) {
242 raise_luaL_error(L_, "no data to send");
243 }
244 265
245 // convert nils to some special non-nil sentinel in sent values 266/*
246 keeper_toggle_nil_sentinels(L_, _key_i + 1, LookupMode::ToKeeper); 267 * [val] = linda_count( linda_ud, [key [, ...]])
247 bool _ret{ false }; 268 *
248 CancelRequest _cancel{ CancelRequest::None }; 269 * Get a count of the pending elements in the specified keys
249 KeeperCallResult _pushed; 270 */
250 { 271LUAG_FUNC(linda_count)
251 Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue<Lane>(L_) }; 272{
252 Keeper* const _K{ _linda->whichKeeper() }; 273 auto _count = [](lua_State* L_) {
253 KeeperState const _KL{ _K ? _K->L : nullptr }; 274 Linda* const _linda{ ToLinda<false>(L_, 1) };
254 if (_KL == nullptr) 275 // make sure the keys are of a valid type
255 return 0; 276 check_key_types(L_, 2, lua_gettop(L_));
256 277
257 STACK_CHECK_START_REL(_KL, 0); 278 Keeper* const _K{ _linda->whichKeeper() };
258 for (bool _try_again{ true };;) { 279 KeeperCallResult const _pushed{ keeper_call(_K->L, KEEPER_API(count), L_, _linda, 2) };
259 if (_lane != nullptr) { 280 return OptionalValue(_pushed, L_, "tried to count an invalid key");
260 _cancel = _lane->cancelRequest; 281 };
261 } 282 return Linda::ProtectedCall(L_, _count);
262 _cancel = (_cancel != CancelRequest::None) ? _cancel : _linda->cancelRequest; 283}
263 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything
264 if (!_try_again || _cancel != CancelRequest::None) {
265 _pushed.emplace(0);
266 break;
267 }
268 284
269 STACK_CHECK(_KL, 0); 285// #################################################################################################
270 _pushed = keeper_call(_KL, KEEPER_API(send), L_, _linda, _key_i);
271 if (!_pushed.has_value()) {
272 break;
273 }
274 LUA_ASSERT(L_, _pushed.value() == 1);
275 286
276 _ret = lua_toboolean(L_, -1) ? true : false; 287/*
277 lua_pop(L_, 1); 288 * lightuserdata= linda_deep( linda_ud )
289 *
290 * Return the 'deep' userdata pointer, identifying the Linda.
291 *
292 * This is needed for using Lindas as key indices (timer system needs it);
293 * separately created proxies of the same underlying deep object will have
294 * different userdata and won't be known to be essentially the same deep one
295 * without this.
296 */
297LUAG_FUNC(linda_deep)
298{
299 Linda* const _linda{ ToLinda<false>(L_, 1) };
300 lua_pushlightuserdata(L_, _linda); // just the address
301 return 1;
302}
278 303
279 if (_ret) { 304// #################################################################################################
280 // Wake up ALL waiting threads
281 _linda->writeHappened.notify_all();
282 break;
283 }
284 305
285 // instant timout to bypass the wait syscall 306/*
286 if (std::chrono::steady_clock::now() >= _until) { 307 * table = linda:dump()
287 break; /* no wait; instant timeout */ 308 * return a table listing all pending data inside the linda
288 } 309 */
310LUAG_FUNC(linda_dump)
311{
312 auto _dump = [](lua_State* L_) {
313 Linda* const _linda{ ToLinda<false>(L_, 1) };
314 return keeper_push_linda_storage(*_linda, DestState{ L_ });
315 };
316 return Linda::ProtectedCall(L_, _dump);
317}
289 318
290 // storage limit hit, wait until timeout or signalled that we should try again 319// #################################################################################################
291 { 320
292 Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings 321/*
293 if (_lane != nullptr) { 322 * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1])
294 // change status of lane to "waiting" 323 *
295 _prev_status = _lane->status; // Running, most likely 324 * Get one or more values from Linda.
296 LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case 325 */
297 _lane->status = Lane::Waiting; 326LUAG_FUNC(linda_get)
298 LUA_ASSERT(L_, _lane->waiting_on == nullptr); 327{
299 _lane->waiting_on = &_linda->readHappened; 328 auto get = [](lua_State* L_) {
300 } 329 Linda* const _linda{ ToLinda<false>(L_, 1) };
301 // could not send because no room: wait until some data was read before trying again, or until timeout is reached 330 lua_Integer const _count{ luaL_optinteger(L_, 3, 1) };
302 std::unique_lock<std::mutex> _keeper_lock{ _K->mutex, std::adopt_lock }; 331 luaL_argcheck(L_, _count >= 1, 3, "count should be >= 1");
303 std::cv_status const status{ _linda->readHappened.wait_until(_keeper_lock, _until) }; 332 luaL_argcheck(L_, lua_gettop(L_) <= 3, 4, "too many arguments");
304 _keeper_lock.release(); // we don't want to release the lock! 333 // make sure the key is of a valid type (throws an error if not the case)
305 _try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups 334 check_key_types(L_, 2, 2);
306 if (_lane != nullptr) { 335
307 _lane->waiting_on = nullptr; 336 KeeperCallResult _pushed;
308 _lane->status = _prev_status; 337 if (_linda->cancelRequest == CancelRequest::None) {
309 } 338 Keeper* const _K{ _linda->whichKeeper() };
310 } 339 _pushed = keeper_call(_K->L, KEEPER_API(get), L_, _linda, 2);
340 if (_pushed.value_or(0) > 0) {
341 keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - _pushed.value(), LookupMode::FromKeeper);
311 } 342 }
312 STACK_CHECK(_KL, 0); 343 } else { // linda is cancelled
344 // do nothing and return lanes.cancel_error
345 kCancelError.pushKey(L_);
346 _pushed.emplace(1);
313 } 347 }
348 // an error can be raised if we attempt to read an unregistered function
349 return OptionalValue(_pushed, L_, "tried to copy unsupported types");
350 };
351 return Linda::ProtectedCall(L_, get);
352}
314 353
315 if (!_pushed.has_value()) { 354// #################################################################################################
316 raise_luaL_error(L_, "tried to copy unsupported types"); 355
356/*
357 * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, int)
358 *
359 * Set limit to 1 Linda keys.
360 * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so
361 * Limit can be 0 to completely block everything
362 */
363LUAG_FUNC(linda_limit)
364{
365 auto _limit = [](lua_State* L_) {
366 Linda* const _linda{ ToLinda<false>(L_, 1) };
367 // make sure we got 3 arguments: the linda, a key and a limit
368 luaL_argcheck(L_, lua_gettop(L_) == 3, 2, "wrong number of arguments");
369 // make sure we got a numeric limit
370 lua_Number const _limit{ luaL_checknumber(L_, 3) };
371 if (_limit < 1) {
372 raise_luaL_argerror(L_, 3, "limit must be >= 0");
317 } 373 }
374 // make sure the key is of a valid type
375 check_key_types(L_, 2, 2);
318 376
319 switch (_cancel) { 377 KeeperCallResult _pushed;
320 case CancelRequest::Soft: 378 if (_linda->cancelRequest == CancelRequest::None) {
321 // if user wants to soft-cancel, the call returns lanes.cancel_error 379 Keeper* const _K{ _linda->whichKeeper() };
380 _pushed = keeper_call(_K->L, KEEPER_API(limit), L_, _linda, 2);
381 LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 0 || _pushed.value() == 1)); // no error, optional boolean value saying if we should wake blocked writer threads
382 if (_pushed.value() == 1) {
383 LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TBOOLEAN && lua_toboolean(L_, -1) == 1);
384 _linda->readHappened.notify_all(); // To be done from within the 'K' locking area
385 }
386 } else { // linda is cancelled
387 // do nothing and return lanes.cancel_error
322 kCancelError.pushKey(L_); 388 kCancelError.pushKey(L_);
323 return 1; 389 _pushed.emplace(1);
324
325 case CancelRequest::Hard:
326 // raise an error interrupting execution only in case of hard cancel
327 raise_cancel_error(L_); // raises an error and doesn't return
328
329 default:
330 lua_pushboolean(L_, _ret); // true (success) or false (timeout)
331 return 1;
332 } 390 }
391 // propagate pushed boolean if any
392 return _pushed.value();
333 }; 393 };
334 return Linda::ProtectedCall(L_, _send); 394 return Linda::ProtectedCall(L_, _limit);
335} 395}
336 396
337// ################################################################################################# 397// #################################################################################################
@@ -484,6 +544,138 @@ LUAG_FUNC(linda_receive)
484// ################################################################################################# 544// #################################################################################################
485 545
486/* 546/*
547 * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...)
548 *
549 * Send one or more values to a Linda. If there is a limit, all values must fit.
550 *
551 * Returns: 'true' if the value was queued
552 * 'false' for timeout (only happens when the queue size is limited)
553 * nil, kCancelError if cancelled
554 */
555LUAG_FUNC(linda_send)
556{
557 auto _send = [](lua_State* L_) {
558 Linda* const _linda{ ToLinda<false>(L_, 1) };
559 int _key_i{ 2 }; // index of first key, if timeout not there
560
561 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
562 if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion
563 lua_Duration const _duration{ lua_tonumber(L_, 2) };
564 if (_duration.count() >= 0.0) {
565 _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(_duration);
566 } else {
567 raise_luaL_argerror(L_, 2, "duration cannot be < 0");
568 }
569 ++_key_i;
570 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key
571 ++_key_i;
572 }
573
574 // make sure the key is of a valid type
575 check_key_types(L_, _key_i, _key_i);
576
577 STACK_GROW(L_, 1);
578
579 // make sure there is something to send
580 if (lua_gettop(L_) == _key_i) {
581 raise_luaL_error(L_, "no data to send");
582 }
583
584 // convert nils to some special non-nil sentinel in sent values
585 keeper_toggle_nil_sentinels(L_, _key_i + 1, LookupMode::ToKeeper);
586 bool _ret{ false };
587 CancelRequest _cancel{ CancelRequest::None };
588 KeeperCallResult _pushed;
589 {
590 Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue<Lane>(L_) };
591 Keeper* const _K{ _linda->whichKeeper() };
592 KeeperState const _KL{ _K ? _K->L : nullptr };
593 if (_KL == nullptr)
594 return 0;
595
596 STACK_CHECK_START_REL(_KL, 0);
597 for (bool _try_again{ true };;) {
598 if (_lane != nullptr) {
599 _cancel = _lane->cancelRequest;
600 }
601 _cancel = (_cancel != CancelRequest::None) ? _cancel : _linda->cancelRequest;
602 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything
603 if (!_try_again || _cancel != CancelRequest::None) {
604 _pushed.emplace(0);
605 break;
606 }
607
608 STACK_CHECK(_KL, 0);
609 _pushed = keeper_call(_KL, KEEPER_API(send), L_, _linda, _key_i);
610 if (!_pushed.has_value()) {
611 break;
612 }
613 LUA_ASSERT(L_, _pushed.value() == 1);
614
615 _ret = lua_toboolean(L_, -1) ? true : false;
616 lua_pop(L_, 1);
617
618 if (_ret) {
619 // Wake up ALL waiting threads
620 _linda->writeHappened.notify_all();
621 break;
622 }
623
624 // instant timout to bypass the wait syscall
625 if (std::chrono::steady_clock::now() >= _until) {
626 break; /* no wait; instant timeout */
627 }
628
629 // storage limit hit, wait until timeout or signalled that we should try again
630 {
631 Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings
632 if (_lane != nullptr) {
633 // change status of lane to "waiting"
634 _prev_status = _lane->status; // Running, most likely
635 LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case
636 _lane->status = Lane::Waiting;
637 LUA_ASSERT(L_, _lane->waiting_on == nullptr);
638 _lane->waiting_on = &_linda->readHappened;
639 }
640 // could not send because no room: wait until some data was read before trying again, or until timeout is reached
641 std::unique_lock<std::mutex> _keeper_lock{ _K->mutex, std::adopt_lock };
642 std::cv_status const status{ _linda->readHappened.wait_until(_keeper_lock, _until) };
643 _keeper_lock.release(); // we don't want to release the lock!
644 _try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups
645 if (_lane != nullptr) {
646 _lane->waiting_on = nullptr;
647 _lane->status = _prev_status;
648 }
649 }
650 }
651 STACK_CHECK(_KL, 0);
652 }
653
654 if (!_pushed.has_value()) {
655 raise_luaL_error(L_, "tried to copy unsupported types");
656 }
657
658 switch (_cancel) {
659 case CancelRequest::Soft:
660 // if user wants to soft-cancel, the call returns lanes.cancel_error
661 kCancelError.pushKey(L_);
662 return 1;
663
664 case CancelRequest::Hard:
665 // raise an error interrupting execution only in case of hard cancel
666 raise_cancel_error(L_); // raises an error and doesn't return
667
668 default:
669 lua_pushboolean(L_, _ret); // true (success) or false (timeout)
670 return 1;
671 }
672 };
673 return Linda::ProtectedCall(L_, _send);
674}
675
676// #################################################################################################
677
678/*
487 * [true|lanes.cancel_error] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]]) 679 * [true|lanes.cancel_error] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]])
488 * 680 *
489 * Set one or more value to Linda. Ignores limits. 681 * Set one or more value to Linda. Ignores limits.
@@ -533,154 +725,6 @@ LUAG_FUNC(linda_set)
533 725
534// ################################################################################################# 726// #################################################################################################
535 727
536/*
537 * [val] = linda_count( linda_ud, [key [, ...]])
538 *
539 * Get a count of the pending elements in the specified keys
540 */
541LUAG_FUNC(linda_count)
542{
543 auto _count = [](lua_State* L_) {
544 Linda* const _linda{ ToLinda<false>(L_, 1) };
545 // make sure the keys are of a valid type
546 check_key_types(L_, 2, lua_gettop(L_));
547
548 Keeper* const _K{ _linda->whichKeeper() };
549 KeeperCallResult const _pushed{ keeper_call(_K->L, KEEPER_API(count), L_, _linda, 2) };
550 return OptionalValue(_pushed, L_, "tried to count an invalid key");
551 };
552 return Linda::ProtectedCall(L_, _count);
553}
554
555// #################################################################################################
556
557/*
558 * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1])
559 *
560 * Get one or more values from Linda.
561 */
562LUAG_FUNC(linda_get)
563{
564 auto get = [](lua_State* L_) {
565 Linda* const _linda{ ToLinda<false>(L_, 1) };
566 lua_Integer const _count{ luaL_optinteger(L_, 3, 1) };
567 luaL_argcheck(L_, _count >= 1, 3, "count should be >= 1");
568 luaL_argcheck(L_, lua_gettop(L_) <= 3, 4, "too many arguments");
569 // make sure the key is of a valid type (throws an error if not the case)
570 check_key_types(L_, 2, 2);
571
572 KeeperCallResult _pushed;
573 if (_linda->cancelRequest == CancelRequest::None) {
574 Keeper* const _K{ _linda->whichKeeper() };
575 _pushed = keeper_call(_K->L, KEEPER_API(get), L_, _linda, 2);
576 if (_pushed.value_or(0) > 0) {
577 keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - _pushed.value(), LookupMode::FromKeeper);
578 }
579 } else { // linda is cancelled
580 // do nothing and return lanes.cancel_error
581 kCancelError.pushKey(L_);
582 _pushed.emplace(1);
583 }
584 // an error can be raised if we attempt to read an unregistered function
585 return OptionalValue(_pushed, L_, "tried to copy unsupported types");
586 };
587 return Linda::ProtectedCall(L_, get);
588}
589
590// #################################################################################################
591
592/*
593 * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, int)
594 *
595 * Set limit to 1 Linda keys.
596 * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so
597 * Limit can be 0 to completely block everything
598 */
599LUAG_FUNC(linda_limit)
600{
601 auto _limit = [](lua_State* L_) {
602 Linda* const _linda{ ToLinda<false>(L_, 1) };
603 // make sure we got 3 arguments: the linda, a key and a limit
604 luaL_argcheck(L_, lua_gettop(L_) == 3, 2, "wrong number of arguments");
605 // make sure we got a numeric limit
606 lua_Number const _limit{ luaL_checknumber(L_, 3) };
607 if (_limit < 1) {
608 raise_luaL_argerror(L_, 3, "limit must be >= 0");
609 }
610 // make sure the key is of a valid type
611 check_key_types(L_, 2, 2);
612
613 KeeperCallResult _pushed;
614 if (_linda->cancelRequest == CancelRequest::None) {
615 Keeper* const _K{ _linda->whichKeeper() };
616 _pushed = keeper_call(_K->L, KEEPER_API(limit), L_, _linda, 2);
617 LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 0 || _pushed.value() == 1)); // no error, optional boolean value saying if we should wake blocked writer threads
618 if (_pushed.value() == 1) {
619 LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TBOOLEAN && lua_toboolean(L_, -1) == 1);
620 _linda->readHappened.notify_all(); // To be done from within the 'K' locking area
621 }
622 } else { // linda is cancelled
623 // do nothing and return lanes.cancel_error
624 kCancelError.pushKey(L_);
625 _pushed.emplace(1);
626 }
627 // propagate pushed boolean if any
628 return _pushed.value();
629 };
630 return Linda::ProtectedCall(L_, _limit);
631}
632
633// #################################################################################################
634
635/*
636 * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none")
637 *
638 * Signal linda so that waiting threads wake up as if their own lane was cancelled
639 */
640LUAG_FUNC(linda_cancel)
641{
642 Linda* const _linda{ ToLinda<false>(L_, 1) };
643 char const* _who{ luaL_optstring(L_, 2, "both") };
644 // make sure we got 3 arguments: the linda, a key and a limit
645 luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments");
646
647 _linda->cancelRequest = CancelRequest::Soft;
648 if (strcmp(_who, "both") == 0) { // tell everyone writers to wake up
649 _linda->writeHappened.notify_all();
650 _linda->readHappened.notify_all();
651 } else if (strcmp(_who, "none") == 0) { // reset flag
652 _linda->cancelRequest = CancelRequest::None;
653 } else if (strcmp(_who, "read") == 0) { // tell blocked readers to wake up
654 _linda->writeHappened.notify_all();
655 } else if (strcmp(_who, "write") == 0) { // tell blocked writers to wake up
656 _linda->readHappened.notify_all();
657 } else {
658 raise_luaL_error(L_, "unknown wake hint '%s'", _who);
659 }
660 return 0;
661}
662
663// #################################################################################################
664
665/*
666 * lightuserdata= linda_deep( linda_ud )
667 *
668 * Return the 'deep' userdata pointer, identifying the Linda.
669 *
670 * This is needed for using Lindas as key indices (timer system needs it);
671 * separately created proxies of the same underlying deep object will have
672 * different userdata and won't be known to be essentially the same deep one
673 * without this.
674 */
675LUAG_FUNC(linda_deep)
676{
677 Linda* const _linda{ ToLinda<false>(L_, 1) };
678 lua_pushlightuserdata(L_, _linda); // just the address
679 return 1;
680}
681
682// #################################################################################################
683
684LUAG_FUNC(linda_tostring) 728LUAG_FUNC(linda_tostring)
685{ 729{
686 return LindaToString<false>(L_, 1); 730 return LindaToString<false>(L_, 1);
@@ -689,49 +733,6 @@ LUAG_FUNC(linda_tostring)
689// ################################################################################################# 733// #################################################################################################
690 734
691/* 735/*
692 * string = linda:__concat( a, b)
693 *
694 * Return the concatenation of a pair of items, one of them being a linda
695 *
696 * Useful for concatenation or debugging purposes
697 */
698LUAG_FUNC(linda_concat)
699{ // L_: linda1? linda2?
700 bool _atLeastOneLinda{ false };
701 // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both.
702 if (LindaToString<true>(L_, 1)) {
703 _atLeastOneLinda = true;
704 lua_replace(L_, 1);
705 }
706 if (LindaToString<true>(L_, 2)) {
707 _atLeastOneLinda = true;
708 lua_replace(L_, 2);
709 }
710 if (!_atLeastOneLinda) { // should not be possible
711 raise_luaL_error(L_, "internal error: linda_concat called on non-Linda");
712 }
713 lua_concat(L_, 2);
714 return 1;
715}
716
717// #################################################################################################
718
719/*
720 * table = linda:dump()
721 * return a table listing all pending data inside the linda
722 */
723LUAG_FUNC(linda_dump)
724{
725 auto _dump = [](lua_State* L_) {
726 Linda* const _linda{ ToLinda<false>(L_, 1) };
727 return keeper_push_linda_storage(*_linda, DestState{ L_ });
728 };
729 return Linda::ProtectedCall(L_, _dump);
730}
731
732// #################################################################################################
733
734/*
735 * table/string = linda:__towatch() 736 * table/string = linda:__towatch()
736 * return a table listing all pending data inside the linda, or the stringified linda if empty 737 * return a table listing all pending data inside the linda, or the stringified linda if empty
737 */ 738 */
@@ -770,6 +771,7 @@ namespace global {
770/*static*/ LindaFactory LindaFactory::Instance{ global::sLindaMT }; 771/*static*/ LindaFactory LindaFactory::Instance{ global::sLindaMT };
771 772
772// ################################################################################################# 773// #################################################################################################
774// #################################################################################################
773 775
774/* 776/*
775 * ud = lanes.linda( [name[,group]]) 777 * ud = lanes.linda( [name[,group]])