aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2025-03-17 12:34:08 +0100
committerBenoit Germain <benoit.germain@ubisoft.com>2025-03-17 12:34:08 +0100
commita57690123ae3ce5bdd7e970690f1380e88e4eaf6 (patch)
treed526e8f545cef2b1c23978cb9ee5c94dbc9cda2c /src
parentd93de7ca51edea911eeecb7c8edcffe77298ed07 (diff)
downloadlanes-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.cpp6
-rw-r--r--src/lane.hpp4
-rw-r--r--src/universe.cpp46
-rw-r--r--src/universe.hpp3
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
212void 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?
214void Universe::initializeAllocatorFunction(lua_State* const L_) 225void 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()
402int Universe::UniverseGC(lua_State* const L_) 414int 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]]