diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/linda.cpp | 109 | ||||
| -rw-r--r-- | src/linda.h | 11 | ||||
| -rw-r--r-- | src/lindafactory.cpp | 30 |
3 files changed, 128 insertions, 22 deletions
diff --git a/src/linda.cpp b/src/linda.cpp index 15e7a2c..67526a7 100644 --- a/src/linda.cpp +++ b/src/linda.cpp | |||
| @@ -233,6 +233,13 @@ int Linda::ProtectedCall(lua_State* const L_, lua_CFunction const f_) | |||
| 233 | 233 | ||
| 234 | // ################################################################################################# | 234 | // ################################################################################################# |
| 235 | 235 | ||
| 236 | void Linda::pushCancelString(lua_State* L_) const | ||
| 237 | { | ||
| 238 | luaG_pushstring(L_, cancelStatus == Status::Cancelled ? "cancelled" : "active"); | ||
| 239 | } | ||
| 240 | |||
| 241 | // ################################################################################################# | ||
| 242 | |||
| 236 | void Linda::releaseKeeper(Keeper* const keeper_) const | 243 | void Linda::releaseKeeper(Keeper* const keeper_) const |
| 237 | { | 244 | { |
| 238 | if (keeper_) { // can be nullptr if we tried to acquire during shutdown | 245 | if (keeper_) { // can be nullptr if we tried to acquire during shutdown |
| @@ -284,18 +291,20 @@ LUAG_FUNC(linda_cancel) | |||
| 284 | // make sure we got 2 arguments: the linda and the cancellation mode | 291 | // make sure we got 2 arguments: the linda and the cancellation mode |
| 285 | luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments"); | 292 | luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments"); |
| 286 | 293 | ||
| 287 | _linda->cancelRequest = CancelRequest::Soft; | 294 | if (_who == "both") { // tell everyone to wake up |
| 288 | if (_who == "both") { // tell everyone writers to wake up | 295 | _linda->cancelStatus = Linda::Status::Cancelled; |
| 289 | _linda->writeHappened.notify_all(); | 296 | _linda->writeHappened.notify_all(); |
| 290 | _linda->readHappened.notify_all(); | 297 | _linda->readHappened.notify_all(); |
| 291 | } else if (_who == "none") { // reset flag | 298 | } else if (_who == "none") { // reset flag |
| 292 | _linda->cancelRequest = CancelRequest::None; | 299 | _linda->cancelStatus = Linda::Status::Active; |
| 293 | } else if (_who == "read") { // tell blocked readers to wake up | 300 | } else if (_who == "read") { // tell blocked readers to wake up |
| 301 | _linda->cancelStatus = Linda::Status::Cancelled; | ||
| 294 | _linda->writeHappened.notify_all(); | 302 | _linda->writeHappened.notify_all(); |
| 295 | } else if (_who == "write") { // tell blocked writers to wake up | 303 | } else if (_who == "write") { // tell blocked writers to wake up |
| 304 | _linda->cancelStatus = Linda::Status::Cancelled; | ||
| 296 | _linda->readHappened.notify_all(); | 305 | _linda->readHappened.notify_all(); |
| 297 | } else { | 306 | } else { |
| 298 | raise_luaL_error(L_, "unknown wake hint '%s'", _who); | 307 | raise_luaL_error(L_, "unknown wake hint '%s'", _who.data()); |
| 299 | } | 308 | } |
| 300 | return 0; | 309 | return 0; |
| 301 | } | 310 | } |
| @@ -357,6 +366,53 @@ LUAG_FUNC(linda_concat) | |||
| 357 | 366 | ||
| 358 | // ################################################################################################# | 367 | // ################################################################################################# |
| 359 | 368 | ||
| 369 | // If key is "status" return the linda cancel status | ||
| 370 | static int linda_index_string(lua_State* L_) | ||
| 371 | { | ||
| 372 | static constexpr StackIndex kIdxSelf{ 1 }; | ||
| 373 | static constexpr StackIndex kIdxKey{ 2 }; | ||
| 374 | |||
| 375 | Linda* const _linda{ ToLinda<false>(L_, kIdxSelf) }; | ||
| 376 | LUA_ASSERT(L_, lua_gettop(L_) == 2); // L_: linda "key" | ||
| 377 | |||
| 378 | std::string_view const _keystr{ luaG_tostring(L_, kIdxKey) }; | ||
| 379 | lua_settop(L_, 2); // keep only our original arguments on the stack | ||
| 380 | |||
| 381 | // look in metatable first | ||
| 382 | lua_getmetatable(L_, kIdxSelf); // L_: linda "key" mt | ||
| 383 | lua_replace(L_, -3); // L_: mt "key" | ||
| 384 | lua_rawget(L_, -2); // L_: mt value | ||
| 385 | if (luaG_type(L_, kIdxTop) != LuaType::NIL) { // found something? | ||
| 386 | return 1; // done | ||
| 387 | } | ||
| 388 | |||
| 389 | lua_pop(L_, 2); // L_: | ||
| 390 | if (_keystr == "status") { | ||
| 391 | _linda->pushCancelString(L_); // L_: "<status>" | ||
| 392 | return 1; | ||
| 393 | } | ||
| 394 | raise_luaL_error(L_, "unknown field '%s'", _keystr.data()); | ||
| 395 | } | ||
| 396 | |||
| 397 | // ################################################################################################# | ||
| 398 | |||
| 399 | // linda:__index(key,usr) -> value | ||
| 400 | static LUAG_FUNC(linda_index) | ||
| 401 | { | ||
| 402 | static constexpr StackIndex kIdxKey{ 2 }; | ||
| 403 | LUA_ASSERT(L_, lua_gettop(L_) == 2); | ||
| 404 | |||
| 405 | switch (luaG_type(L_, kIdxKey)) { | ||
| 406 | case LuaType::STRING: | ||
| 407 | return linda_index_string(L_); // stack modification is undefined, returned value is at the top | ||
| 408 | |||
| 409 | default: // unknown key | ||
| 410 | raise_luaL_error(L_, "Unsupported linda indexing key type %s", luaG_typename(L_, kIdxKey).data()); | ||
| 411 | } | ||
| 412 | } | ||
| 413 | |||
| 414 | // ################################################################################################# | ||
| 415 | |||
| 360 | /* | 416 | /* |
| 361 | * [val] = linda_count( linda_ud, [key [, ...]]) | 417 | * [val] = linda_count( linda_ud, [key [, ...]]) |
| 362 | * | 418 | * |
| @@ -433,7 +489,7 @@ LUAG_FUNC(linda_get) | |||
| 433 | CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); | 489 | CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); |
| 434 | 490 | ||
| 435 | KeeperCallResult _pushed; | 491 | KeeperCallResult _pushed; |
| 436 | if (_linda->cancelRequest == CancelRequest::None) { | 492 | if (_linda->cancelStatus == Linda::Active) { |
| 437 | Keeper* const _keeper{ _linda->whichKeeper() }; | 493 | Keeper* const _keeper{ _linda->whichKeeper() }; |
| 438 | _pushed = keeper_call(_keeper->K, KEEPER_API(get), L_, _linda, StackIndex{ 2 }); | 494 | _pushed = keeper_call(_keeper->K, KEEPER_API(get), L_, _linda, StackIndex{ 2 }); |
| 439 | } else { // linda is cancelled | 495 | } else { // linda is cancelled |
| @@ -477,7 +533,7 @@ LUAG_FUNC(linda_limit) | |||
| 477 | CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); | 533 | CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); |
| 478 | 534 | ||
| 479 | KeeperCallResult _pushed; | 535 | KeeperCallResult _pushed; |
| 480 | if (_linda->cancelRequest == CancelRequest::None) { | 536 | if (_linda->cancelStatus == Linda::Active) { |
| 481 | if (_unlimited) { | 537 | if (_unlimited) { |
| 482 | LUA_ASSERT(L_, lua_gettop(L_) == 3 && luaG_tostring(L_, StackIndex{ 3 }) == "unlimited"); | 538 | LUA_ASSERT(L_, lua_gettop(L_) == 3 && luaG_tostring(L_, StackIndex{ 3 }) == "unlimited"); |
| 483 | // inside the Keeper, unlimited is signified with a -1 limit (can't use nil because of nil kNilSentinel conversions!) | 539 | // inside the Keeper, unlimited is signified with a -1 limit (can't use nil because of nil kNilSentinel conversions!) |
| @@ -586,7 +642,9 @@ LUAG_FUNC(linda_receive) | |||
| 586 | if (_lane != nullptr) { | 642 | if (_lane != nullptr) { |
| 587 | _cancel = _lane->cancelRequest; | 643 | _cancel = _lane->cancelRequest; |
| 588 | } | 644 | } |
| 589 | _cancel = (_cancel != CancelRequest::None) ? _cancel : _linda->cancelRequest; | 645 | _cancel = (_cancel != CancelRequest::None) |
| 646 | ? _cancel | ||
| 647 | : ((_linda->cancelStatus == Linda::Cancelled) ? CancelRequest::Soft : CancelRequest::None); | ||
| 590 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | 648 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything |
| 591 | if (!_try_again || _cancel != CancelRequest::None) { | 649 | if (!_try_again || _cancel != CancelRequest::None) { |
| 592 | _pushed.emplace(0); | 650 | _pushed.emplace(0); |
| @@ -723,7 +781,9 @@ LUAG_FUNC(linda_send) | |||
| 723 | if (_lane != nullptr) { | 781 | if (_lane != nullptr) { |
| 724 | _cancel = _lane->cancelRequest; | 782 | _cancel = _lane->cancelRequest; |
| 725 | } | 783 | } |
| 726 | _cancel = (_cancel != CancelRequest::None) ? _cancel : _linda->cancelRequest; | 784 | _cancel = (_cancel != CancelRequest::None) |
| 785 | ? _cancel | ||
| 786 | : ((_linda->cancelStatus == Linda::Cancelled) ? CancelRequest::Soft : CancelRequest::None); | ||
| 727 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | 787 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything |
| 728 | if (!_try_again || _cancel != CancelRequest::None) { | 788 | if (!_try_again || _cancel != CancelRequest::None) { |
| 729 | _pushed.emplace(0); | 789 | _pushed.emplace(0); |
| @@ -826,7 +886,7 @@ LUAG_FUNC(linda_set) | |||
| 826 | CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); | 886 | CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); |
| 827 | 887 | ||
| 828 | KeeperCallResult _pushed; | 888 | KeeperCallResult _pushed; |
| 829 | if (_linda->cancelRequest == CancelRequest::None) { | 889 | if (_linda->cancelStatus == Linda::Active) { |
| 830 | Keeper* const _keeper{ _linda->whichKeeper() }; | 890 | Keeper* const _keeper{ _linda->whichKeeper() }; |
| 831 | _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, StackIndex{ 2 }); | 891 | _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, StackIndex{ 2 }); |
| 832 | if (_pushed.has_value()) { // no error? | 892 | if (_pushed.has_value()) { // no error? |
| @@ -884,6 +944,33 @@ LUAG_FUNC(linda_towatch) | |||
| 884 | 944 | ||
| 885 | // ################################################################################################# | 945 | // ################################################################################################# |
| 886 | 946 | ||
| 947 | /* | ||
| 948 | * (void) = linda_wake( linda_ud, "read"|"write"|"both") | ||
| 949 | * | ||
| 950 | * Signal linda so that waiting threads wake up as if their own lane was cancelled | ||
| 951 | */ | ||
| 952 | LUAG_FUNC(linda_wake) | ||
| 953 | { | ||
| 954 | Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; | ||
| 955 | std::string_view const _who{ luaG_optstring(L_, StackIndex{ 2 }, "both") }; | ||
| 956 | // make sure we got 2 arguments: the linda and the wake targets | ||
| 957 | luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments"); | ||
| 958 | |||
| 959 | if (_who == "both") { // tell everyone to wake up | ||
| 960 | _linda->writeHappened.notify_all(); | ||
| 961 | _linda->readHappened.notify_all(); | ||
| 962 | } else if (_who == "read") { // simulate a read to wake writers | ||
| 963 | _linda->writeHappened.notify_all(); | ||
| 964 | } else if (_who == "write") { // simulate a write to wake readers | ||
| 965 | _linda->readHappened.notify_all(); | ||
| 966 | } else { | ||
| 967 | raise_luaL_error(L_, "unknown wake hint '%s'", _who.data()); | ||
| 968 | } | ||
| 969 | return 0; | ||
| 970 | } | ||
| 971 | |||
| 972 | // ################################################################################################# | ||
| 973 | |||
| 887 | namespace { | 974 | namespace { |
| 888 | namespace local { | 975 | namespace local { |
| 889 | static luaL_Reg const sLindaMT[] = { | 976 | static luaL_Reg const sLindaMT[] = { |
| @@ -891,6 +978,7 @@ namespace { | |||
| 891 | { "__close", LG_linda_close }, | 978 | { "__close", LG_linda_close }, |
| 892 | #endif // LUA_VERSION_NUM >= 504 | 979 | #endif // LUA_VERSION_NUM >= 504 |
| 893 | { "__concat", LG_linda_concat }, | 980 | { "__concat", LG_linda_concat }, |
| 981 | { "__index", LG_linda_index }, | ||
| 894 | { "__tostring", LG_linda_tostring }, | 982 | { "__tostring", LG_linda_tostring }, |
| 895 | #if HAVE_DECODA_SUPPORT() | 983 | #if HAVE_DECODA_SUPPORT() |
| 896 | { "__towatch", LG_linda_towatch }, // Decoda __towatch support | 984 | { "__towatch", LG_linda_towatch }, // Decoda __towatch support |
| @@ -904,6 +992,7 @@ namespace { | |||
| 904 | { "receive", LG_linda_receive }, | 992 | { "receive", LG_linda_receive }, |
| 905 | { "send", LG_linda_send }, | 993 | { "send", LG_linda_send }, |
| 906 | { "set", LG_linda_set }, | 994 | { "set", LG_linda_set }, |
| 995 | { "wake", LG_linda_wake }, | ||
| 907 | { nullptr, nullptr } | 996 | { nullptr, nullptr } |
| 908 | }; | 997 | }; |
| 909 | } // namespace local | 998 | } // namespace local |
| @@ -985,7 +1074,7 @@ LUAG_FUNC(linda) | |||
| 985 | LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, _nuv); // L_: name group close_handler linda | 1074 | LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, _nuv); // L_: name group close_handler linda |
| 986 | if (_closeHandlerIdx != 0) { | 1075 | if (_closeHandlerIdx != 0) { |
| 987 | lua_replace(L_, 2); // L_: name linda close_handler | 1076 | lua_replace(L_, 2); // L_: name linda close_handler |
| 988 | lua_setiuservalue(L_, StackIndex{ 2 }, 1); // L_: name linda | 1077 | lua_setiuservalue(L_, StackIndex{ 2 }, UserValueIndex{ 1 }); // L_: name linda |
| 989 | } | 1078 | } |
| 990 | // depending on whether we have a handler or not, the stack is not in the same state at this point | 1079 | // depending on whether we have a handler or not, the stack is not in the same state at this point |
| 991 | // just make sure we have our Linda at the top | 1080 | // just make sure we have our Linda at the top |
diff --git a/src/linda.h b/src/linda.h index c05fb14..02b0514 100644 --- a/src/linda.h +++ b/src/linda.h | |||
| @@ -40,7 +40,15 @@ class Linda | |||
| 40 | } | 40 | } |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | enum class Status | ||
| 44 | { | ||
| 45 | Active, | ||
| 46 | Cancelled | ||
| 47 | }; | ||
| 48 | using enum Status; | ||
| 49 | |||
| 43 | private: | 50 | private: |
| 51 | |||
| 44 | static constexpr size_t kEmbeddedNameLength = 24; | 52 | static constexpr size_t kEmbeddedNameLength = 24; |
| 45 | using EmbeddedName = std::array<char, kEmbeddedNameLength>; | 53 | using EmbeddedName = std::array<char, kEmbeddedNameLength>; |
| 46 | // depending on the name length, it is either embedded inside the Linda, or allocated separately | 54 | // depending on the name length, it is either embedded inside the Linda, or allocated separately |
| @@ -53,7 +61,7 @@ class Linda | |||
| 53 | std::condition_variable writeHappened{}; | 61 | std::condition_variable writeHappened{}; |
| 54 | Universe* const U{ nullptr }; // the universe this linda belongs to | 62 | Universe* const U{ nullptr }; // the universe this linda belongs to |
| 55 | KeeperIndex const keeperIndex{ -1 }; // the keeper associated to this linda | 63 | KeeperIndex const keeperIndex{ -1 }; // the keeper associated to this linda |
| 56 | CancelRequest cancelRequest{ CancelRequest::None }; | 64 | Status cancelStatus{ Status::Active }; |
| 57 | 65 | ||
| 58 | public: | 66 | public: |
| 59 | [[nodiscard]] static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internalAllocator.alloc(size_); } | 67 | [[nodiscard]] static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internalAllocator.alloc(size_); } |
| @@ -89,6 +97,7 @@ class Linda | |||
| 89 | }; | 97 | }; |
| 90 | void releaseKeeper(Keeper* keeper_) const; | 98 | void releaseKeeper(Keeper* keeper_) const; |
| 91 | [[nodiscard]] static int ProtectedCall(lua_State* L_, lua_CFunction f_); | 99 | [[nodiscard]] static int ProtectedCall(lua_State* L_, lua_CFunction f_); |
| 100 | void pushCancelString(lua_State* L_) const; | ||
| 92 | [[nodiscard]] KeeperOperationInProgress startKeeperOperation(lua_State* const L_) { return KeeperOperationInProgress{ *this, L_ }; }; | 101 | [[nodiscard]] KeeperOperationInProgress startKeeperOperation(lua_State* const L_) { return KeeperOperationInProgress{ *this, L_ }; }; |
| 93 | [[nodiscard]] Keeper* whichKeeper() const { return U->keepers.getKeeper(keeperIndex); } | 102 | [[nodiscard]] Keeper* whichKeeper() const { return U->keepers.getKeeper(keeperIndex); } |
| 94 | }; | 103 | }; |
diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp index cb801dd..11e2cff 100644 --- a/src/lindafactory.cpp +++ b/src/lindafactory.cpp | |||
| @@ -41,25 +41,33 @@ static constexpr std::string_view kLindaMetatableName{ "Linda" }; | |||
| 41 | 41 | ||
| 42 | void LindaFactory::createMetatable(lua_State* L_) const | 42 | void LindaFactory::createMetatable(lua_State* L_) const |
| 43 | { | 43 | { |
| 44 | static constexpr std::string_view kIndex{ "__index" }; | ||
| 45 | |||
| 44 | STACK_CHECK_START_REL(L_, 0); | 46 | STACK_CHECK_START_REL(L_, 0); |
| 45 | lua_newtable(L_); | 47 | lua_newtable(L_); // L_: mt |
| 46 | // metatable is its own index | ||
| 47 | lua_pushvalue(L_, -1); | ||
| 48 | lua_setfield(L_, -2, "__index"); | ||
| 49 | 48 | ||
| 50 | // protect metatable from external access | 49 | // protect metatable from external access |
| 51 | luaG_pushstring(L_, kLindaMetatableName); | 50 | luaG_pushstring(L_, kLindaMetatableName); // L_: mt "<name>" |
| 52 | lua_setfield(L_, -2, "__metatable"); | 51 | lua_setfield(L_, -2, "__metatable"); // L_: mt |
| 53 | 52 | ||
| 54 | // the linda functions | 53 | // the linda functions |
| 55 | luaG_registerlibfuncs(L_, mLindaMT); | 54 | luaG_registerlibfuncs(L_, mLindaMT); |
| 56 | 55 | ||
| 57 | // some constants | 56 | // some constants |
| 58 | kLindaBatched.pushKey(L_); | 57 | kLindaBatched.pushKey(L_); // L_: mt kLindaBatched |
| 59 | lua_setfield(L_, -2, "batched"); | 58 | lua_setfield(L_, -2, "batched"); // L_: mt |
| 60 | 59 | ||
| 61 | kNilSentinel.pushKey(L_); | 60 | kNilSentinel.pushKey(L_); // L_: mt kNilSentinel |
| 62 | lua_setfield(L_, -2, "null"); | 61 | lua_setfield(L_, -2, "null"); // L_: mt |
| 62 | |||
| 63 | // if the metatable contains __index, leave it as is | ||
| 64 | if (luaG_getfield(L_, kIdxTop, kIndex) != LuaType::NIL) { // L_: mt __index | ||
| 65 | lua_pop(L_, 1); // L_: mt __index | ||
| 66 | } else { | ||
| 67 | // metatable is its own index | ||
| 68 | lua_pushvalue(L_, kIdxTop); // L_: mt mt | ||
| 69 | luaG_setfield(L_, StackIndex{ -2 }, kIndex); // L_: mt | ||
| 70 | } | ||
| 63 | 71 | ||
| 64 | STACK_CHECK(L_, 1); | 72 | STACK_CHECK(L_, 1); |
| 65 | } | 73 | } |
