diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2025-03-17 12:34:08 +0100 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2025-03-17 12:34:08 +0100 |
commit | a57690123ae3ce5bdd7e970690f1380e88e4eaf6 (patch) | |
tree | d526e8f545cef2b1c23978cb9ee5c94dbc9cda2c /src | |
parent | d93de7ca51edea911eeecb7c8edcffe77298ed07 (diff) | |
download | lanes-a57690123ae3ce5bdd7e970690f1380e88e4eaf6.tar.gz lanes-a57690123ae3ce5bdd7e970690f1380e88e4eaf6.tar.bz2 lanes-a57690123ae3ce5bdd7e970690f1380e88e4eaf6.zip |
Raise a regular Lua error instead of throwing a C++ std::logic_error exception in Universe::UniverseGC
Diffstat (limited to 'src')
-rw-r--r-- | src/lane.cpp | 6 | ||||
-rw-r--r-- | src/lane.hpp | 4 | ||||
-rw-r--r-- | src/universe.cpp | 46 | ||||
-rw-r--r-- | src/universe.hpp | 3 |
4 files changed, 48 insertions, 11 deletions
diff --git a/src/lane.cpp b/src/lane.cpp index 7a5c257..5cebdfa 100644 --- a/src/lane.cpp +++ b/src/lane.cpp | |||
@@ -756,6 +756,12 @@ static void lane_main(Lane* const lane_) | |||
756 | } | 756 | } |
757 | } | 757 | } |
758 | 758 | ||
759 | if (lane_->flaggedAfterUniverseGC.load(std::memory_order_relaxed)) { | ||
760 | // let's try not to crash if the lane didn't terminate gracefully and the Universe met its end | ||
761 | // there will be leaks, but what else can we do? | ||
762 | return; | ||
763 | } | ||
764 | |||
759 | if (_errorHandlerCount) { | 765 | if (_errorHandlerCount) { |
760 | lua_remove(_L, 1); // L: retvals|error | 766 | lua_remove(_L, 1); // L: retvals|error |
761 | } | 767 | } |
diff --git a/src/lane.hpp b/src/lane.hpp index 9b678d6..5fe36b6 100644 --- a/src/lane.hpp +++ b/src/lane.hpp | |||
@@ -139,6 +139,10 @@ class Lane final | |||
139 | 139 | ||
140 | ErrorTraceLevel const errorTraceLevel{ Basic }; | 140 | ErrorTraceLevel const errorTraceLevel{ Basic }; |
141 | 141 | ||
142 | // when Universe is collected, and an uncooperative Lane refuses to terminate, this flag becomes true | ||
143 | // in case of crash, that's the Lane's fault! | ||
144 | std::atomic_bool flaggedAfterUniverseGC{ false }; | ||
145 | |||
142 | [[nodiscard]] | 146 | [[nodiscard]] |
143 | static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internalAllocator.alloc(size_); } | 147 | static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internalAllocator.alloc(size_); } |
144 | // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception | 148 | // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception |
diff --git a/src/universe.cpp b/src/universe.cpp index 3da0801..dd7bc4b 100644 --- a/src/universe.cpp +++ b/src/universe.cpp | |||
@@ -209,6 +209,17 @@ static int luaG_provide_protected_allocator(lua_State* const L_) | |||
209 | 209 | ||
210 | // ################################################################################################# | 210 | // ################################################################################################# |
211 | 211 | ||
212 | void Universe::flagDanglingLanes() const | ||
213 | { | ||
214 | std::lock_guard<std::mutex> _guard{ selfdestructMutex }; | ||
215 | Lane* _lane{ selfdestructFirst }; | ||
216 | while (_lane != SELFDESTRUCT_END) { | ||
217 | _lane->flaggedAfterUniverseGC.store(true, std::memory_order_relaxed); | ||
218 | _lane = _lane->selfdestruct_next; | ||
219 | } | ||
220 | } | ||
221 | // ################################################################################################# | ||
222 | |||
212 | // called once at the creation of the universe (therefore L_ is the master Lua state everything originates from) | 223 | // called once at the creation of the universe (therefore L_ is the master Lua state everything originates from) |
213 | // Do I need to disable this when compiling for LuaJIT to prevent issues? | 224 | // Do I need to disable this when compiling for LuaJIT to prevent issues? |
214 | void Universe::initializeAllocatorFunction(lua_State* const L_) | 225 | void Universe::initializeAllocatorFunction(lua_State* const L_) |
@@ -399,6 +410,7 @@ bool Universe::terminateFreeRunningLanes(lua_Duration const shutdownTimeout_, Ca | |||
399 | // ################################################################################################# | 410 | // ################################################################################################# |
400 | 411 | ||
401 | // process end: cancel any still free-running threads | 412 | // process end: cancel any still free-running threads |
413 | // as far as I can tell, this can only by called only from lua_close() | ||
402 | int Universe::UniverseGC(lua_State* const L_) | 414 | int Universe::UniverseGC(lua_State* const L_) |
403 | { | 415 | { |
404 | lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; | 416 | lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; |
@@ -416,23 +428,37 @@ int Universe::UniverseGC(lua_State* const L_) | |||
416 | kFinalizerRegKey.pushValue(L_); // L_: U finalizer|nil | 428 | kFinalizerRegKey.pushValue(L_); // L_: U finalizer|nil |
417 | if (!lua_isnil(L_, -1)) { | 429 | if (!lua_isnil(L_, -1)) { |
418 | lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool | 430 | lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool |
419 | // no protection. Lua rules for errors in finalizers apply normally | 431 | // no protection. Lua rules for errors in finalizers apply normally: |
420 | lua_call(L_, 1, 1); // L_: U ret|error | 432 | // Lua 5.4: error is propagated in the warn system |
433 | // older: error is swallowed | ||
434 | lua_call(L_, 1, 1); // L_: U msg? | ||
435 | // phew, no error in finalizer, since we reached that point | ||
421 | } | 436 | } |
422 | STACK_CHECK(L_, 2); | ||
423 | 437 | ||
424 | // if some lanes are still running here, we have no other choice than crashing or freezing and let the client figure out what's wrong | 438 | if (lua_isnil(L_, kIdxTop)) { |
425 | bool const _throw{ luaG_tostring(L_, kIdxTop) == "throw" }; | 439 | lua_pop(L_, 1); // L_: U |
426 | lua_pop(L_, 1); // L_: U | 440 | // no finalizer, or it returned no value: push some default message on the stack, in case it is necessary |
441 | luaG_pushstring(L_, "uncooperative lanes detected at shutdown"); // L_: U "msg" | ||
442 | } | ||
443 | STACK_CHECK(L_, 2); | ||
427 | 444 | ||
428 | while (_U->selfdestructFirst != SELFDESTRUCT_END) { | 445 | // now, all remaining lanes are flagged. if they crash because we remove keepers and the Universe from under them, it is their fault |
429 | if (_throw) { | 446 | bool const _detectedUncooperativeLanes{ _U->selfdestructFirst != SELFDESTRUCT_END }; |
430 | throw std::logic_error{ "Some lanes are still running at shutdown" }; | 447 | if (_detectedUncooperativeLanes) { |
448 | _U->flagDanglingLanes(); | ||
449 | if (luaG_tostring(L_, kIdxTop) == "freeze") { | ||
450 | std::this_thread::sleep_until(std::chrono::time_point<std::chrono::steady_clock>::max()); | ||
431 | } else { | 451 | } else { |
432 | std::this_thread::yield(); | 452 | // take the value returned by the finalizer (or our default message) and throw it as an error |
453 | // since we are inside Lua's GCTM, it will be propagated through the warning system (Lua 5.4) or swallowed silently | ||
454 | raise_lua_error(L_); | ||
433 | } | 455 | } |
434 | } | 456 | } |
435 | 457 | ||
458 | // --------------------------------------------------------- | ||
459 | // we don't reach that point if some lanes are still running | ||
460 | // --------------------------------------------------------- | ||
461 | |||
436 | // no need to mutex-protect this as all lanes in the universe are gone at that point | 462 | // no need to mutex-protect this as all lanes in the universe are gone at that point |
437 | Linda::DeleteTimerLinda(L_, std::exchange(_U->timerLinda, nullptr), PK); | 463 | Linda::DeleteTimerLinda(L_, std::exchange(_U->timerLinda, nullptr), PK); |
438 | 464 | ||
diff --git a/src/universe.hpp b/src/universe.hpp index ab06907..2a3085d 100644 --- a/src/universe.hpp +++ b/src/universe.hpp | |||
@@ -106,7 +106,7 @@ class Universe final | |||
106 | LaneTracker tracker; | 106 | LaneTracker tracker; |
107 | 107 | ||
108 | // Protects modifying the selfdestruct chain | 108 | // Protects modifying the selfdestruct chain |
109 | std::mutex selfdestructMutex; | 109 | mutable std::mutex selfdestructMutex; |
110 | 110 | ||
111 | // require() serialization | 111 | // require() serialization |
112 | std::recursive_mutex requireMutex; | 112 | std::recursive_mutex requireMutex; |
@@ -126,6 +126,7 @@ class Universe final | |||
126 | 126 | ||
127 | private: | 127 | private: |
128 | static int UniverseGC(lua_State* L_); | 128 | static int UniverseGC(lua_State* L_); |
129 | void flagDanglingLanes() const; | ||
129 | 130 | ||
130 | public: | 131 | public: |
131 | [[nodiscard]] | 132 | [[nodiscard]] |