From 18c708eb8fbe995efe4b06b93421db3d25e22636 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Tue, 10 Dec 2024 17:07:09 +0100 Subject: lanes.collectgarbage() and linda:collectgarbage() --- src/keeper.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/keeper.hpp | 5 +++++ src/lanes.cpp | 1 + src/lanes.lua | 1 + src/linda.cpp | 24 ++++++++++++++++++++++++ 5 files changed, 86 insertions(+) (limited to 'src') 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_) // ################################################################################################# // ################################################################################################# +// in: linda +// out: nothing +int keepercall_collectgarbage(lua_State* const L_) +{ + lua_gc(L_, LUA_GCCOLLECT, 0); + return 0; +} + +// ################################################################################################# + // in: linda [, key [, ...]] int keepercall_count(lua_State* const L_) { @@ -926,6 +936,42 @@ void Keepers::DeleteKV::operator()(Keeper* const k_) const // ################################################################################################# +void Keepers::collectGarbage() +{ + if (isClosing.test(std::memory_order_acquire)) { + assert(false); // should never close more than once in practice + return; + } + + if (std::holds_alternative(keeper_array)) { + return; + } + + auto _gcOneKeeper = [](Keeper& keeper_) { + std::lock_guard _guard(keeper_.mutex); + if (keeper_.K) { + lua_gc(keeper_.K, LUA_GCCOLLECT, 0); + } + }; + + if (std::holds_alternative(keeper_array)) { + _gcOneKeeper(std::get(keeper_array)); + } else { + KV& _kv = std::get(keeper_array); + + // 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 + // 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 + // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success + // which is early-outed with a keepers->nbKeepers null-check + for (size_t const _i : std::ranges::iota_view{ size_t{ 0 }, _kv.nbKeepers }) { + _gcOneKeeper(_kv.keepers[_i]); + } + } +} + +// ################################################################################################# + + void Keepers::close() { 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 } } } + +// ################################################################################################# + +LUAG_FUNC(collectgarbage) +{ + Universe* const _U{ Universe::Get(L_) }; + _U->keepers.collectGarbage(); + return 0; +} 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 static void* operator new(size_t size_) = delete; Keepers() = default; + void collectGarbage(); void close(); [[nodiscard]] Keeper* getKeeper(KeeperIndex idx_); @@ -96,6 +97,8 @@ using keeper_api_t = lua_CFunction; // lua_Cfunctions to run inside a keeper state [[nodiscard]] +int keepercall_collectgarbage(lua_State* L_); +[[nodiscard]] int keepercall_count(lua_State* L_); [[nodiscard]] int keepercall_destruct(lua_State* L_); @@ -116,3 +119,5 @@ int keepercall_set(lua_State* L_); [[nodiscard]] KeeperCallResult keeper_call(KeeperState K_, keeper_api_t func_, lua_State* L_, Linda* linda_, StackIndex starting_index_); + +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); namespace { namespace local { static struct luaL_Reg const sLanesFunctions[] = { + { "collectgarbage", LG_collectgarbage }, { Universe::kFinally, Universe::InitializeFinalizer }, { "linda", LG_linda }, { "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_) -- activate full interface lanes.cancel_error = core.cancel_error + lanes.collectgarbage = core.collectgarbage lanes.finally = core.finally lanes.linda = core.linda 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 @@ -449,6 +449,29 @@ static LUAG_FUNC(linda_index) // ################################################################################################# +/* + * (void) = linda_collectgarbage( linda_ud) + * + * Force a GC cycle in the keeper assigned to the Linda + */ +LUAG_FUNC(linda_collectgarbage) +{ + static constexpr lua_CFunction _collectgarbage{ + +[](lua_State* const L_) { + Linda* const _linda{ ToLinda(L_, StackIndex{ 1 }) }; + if (lua_gettop(L_) > 1) { + raise_luaL_argerror(L_, StackIndex{ 2 }, "Unexpected extra argument"); + } + Keeper* const _keeper{ _linda->whichKeeper() }; + KeeperCallResult const _pushed{ keeper_call(_keeper->K, KEEPER_API(collectgarbage), L_, _linda, StackIndex{ 0 }) }; + return OptionalValue(_pushed, L_, "Unexpected error"); + } + }; + return Linda::ProtectedCall(L_, _collectgarbage); +} + +// ################################################################################################# + /* * [val] = linda_count( linda_ud, [slot [, ...]]) * @@ -1075,6 +1098,7 @@ namespace { { "__towatch", LG_linda_towatch }, // Decoda __towatch support #endif // HAVE_DECODA_SUPPORT() { "cancel", LG_linda_cancel }, + { "collectgarbage", LG_linda_collectgarbage }, { "count", LG_linda_count }, { "deep", LG_linda_deep }, { "dump", LG_linda_dump }, -- cgit v1.2.3-55-g6feb