aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-10-25 16:45:28 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-10-25 16:45:28 +0200
commitc860f557a7ba72e6a39ea5db36e293de802adea5 (patch)
tree1d15a9b798b2f812c5315022d579e8f083b4385a /src
parentf2d578555bf51da7a0acd1d1d7e724860b89a149 (diff)
downloadlanes-c860f557a7ba72e6a39ea5db36e293de802adea5.tar.gz
lanes-c860f557a7ba72e6a39ea5db36e293de802adea5.tar.bz2
lanes-c860f557a7ba72e6a39ea5db36e293de802adea5.zip
New linda:wake() and linda.status
Diffstat (limited to 'src')
-rw-r--r--src/linda.cpp109
-rw-r--r--src/linda.h11
-rw-r--r--src/lindafactory.cpp30
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
236void Linda::pushCancelString(lua_State* L_) const
237{
238 luaG_pushstring(L_, cancelStatus == Status::Cancelled ? "cancelled" : "active");
239}
240
241// #################################################################################################
242
236void Linda::releaseKeeper(Keeper* const keeper_) const 243void 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
370static 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
400static 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 */
952LUAG_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
887namespace { 974namespace {
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
42void LindaFactory::createMetatable(lua_State* L_) const 42void 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}