diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-12-10 17:07:09 +0100 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-12-10 17:07:09 +0100 |
commit | 18c708eb8fbe995efe4b06b93421db3d25e22636 (patch) | |
tree | 860a74b3a914dfacc9c959374ad4415a7f54873b | |
parent | 5f9ef9e1b75adc27a4ae4129cc33137aa7aaa565 (diff) | |
download | lanes-18c708eb8fbe995efe4b06b93421db3d25e22636.tar.gz lanes-18c708eb8fbe995efe4b06b93421db3d25e22636.tar.bz2 lanes-18c708eb8fbe995efe4b06b93421db3d25e22636.zip |
lanes.collectgarbage() and linda:collectgarbage()
-rw-r--r-- | CHANGES | 2 | ||||
-rw-r--r-- | docs/index.html | 23 | ||||
-rw-r--r-- | src/keeper.cpp | 55 | ||||
-rw-r--r-- | src/keeper.hpp | 5 | ||||
-rw-r--r-- | src/lanes.cpp | 1 | ||||
-rw-r--r-- | src/lanes.lua | 1 | ||||
-rw-r--r-- | src/linda.cpp | 24 |
7 files changed, 111 insertions, 0 deletions
@@ -13,6 +13,7 @@ CHANGE 2: BGe 27-Nov-24 | |||
13 | - lanes.sleep() accepts a new argument "indefinitely" to block forever (until hard cancellation is received). | 13 | - lanes.sleep() accepts a new argument "indefinitely" to block forever (until hard cancellation is received). |
14 | - function set_debug_threadname() available inside a Lane is renamed lane_threadname(); can now both read and write the name. | 14 | - function set_debug_threadname() available inside a Lane is renamed lane_threadname(); can now both read and write the name. |
15 | - new function lanes.finally(). Installs a function that gets called at Lanes shutdown after attempting to terminate all lanes. | 15 | - new function lanes.finally(). Installs a function that gets called at Lanes shutdown after attempting to terminate all lanes. |
16 | - new function lanes.collectgarbage(), to force a full GC cycle in the keeper states. | ||
16 | If some lanes still run after the finalizer, Lanes with throw an exception or freeze, depending on its return value. | 17 | If some lanes still run after the finalizer, Lanes with throw an exception or freeze, depending on its return value. |
17 | - Configuration settings: | 18 | - Configuration settings: |
18 | - Boolean parameters only accept boolean values. | 19 | - Boolean parameters only accept boolean values. |
@@ -50,6 +51,7 @@ CHANGE 2: BGe 27-Nov-24 | |||
50 | - linda:dump() outputs <key>.limit as 'unlimited' instead of -1 for unlimited keys. | 51 | - 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: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. | 53 | - linda.status reads the cancel status of the Linda. |
54 | - new function linda:collectgarbage() to force collection of all stale data in the associated keeper. | ||
53 | - Deep userdata are an acceptable key to send data into (for example, another linda). | 55 | - Deep userdata are an acceptable key to send data into (for example, another linda). |
54 | - Full userdata conversion: | 56 | - Full userdata conversion: |
55 | - __lanesconvert added. | 57 | - __lanesconvert added. |
diff --git a/docs/index.html b/docs/index.html index 6757981..8bb181f 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -453,6 +453,20 @@ | |||
453 | </tr> | 453 | </tr> |
454 | </table> | 454 | </table> |
455 | 455 | ||
456 | <p id="collectgarbage"> | ||
457 | A full GC cycle can be triggered on all keeper states with <tt>lanes.collectgarbage()</tt>. This can force the collection of stale storage data for a collected Linda. | ||
458 | </p> | ||
459 | |||
460 | <p> | ||
461 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> | ||
462 | <tr> | ||
463 | <td> | ||
464 | <pre> lanes.collectgarbage()</pre> | ||
465 | </td> | ||
466 | </tr> | ||
467 | </table> | ||
468 | </p> | ||
469 | |||
456 | <p id="finally"> | 470 | <p id="finally"> |
457 | It is also possible to install a function that will be called when Lanes is shutdown (that is, when the first state that required Lanes is closed). | 471 | It is also possible to install a function that will be called when Lanes is shutdown (that is, when the first state that required Lanes is closed). |
458 | </p> | 472 | </p> |
@@ -1395,6 +1409,15 @@ | |||
1395 | </p> | 1409 | </p> |
1396 | 1410 | ||
1397 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1411 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1412 | linda_h:collectgarbage() | ||
1413 | </pre></td></tr></table> | ||
1414 | |||
1415 | <p> | ||
1416 | Forces a full garbage collect cycle inside the Keeper state assigned to the Linda (same as <tt>lua_gc(L, LUA_GCCOLLECT)</tt>).<br /> | ||
1417 | Can be useful to clean stale storage after some keys are cleaned. | ||
1418 | </p> | ||
1419 | |||
1420 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | ||
1398 | [val] = linda_h:count([slot[,...]]) | 1421 | [val] = linda_h:count([slot[,...]]) |
1399 | </pre></td></tr></table> | 1422 | </pre></td></tr></table> |
1400 | 1423 | ||
diff --git a/src/keeper.cpp b/src/keeper.cpp index e7b02e6..2d9d800 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp | |||
@@ -337,6 +337,16 @@ static void PushKeysDB(KeeperState const K_, StackIndex const idx_) | |||
337 | // ################################################################################################# | 337 | // ################################################################################################# |
338 | // ################################################################################################# | 338 | // ################################################################################################# |
339 | 339 | ||
340 | // in: linda | ||
341 | // out: nothing | ||
342 | int keepercall_collectgarbage(lua_State* const L_) | ||
343 | { | ||
344 | lua_gc(L_, LUA_GCCOLLECT, 0); | ||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | // ################################################################################################# | ||
349 | |||
340 | // in: linda [, key [, ...]] | 350 | // in: linda [, key [, ...]] |
341 | int keepercall_count(lua_State* const L_) | 351 | int keepercall_count(lua_State* const L_) |
342 | { | 352 | { |
@@ -926,6 +936,42 @@ void Keepers::DeleteKV::operator()(Keeper* const k_) const | |||
926 | 936 | ||
927 | // ################################################################################################# | 937 | // ################################################################################################# |
928 | 938 | ||
939 | void Keepers::collectGarbage() | ||
940 | { | ||
941 | if (isClosing.test(std::memory_order_acquire)) { | ||
942 | assert(false); // should never close more than once in practice | ||
943 | return; | ||
944 | } | ||
945 | |||
946 | if (std::holds_alternative<std::monostate>(keeper_array)) { | ||
947 | return; | ||
948 | } | ||
949 | |||
950 | auto _gcOneKeeper = [](Keeper& keeper_) { | ||
951 | std::lock_guard<std::mutex> _guard(keeper_.mutex); | ||
952 | if (keeper_.K) { | ||
953 | lua_gc(keeper_.K, LUA_GCCOLLECT, 0); | ||
954 | } | ||
955 | }; | ||
956 | |||
957 | if (std::holds_alternative<Keeper>(keeper_array)) { | ||
958 | _gcOneKeeper(std::get<Keeper>(keeper_array)); | ||
959 | } else { | ||
960 | KV& _kv = std::get<KV>(keeper_array); | ||
961 | |||
962 | // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it | ||
963 | // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists | ||
964 | // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success | ||
965 | // which is early-outed with a keepers->nbKeepers null-check | ||
966 | for (size_t const _i : std::ranges::iota_view{ size_t{ 0 }, _kv.nbKeepers }) { | ||
967 | _gcOneKeeper(_kv.keepers[_i]); | ||
968 | } | ||
969 | } | ||
970 | } | ||
971 | |||
972 | // ################################################################################################# | ||
973 | |||
974 | |||
929 | void Keepers::close() | 975 | void Keepers::close() |
930 | { | 976 | { |
931 | if (isClosing.test_and_set(std::memory_order_release)) { | 977 | if (isClosing.test_and_set(std::memory_order_release)) { |
@@ -1103,3 +1149,12 @@ void Keepers::initialize(Universe& U_, lua_State* L_, size_t const nbKeepers_, i | |||
1103 | } | 1149 | } |
1104 | } | 1150 | } |
1105 | } | 1151 | } |
1152 | |||
1153 | // ################################################################################################# | ||
1154 | |||
1155 | LUAG_FUNC(collectgarbage) | ||
1156 | { | ||
1157 | Universe* const _U{ Universe::Get(L_) }; | ||
1158 | _U->keepers.collectGarbage(); | ||
1159 | return 0; | ||
1160 | } | ||
diff --git a/src/keeper.hpp b/src/keeper.hpp index e2ad445..c0a8dc5 100644 --- a/src/keeper.hpp +++ b/src/keeper.hpp | |||
@@ -73,6 +73,7 @@ struct Keepers | |||
73 | static void* operator new(size_t size_) = delete; | 73 | static void* operator new(size_t size_) = delete; |
74 | 74 | ||
75 | Keepers() = default; | 75 | Keepers() = default; |
76 | void collectGarbage(); | ||
76 | void close(); | 77 | void close(); |
77 | [[nodiscard]] | 78 | [[nodiscard]] |
78 | Keeper* getKeeper(KeeperIndex idx_); | 79 | Keeper* getKeeper(KeeperIndex idx_); |
@@ -96,6 +97,8 @@ using keeper_api_t = lua_CFunction; | |||
96 | 97 | ||
97 | // lua_Cfunctions to run inside a keeper state | 98 | // lua_Cfunctions to run inside a keeper state |
98 | [[nodiscard]] | 99 | [[nodiscard]] |
100 | int keepercall_collectgarbage(lua_State* L_); | ||
101 | [[nodiscard]] | ||
99 | int keepercall_count(lua_State* L_); | 102 | int keepercall_count(lua_State* L_); |
100 | [[nodiscard]] | 103 | [[nodiscard]] |
101 | int keepercall_destruct(lua_State* L_); | 104 | int keepercall_destruct(lua_State* L_); |
@@ -116,3 +119,5 @@ int keepercall_set(lua_State* L_); | |||
116 | 119 | ||
117 | [[nodiscard]] | 120 | [[nodiscard]] |
118 | KeeperCallResult keeper_call(KeeperState K_, keeper_api_t func_, lua_State* L_, Linda* linda_, StackIndex starting_index_); | 121 | KeeperCallResult keeper_call(KeeperState K_, keeper_api_t func_, lua_State* L_, Linda* linda_, StackIndex starting_index_); |
122 | |||
123 | LUAG_FUNC(collectgarbage); | ||
diff --git a/src/lanes.cpp b/src/lanes.cpp index 83d01fb..f32e7af 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp | |||
@@ -649,6 +649,7 @@ extern LUAG_FUNC(linda); | |||
649 | namespace { | 649 | namespace { |
650 | namespace local { | 650 | namespace local { |
651 | static struct luaL_Reg const sLanesFunctions[] = { | 651 | static struct luaL_Reg const sLanesFunctions[] = { |
652 | { "collectgarbage", LG_collectgarbage }, | ||
652 | { Universe::kFinally, Universe::InitializeFinalizer }, | 653 | { Universe::kFinally, Universe::InitializeFinalizer }, |
653 | { "linda", LG_linda }, | 654 | { "linda", LG_linda }, |
654 | { "nameof", LG_nameof }, | 655 | { "nameof", LG_nameof }, |
diff --git a/src/lanes.lua b/src/lanes.lua index 8d8f25d..4df1f64 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -851,6 +851,7 @@ local configure = function(settings_) | |||
851 | 851 | ||
852 | -- activate full interface | 852 | -- activate full interface |
853 | lanes.cancel_error = core.cancel_error | 853 | lanes.cancel_error = core.cancel_error |
854 | lanes.collectgarbage = core.collectgarbage | ||
854 | lanes.finally = core.finally | 855 | lanes.finally = core.finally |
855 | lanes.linda = core.linda | 856 | lanes.linda = core.linda |
856 | lanes.nameof = core.nameof | 857 | lanes.nameof = core.nameof |
diff --git a/src/linda.cpp b/src/linda.cpp index 7af387f..4f33899 100644 --- a/src/linda.cpp +++ b/src/linda.cpp | |||
@@ -450,6 +450,29 @@ static LUAG_FUNC(linda_index) | |||
450 | // ################################################################################################# | 450 | // ################################################################################################# |
451 | 451 | ||
452 | /* | 452 | /* |
453 | * (void) = linda_collectgarbage( linda_ud) | ||
454 | * | ||
455 | * Force a GC cycle in the keeper assigned to the Linda | ||
456 | */ | ||
457 | LUAG_FUNC(linda_collectgarbage) | ||
458 | { | ||
459 | static constexpr lua_CFunction _collectgarbage{ | ||
460 | +[](lua_State* const L_) { | ||
461 | Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; | ||
462 | if (lua_gettop(L_) > 1) { | ||
463 | raise_luaL_argerror(L_, StackIndex{ 2 }, "Unexpected extra argument"); | ||
464 | } | ||
465 | Keeper* const _keeper{ _linda->whichKeeper() }; | ||
466 | KeeperCallResult const _pushed{ keeper_call(_keeper->K, KEEPER_API(collectgarbage), L_, _linda, StackIndex{ 0 }) }; | ||
467 | return OptionalValue(_pushed, L_, "Unexpected error"); | ||
468 | } | ||
469 | }; | ||
470 | return Linda::ProtectedCall(L_, _collectgarbage); | ||
471 | } | ||
472 | |||
473 | // ################################################################################################# | ||
474 | |||
475 | /* | ||
453 | * [val] = linda_count( linda_ud, [slot [, ...]]) | 476 | * [val] = linda_count( linda_ud, [slot [, ...]]) |
454 | * | 477 | * |
455 | * Get a count of the pending elements in the specified keys | 478 | * Get a count of the pending elements in the specified keys |
@@ -1075,6 +1098,7 @@ namespace { | |||
1075 | { "__towatch", LG_linda_towatch }, // Decoda __towatch support | 1098 | { "__towatch", LG_linda_towatch }, // Decoda __towatch support |
1076 | #endif // HAVE_DECODA_SUPPORT() | 1099 | #endif // HAVE_DECODA_SUPPORT() |
1077 | { "cancel", LG_linda_cancel }, | 1100 | { "cancel", LG_linda_cancel }, |
1101 | { "collectgarbage", LG_linda_collectgarbage }, | ||
1078 | { "count", LG_linda_count }, | 1102 | { "count", LG_linda_count }, |
1079 | { "deep", LG_linda_deep }, | 1103 | { "deep", LG_linda_deep }, |
1080 | { "dump", LG_linda_dump }, | 1104 | { "dump", LG_linda_dump }, |