From 5f9ef9e1b75adc27a4ae4129cc33137aa7aaa565 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Mon, 9 Dec 2024 17:39:30 +0100 Subject: Improved DeepPrelude architecture --- src/deep.cpp | 66 +++++++++++++++++++++++++----------------------- src/deep.hpp | 11 +++++++- src/lanes.cpp | 1 + src/linda.cpp | 34 +++++++++++++++++++++++++ src/linda.hpp | 6 +++++ src/macros_and_utils.hpp | 13 ++++++++++ src/universe.cpp | 25 ++++-------------- src/universe.hpp | 11 ++++---- 8 files changed, 108 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/src/deep.cpp b/src/deep.cpp index 6f4da9f..7b287f5 100644 --- a/src/deep.cpp +++ b/src/deep.cpp @@ -62,37 +62,6 @@ namespace { // ############################################################################################# // ############################################################################################# - /* - * void= mt.__gc( proxy_ud ) - * - * End of life for a proxy object; reduce the deep reference count and clean it up if reaches 0. - * - */ - [[nodiscard]] - static int DeepGC(lua_State* const L_) - { - DeepPrelude* const* const _proxy{ luaG_tofulluserdata(L_, StackIndex{ 1 }) }; - DeepPrelude* const _p{ *_proxy }; - - // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded - // in that case, we are not multithreaded and locking isn't necessary anyway - bool const _isLastRef{ _p->refcount.fetch_sub(1, std::memory_order_relaxed) == 1 }; - - if (_isLastRef) { - // retrieve wrapped __gc, if any - lua_pushvalue(L_, lua_upvalueindex(1)); // L_: self __gc? - if (!lua_isnil(L_, -1)) { - lua_insert(L_, -2); // L_: __gc self - lua_call(L_, 1, 0); // L_: - } else { - // need an empty stack in case we are GC_ing from a Keeper, so that empty stack checks aren't triggered - lua_pop(L_, 2); // L_: - } - DeepFactory::DeleteDeepObject(L_, _p); - } - return 0; - } - // ############################################################################################# // Pops the key (metatable or factory) off the stack, and replaces with the deep lookup value (factory/metatable/nil). @@ -121,6 +90,39 @@ namespace { // ################################################################################################# // ################################################################################################# +/* + * void= mt.__gc( proxy_ud ) + * + * End of life for a proxy object; reduce the deep reference count and clean it up if reaches 0. + * + */ +[[nodiscard]] +int DeepFactory::DeepGC(lua_State* const L_) +{ + DeepPrelude* const* const _proxy{ luaG_tofulluserdata(L_, StackIndex{ 1 }) }; + DeepPrelude* const _p{ *_proxy }; + + // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded + // in that case, we are not multithreaded and locking isn't necessary anyway + bool const _isLastRef{ _p->refcount.fetch_sub(1, std::memory_order_relaxed) == 1 }; + + if (_isLastRef) { + // retrieve wrapped __gc, if any + lua_pushvalue(L_, lua_upvalueindex(1)); // L_: self __gc? + if (!lua_isnil(L_, -1)) { + lua_insert(L_, -2); // L_: __gc self + lua_call(L_, 1, 0); // L_: + } else { + // need an empty stack in case we are GC_ing from a Keeper, so that empty stack checks aren't triggered + lua_pop(L_, 2); // L_: + } + DeepFactory::DeleteDeepObject(L_, _p); + } + return 0; +} + +// ################################################################################################# + // NEVER call deleteDeepObjectInternal by itself, ALWAYS go through DeleteDeepObject() void DeepFactory::DeleteDeepObject(lua_State* const L_, DeepPrelude* const o_) { @@ -381,7 +383,7 @@ DeepPrelude* DeepFactory::toDeep(lua_State* const L_, StackIndex const index_) c // ################################################################################################# -void DeepPrelude::push(lua_State* L_) const +void DeepPrelude::push(lua_State* const L_) const { STACK_CHECK_START_REL(L_, 0); kDeepProxyCacheRegKey.getSubTableMode(L_, "v"); // L_: DPC diff --git a/src/deep.hpp b/src/deep.hpp index 6fa12b1..e4bdaca 100644 --- a/src/deep.hpp +++ b/src/deep.hpp @@ -20,19 +20,26 @@ static constexpr UniqueKey kDeepVersion{ 0x91171AEC6641E9DBull, "kDeepVersion" } // should be used as header for deep userdata // a deep userdata is a full userdata that stores a single pointer to the actual DeepPrelude-derived object -struct DeepPrelude +class DeepPrelude { + friend class DeepFactory; + + private: UniqueKey const magic{ kDeepVersion }; // when stored in a keeper state, the full userdata doesn't have a metatable, so we need direct access to the factory DeepFactory& factory; + + protected: // data is destroyed when refcount is 0 std::atomic refcount{ 0 }; + protected: DeepPrelude(DeepFactory& factory_) : factory{ factory_ } { } + public: void push(lua_State* L_) const; }; @@ -63,6 +70,8 @@ class DeepFactory virtual std::string_view moduleName() const = 0; private: + [[nodiscard]] + static int DeepGC(lua_State* L_); void storeDeepLookup(lua_State* L_) const; public: diff --git a/src/lanes.cpp b/src/lanes.cpp index 0b29a8f..83d01fb 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp @@ -86,6 +86,7 @@ THE SOFTWARE. #include "intercopycontext.hpp" #include "keeper.hpp" #include "lane.hpp" +#include "linda.hpp" #include "nameof.hpp" #include "state.hpp" #include "threading.hpp" diff --git a/src/linda.cpp b/src/linda.cpp index 6ebbc1f..7af387f 100644 --- a/src/linda.cpp +++ b/src/linda.cpp @@ -130,6 +130,8 @@ namespace { // ################################################################################################# // ################################################################################################# +LUAG_FUNC(linda); + // ################################################################################################# // ################################################################################################# // #################################### Linda implementation ####################################### @@ -165,6 +167,38 @@ Keeper* Linda::acquireKeeper() const // ################################################################################################# +Linda* Linda::CreateTimerLinda(lua_State* const L_) +{ + STACK_CHECK_START_REL(L_, 0); // L_: + // Initialize 'timerLinda'; a common Linda object shared by all states + lua_pushcfunction(L_, LG_linda); // L_: lanes.linda + luaG_pushstring(L_, "lanes-timer"); // L_: lanes.linda "lanes-timer" + lua_pushinteger(L_, 0); // L_: lanes.linda "lanes-timer" 0 + lua_call(L_, 2, 1); // L_: linda + STACK_CHECK(L_, 1); + + // Proxy userdata contents is only a 'DeepPrelude*' pointer + auto const _timerLinda{ *luaG_tofulluserdata(L_, kIdxTop) }; + // increment refcount so that this linda remains alive as long as the universe exists. + _timerLinda->refcount.fetch_add(1, std::memory_order_relaxed); + lua_pop(L_, 1); // L_: + STACK_CHECK(L_, 0); + return _timerLinda; +} + +// ################################################################################################# + +void Linda::DeleteTimerLinda(lua_State* const L_, Linda* const linda_) +{ + if (linda_ != nullptr) { // test in case some early internal error prevented Lanes from creating the deep timer + [[maybe_unused]] auto const _prev_ref_count{ linda_->refcount.fetch_sub(1, std::memory_order_relaxed) }; + LUA_ASSERT(L_, _prev_ref_count == 1); // this should be the last reference + DeepFactory::DeleteDeepObject(L_, linda_); + } +} + +// ################################################################################################# + void Linda::freeAllocatedName() { if (std::holds_alternative(nameVariant)) { diff --git a/src/linda.hpp b/src/linda.hpp index aa63316..920db1b 100644 --- a/src/linda.hpp +++ b/src/linda.hpp @@ -82,6 +82,9 @@ class Linda Linda& operator=(Linda const&&) = delete; private: + [[nodiscard]] + static Linda* CreateTimerLinda(lua_State* L_); + static void DeleteTimerLinda(lua_State* L_, Linda* linda_); void freeAllocatedName(); void setName(std::string_view const& name_); @@ -89,6 +92,9 @@ class Linda [[nodiscard]] Keeper* acquireKeeper() const; [[nodiscard]] + static Linda* CreateTimerLinda(lua_State* const L_, Passkey) { return CreateTimerLinda(L_); } + static void DeleteTimerLinda(lua_State* const L_, Linda* const linda_, Passkey) { DeleteTimerLinda(L_, linda_); } + [[nodiscard]] std::string_view getName() const; [[nodiscard]] bool inKeeperOperation() const { return keeperOperationCount.load(std::memory_order_seq_cst) != 0; } diff --git a/src/macros_and_utils.hpp b/src/macros_and_utils.hpp index 6850ddf..0897367 100644 --- a/src/macros_and_utils.hpp +++ b/src/macros_and_utils.hpp @@ -47,3 +47,16 @@ typename T::value_type const& OptionalValue(T const& x_, Ts... args_) } return x_.value(); } + +// ################################################################################################# + +struct PasskeyToken {}; +constexpr PasskeyToken PK{}; +template +class Passkey +{ + private: + friend T; + Passkey(PasskeyToken) {} + // rule of 5 ignored out of laziness here +}; diff --git a/src/universe.cpp b/src/universe.cpp index ec7d043..b7d11d8 100644 --- a/src/universe.cpp +++ b/src/universe.cpp @@ -35,6 +35,7 @@ THE SOFTWARE. #include "intercopycontext.hpp" #include "keeper.hpp" #include "lane.hpp" +#include "linda.hpp" #include "state.hpp" extern LUAG_FUNC(linda); @@ -147,7 +148,7 @@ Universe* Universe::Create(lua_State* const L_) DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); lua_createtable(L_, 0, 1); // L_: settings universe {mt} std::ignore = luaG_getfield(L_, kIdxSettings, "shutdown_timeout"); // L_: settings universe {mt} shutdown_timeout - lua_pushcclosure(L_, LG_universe_gc, 1); // L_: settings universe {mt} LG_universe_gc + lua_pushcclosure(L_, UniverseGC, 1); // L_: settings universe {mt} UniverseGC lua_setfield(L_, -2, "__gc"); // L_: settings universe {mt} lua_setmetatable(L_, -2); // L_: settings universe lua_pop(L_, 1); // L_: settings @@ -175,18 +176,7 @@ Universe* Universe::Create(lua_State* const L_) STACK_CHECK(L_, 0); // Initialize 'timerLinda'; a common Linda object shared by all states - lua_pushcfunction(L_, LG_linda); // L_: settings lanes.linda - luaG_pushstring(L_, "lanes-timer"); // L_: settings lanes.linda "lanes-timer" - lua_pushinteger(L_, 0); // L_: settings lanes.linda "lanes-timer" 0 - lua_call(L_, 2, 1); // L_: settings linda - STACK_CHECK(L_, 1); - - // Proxy userdata contents is only a 'DeepPrelude*' pointer - _U->timerLinda = *luaG_tofulluserdata(L_, kIdxTop); - // increment refcount so that this linda remains alive as long as the universe exists. - _U->timerLinda->refcount.fetch_add(1, std::memory_order_relaxed); - lua_pop(L_, 1); // L_: settings - STACK_CHECK(L_, 0); + _U->timerLinda = Linda::CreateTimerLinda(L_, PK); return _U; } @@ -409,7 +399,7 @@ bool Universe::terminateFreeRunningLanes(lua_Duration const shutdownTimeout_, Ca // ################################################################################################# // process end: cancel any still free-running threads -LUAG_FUNC(universe_gc) +int Universe::UniverseGC(lua_State* const L_) { lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; STACK_CHECK_START_ABS(L_, 1); @@ -444,12 +434,7 @@ LUAG_FUNC(universe_gc) } // no need to mutex-protect this as all lanes in the universe are gone at that point - if (_U->timerLinda != nullptr) { // test in case some early internal error prevented Lanes from creating the deep timer - [[maybe_unused]] int const _prev_ref_count{ _U->timerLinda->refcount.fetch_sub(1, std::memory_order_relaxed) }; - LUA_ASSERT(L_, _prev_ref_count == 1); // this should be the last reference - DeepFactory::DeleteDeepObject(L_, _U->timerLinda); - _U->timerLinda = nullptr; - } + Linda::DeleteTimerLinda(L_, std::exchange(_U->timerLinda, nullptr), PK); _U->keepers.close(); diff --git a/src/universe.hpp b/src/universe.hpp index 639dcdb..dbf0ece 100644 --- a/src/universe.hpp +++ b/src/universe.hpp @@ -10,8 +10,8 @@ // ################################################################################################# // forwards -struct DeepPrelude; class Lane; +class Linda; // ################################################################################################# @@ -99,7 +99,7 @@ class Universe // Initialized by 'init_once_LOCKED()': the deep userdata Linda object // used for timers (each lane will get a proxy to this) - DeepPrelude* timerLinda{ nullptr }; + Linda* timerLinda{ nullptr }; LaneTracker tracker; @@ -122,6 +122,9 @@ class Universe // The terminal desinit sequence should wait for all such processing to terminate before force-killing threads std::atomic selfdestructingCount{ 0 }; + private: + static int UniverseGC(lua_State* L_); + public: [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, lua_State* L_) noexcept { return luaG_newuserdatauv(L_, UserValueCount{ 0 }); }; @@ -171,7 +174,3 @@ inline void Universe::Store(lua_State* L_, Universe* U_) kUniverseLightRegKey.setValue(L_, [U = U_](lua_State* L_) { U ? lua_pushlightuserdata(L_, U) : lua_pushnil(L_); }); STACK_CHECK(L_, 0); } - -// ################################################################################################# - -LUAG_FUNC(universe_gc); -- cgit v1.2.3-55-g6feb