diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-10-25 16:45:28 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-10-25 16:45:28 +0200 |
commit | c860f557a7ba72e6a39ea5db36e293de802adea5 (patch) | |
tree | 1d15a9b798b2f812c5315022d579e8f083b4385a | |
parent | f2d578555bf51da7a0acd1d1d7e724860b89a149 (diff) | |
download | lanes-c860f557a7ba72e6a39ea5db36e293de802adea5.tar.gz lanes-c860f557a7ba72e6a39ea5db36e293de802adea5.tar.bz2 lanes-c860f557a7ba72e6a39ea5db36e293de802adea5.zip |
New linda:wake() and linda.status
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | docs/index.html | 14 | ||||
-rw-r--r-- | src/linda.cpp | 109 | ||||
-rw-r--r-- | src/linda.h | 11 | ||||
-rw-r--r-- | src/lindafactory.cpp | 30 |
5 files changed, 143 insertions, 25 deletions
@@ -47,7 +47,9 @@ CHANGE 2: BGe 04-Jul-24 | |||
47 | - linda :receive(), :send(), :get(), :set(), :limit() return nil, error in case of problem. Returned values in case of success change too. | 47 | - linda :receive(), :send(), :get(), :set(), :limit() return nil, error in case of problem. Returned values in case of success change too. |
48 | - linda:limit() can be used to read the value if no new limit is provided. | 48 | - linda:limit() can be used to read the value if no new limit is provided. |
49 | - New __close metamethod that calls any suitable handler that was provided at Linda creation. | 49 | - New __close metamethod that calls any suitable handler that was provided at Linda creation. |
50 | - linda:dump outputs <key>.limit as 'unlimited' instead of -1 for unlimited keys. | 50 | - linda:dump() outputs <key>.limit as 'unlimited' instead of -1 for unlimited keys. |
51 | - linda:wake() can wake up threads waiting for a Linda without doing any I/O on it. | ||
52 | - linda.status reads the cancel status of the Linda. | ||
51 | - Deep userdata are an acceptable key to send data into (for example, another linda). | 53 | - Deep userdata are an acceptable key to send data into (for example, another linda). |
52 | - Full userdata conversion: | 54 | - Full userdata conversion: |
53 | - __lanesconvert added. | 55 | - __lanesconvert added. |
diff --git a/docs/index.html b/docs/index.html index a8eadbf..b95bc0e 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -70,7 +70,7 @@ | |||
70 | </p> | 70 | </p> |
71 | 71 | ||
72 | <p> | 72 | <p> |
73 | This document was revised on 24-Sep-24, and applies to version <tt>4.0.0</tt>. | 73 | This document was revised on 25-Oct-24, and applies to version <tt>4.0.0</tt>. |
74 | </p> | 74 | </p> |
75 | </font> | 75 | </font> |
76 | </center> | 76 | </center> |
@@ -1405,16 +1405,26 @@ | |||
1405 | 1405 | ||
1406 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1406 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1407 | void = linda_h:cancel("read"|"write"|"both"|"none") | 1407 | void = linda_h:cancel("read"|"write"|"both"|"none") |
1408 | "cancelled"|"active" = linda_h.status | ||
1408 | </pre></td></tr></table> | 1409 | </pre></td></tr></table> |
1409 | 1410 | ||
1410 | <p> | 1411 | <p> |
1411 | Signals the linda so that lanes waiting for read, write, or both, wake up. | 1412 | <tt>linda:cancel()</tt> signals the linda so that lanes waiting for read, write, or both, wake up. |
1412 | All linda operations (including <tt>get()</tt> and <tt>set()</tt>) will return <tt>lanes.cancel_error</tt> as when the calling lane is <a href="#cancelling">soft-cancelled</a> as long as the linda is marked as cancelled.<br /> | 1413 | All linda operations (including <tt>get()</tt> and <tt>set()</tt>) will return <tt>lanes.cancel_error</tt> as when the calling lane is <a href="#cancelling">soft-cancelled</a> as long as the linda is marked as cancelled.<br /> |
1413 | <tt>"none"</tt> reset the linda's cancel status, but doesn't signal it.<br /> | 1414 | <tt>"none"</tt> reset the linda's cancel status, but doesn't signal it.<br /> |
1415 | <tt>linda.status</tt> reads the current cancel status. | ||
1414 | If not void, the lane's cancel status overrides the linda's cancel status. | 1416 | If not void, the lane's cancel status overrides the linda's cancel status. |
1415 | </p> | 1417 | </p> |
1416 | 1418 | ||
1417 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1419 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1420 | void = linda_h:wake("read"|"write"|"both") | ||
1421 | </pre></td></tr></table> | ||
1422 | |||
1423 | <p> | ||
1424 | Signals the linda so that lanes waiting for read, write, or both, wake up. Does not change the current Linda's cancel status. Can be used to break early out of a blocking wait. | ||
1425 | </p> | ||
1426 | |||
1427 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | ||
1418 | light userdata = h:deep() | 1428 | light userdata = h:deep() |
1419 | </pre></td></tr></table> | 1429 | </pre></td></tr></table> |
1420 | 1430 | ||
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 | } |