diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-20 12:05:28 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-20 12:05:28 +0200 |
commit | 4d0d86f197eda7215a259286d8791efe27df8bde (patch) | |
tree | 85d7fab9c16192239d039ae56bc08d0913c15130 /src | |
parent | 9b24577be41e2933bcb0bace0be85374dbf216c5 (diff) | |
download | lanes-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.cpp | 594 |
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 | */ |
216 | LUAG_FUNC(linda_send) | 213 | LUAG_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 | */ | ||
245 | LUAG_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 | { | 271 | LUAG_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 | */ | ||
297 | LUAG_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 | */ |
310 | LUAG_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; | 326 | LUAG_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 | */ | ||
363 | LUAG_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 | */ | ||
555 | LUAG_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 | */ | ||
541 | LUAG_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 | */ | ||
562 | LUAG_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 | */ | ||
599 | LUAG_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 | */ | ||
640 | LUAG_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 | */ | ||
675 | LUAG_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 | |||
684 | LUAG_FUNC(linda_tostring) | 728 | LUAG_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 | */ | ||
698 | LUAG_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 | */ | ||
723 | LUAG_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]]) |