diff options
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]]) |
