From ac12af5c39b0689edb931fbe9a162db5687d392f Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Fri, 28 Jun 2024 17:52:29 +0200 Subject: Make Lanes crash on purpose at shutdown if some lanes still run --- src/cancel.cpp | 2 ++ src/cancel.h | 1 + src/lanes.lua | 9 --------- src/universe.cpp | 34 +++++++++++++++++++--------------- src/universe.h | 2 +- 5 files changed, 23 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/cancel.cpp b/src/cancel.cpp index 755215f..15a2c83 100644 --- a/src/cancel.cpp +++ b/src/cancel.cpp @@ -95,6 +95,8 @@ CancelOp WhichCancelOp(std::string_view const& opString_) _op = CancelOp::MaskLine; } else if (opString_ == "count") { _op = CancelOp::MaskCount; + } else if (opString_ == "all") { + _op = CancelOp::MaskAll; } return _op; } diff --git a/src/cancel.h b/src/cancel.h index 93fae4d..e62cf0a 100644 --- a/src/cancel.h +++ b/src/cancel.h @@ -29,6 +29,7 @@ enum class CancelOp MaskRet = LUA_MASKRET, MaskLine = LUA_MASKLINE, MaskCount = LUA_MASKCOUNT, + MaskAll = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT }; // xxh64 of string "kCancelError" generated at https://www.pelock.com/products/hash-calculator diff --git a/src/lanes.lua b/src/lanes.lua index 48ebeb6..d28fcf4 100644 --- a/src/lanes.lua +++ b/src/lanes.lua @@ -98,7 +98,6 @@ local default_params = keepers_gc_threshold = -1, nb_user_keepers = 0, on_state_create = nil, - shutdown_mode = "hard", shutdown_timeout = 0.25, strip_functions = true, track_lanes = false, @@ -159,14 +158,6 @@ local param_checkers = end return true end, - shutdown_mode = function(val_) - local valid_hooks = { soft = true, hard = true, call = true, ret = true, line = true, count = true } - -- shutdown_mode should be a known hook mask - if not valid_hooks[val_] then - return nil, "unknown value" - end - return true - end, shutdown_timeout = function(val_) -- shutdown_timeout should be a number in [0,3600] if type(val_) ~= "number" then diff --git a/src/universe.cpp b/src/universe.cpp index 1cb4fd0..5fda29a 100644 --- a/src/universe.cpp +++ b/src/universe.cpp @@ -147,8 +147,7 @@ void Universe::callOnStateCreate(lua_State* const L_, lua_State* const from_, Lo DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); lua_createtable(L_, 0, 1); // L_: settings universe {mt} std::ignore = luaG_getfield(L_, 1, "shutdown_timeout"); // L_: settings universe {mt} shutdown_timeout - std::ignore = luaG_getfield(L_, 1, "shutdown_mode"); // L_: settings universe {mt} shutdown_timeout shutdown_mode - lua_pushcclosure(L_, LG_universe_gc, 2); // L_: settings universe {mt} LG_universe_gc + lua_pushcclosure(L_, LG_universe_gc, 1); // L_: settings universe {mt} LG_universe_gc lua_setfield(L_, -2, "__gc"); // L_: settings universe {mt} lua_setmetatable(L_, -2); // L_: settings universe lua_pop(L_, 1); // L_: settings @@ -352,7 +351,7 @@ lanes::AllocatorDefinition Universe::resolveAllocator(lua_State* const L_, std:: // ################################################################################################# -void Universe::terminateFreeRunningLanes(lua_State* const L_, lua_Duration const shutdownTimeout_, CancelOp const op_) +bool Universe::terminateFreeRunningLanes(lua_Duration const shutdownTimeout_, CancelOp const op_) { if (selfdestructFirst != SELFDESTRUCT_END) { // Signal _all_ still running threads to exit (including the timer thread) @@ -404,15 +403,8 @@ void Universe::terminateFreeRunningLanes(lua_State* const L_, lua_Duration const } } - // If after all this, we still have some free-running lanes, it's an external user error, they should have stopped appropriately - { - std::lock_guard _guard{ selfdestructMutex }; - Lane* _lane{ selfdestructFirst }; - if (_lane != SELFDESTRUCT_END) { - // this causes a leak because we don't call U's destructor (which could be bad if the still running lanes are accessing it) - raise_luaL_error(L_, "Zombie thread '%s' refuses to die!", _lane->debugName.data()); - } - } + // are all lanes successfully terminated? + return selfdestructFirst == SELFDESTRUCT_END; } // ################################################################################################# @@ -421,15 +413,21 @@ void Universe::terminateFreeRunningLanes(lua_State* const L_, lua_Duration const LUAG_FUNC(universe_gc) { lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; - std::string_view const _op_string{ luaG_tostring(L_, lua_upvalueindex(2)) }; STACK_CHECK_START_ABS(L_, 1); Universe* const _U{ luaG_tofulluserdata(L_, 1) }; // L_: U - _U->terminateFreeRunningLanes(L_, _shutdown_timeout, WhichCancelOp(_op_string)); + + // attempt to terminate all lanes with increasingly stronger cancel methods + bool const _allLanesTerminated{ + _U->terminateFreeRunningLanes(_shutdown_timeout, CancelOp::Soft) + || _U->terminateFreeRunningLanes(_shutdown_timeout, CancelOp::Hard) + || _U->terminateFreeRunningLanes(_shutdown_timeout, CancelOp::MaskAll) + }; // invoke the function installed by lanes.finally() kFinalizerRegKey.pushValue(L_); // L_: U finalizer|nil if (!lua_isnil(L_, -1)) { - lua_pcall(L_, 0, 0, 0); // L_: U + lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool + lua_pcall(L_, 1, 0, 0); // L_: U // discard any error that might have occured lua_settop(L_, 1); } else { @@ -438,6 +436,12 @@ LUAG_FUNC(universe_gc) // in case of error, the message is pushed on the stack STACK_CHECK(L_, 1); + // if some lanes are still running here, we have no other choice than crashing and let the client figure out what's wrong + while (_U->selfdestructFirst != SELFDESTRUCT_END) { + throw std::logic_error{ "Some lanes are still running at shutdown" }; + //std::this_thread::yield(); + } + // no need to mutex-protect this as all threads 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) }; diff --git a/src/universe.h b/src/universe.h index 6374648..dc8940f 100644 --- a/src/universe.h +++ b/src/universe.h @@ -139,7 +139,7 @@ class Universe void initializeOnStateCreate(lua_State* const L_); lanes::AllocatorDefinition resolveAllocator(lua_State* const L_, std::string_view const& hint_) const; static inline void Store(lua_State* L_, Universe* U_); - void terminateFreeRunningLanes(lua_State* L_, lua_Duration shutdownTimeout_, CancelOp op_); + [[nodiscard]] bool terminateFreeRunningLanes(lua_Duration shutdownTimeout_, CancelOp op_); }; // ################################################################################################# -- cgit v1.2.3-55-g6feb