aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-12-10 17:07:09 +0100
committerBenoit Germain <benoit.germain@ubisoft.com>2024-12-10 17:07:09 +0100
commit18c708eb8fbe995efe4b06b93421db3d25e22636 (patch)
tree860a74b3a914dfacc9c959374ad4415a7f54873b
parent5f9ef9e1b75adc27a4ae4129cc33137aa7aaa565 (diff)
downloadlanes-18c708eb8fbe995efe4b06b93421db3d25e22636.tar.gz
lanes-18c708eb8fbe995efe4b06b93421db3d25e22636.tar.bz2
lanes-18c708eb8fbe995efe4b06b93421db3d25e22636.zip
lanes.collectgarbage() and linda:collectgarbage()
-rw-r--r--CHANGES2
-rw-r--r--docs/index.html23
-rw-r--r--src/keeper.cpp55
-rw-r--r--src/keeper.hpp5
-rw-r--r--src/lanes.cpp1
-rw-r--r--src/lanes.lua1
-rw-r--r--src/linda.cpp24
7 files changed, 111 insertions, 0 deletions
diff --git a/CHANGES b/CHANGES
index 86da9e8..ca1a42c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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
342int 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 [, ...]]
341int keepercall_count(lua_State* const L_) 351int 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
939void 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
929void Keepers::close() 975void 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
1155LUAG_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]]
100int keepercall_collectgarbage(lua_State* L_);
101[[nodiscard]]
99int keepercall_count(lua_State* L_); 102int keepercall_count(lua_State* L_);
100[[nodiscard]] 103[[nodiscard]]
101int keepercall_destruct(lua_State* L_); 104int keepercall_destruct(lua_State* L_);
@@ -116,3 +119,5 @@ int keepercall_set(lua_State* L_);
116 119
117[[nodiscard]] 120[[nodiscard]]
118KeeperCallResult keeper_call(KeeperState K_, keeper_api_t func_, lua_State* L_, Linda* linda_, StackIndex starting_index_); 121KeeperCallResult keeper_call(KeeperState K_, keeper_api_t func_, lua_State* L_, Linda* linda_, StackIndex starting_index_);
122
123LUAG_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);
649namespace { 649namespace {
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 */
457LUAG_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 },