diff options
Diffstat (limited to 'src/universe.cpp')
-rw-r--r-- | src/universe.cpp | 105 |
1 files changed, 62 insertions, 43 deletions
diff --git a/src/universe.cpp b/src/universe.cpp index 335f056..4db036b 100644 --- a/src/universe.cpp +++ b/src/universe.cpp | |||
@@ -51,6 +51,16 @@ static constexpr RegistryUniqueKey kUniverseFullRegKey{ 0x1C2D76870DD9DD9Full }; | |||
51 | 51 | ||
52 | // ################################################################################################# | 52 | // ################################################################################################# |
53 | 53 | ||
54 | [[nodiscard]] | ||
55 | void* ProtectedAllocator::Protected_lua_Alloc(void* const ud_, void* const ptr_, size_t const osize_, size_t const nsize_) | ||
56 | { | ||
57 | ProtectedAllocator* const _allocator{ static_cast<ProtectedAllocator*>(ud_) }; | ||
58 | std::lock_guard<std::mutex> _guard{ _allocator->mutex }; | ||
59 | return _allocator->alloc(ptr_, osize_, nsize_); | ||
60 | } | ||
61 | |||
62 | // ################################################################################################# | ||
63 | |||
54 | Universe::Universe() | 64 | Universe::Universe() |
55 | { | 65 | { |
56 | //--- | 66 | //--- |
@@ -101,18 +111,18 @@ void Universe::callOnStateCreate(lua_State* const L_, lua_State* const from_, Lo | |||
101 | } | 111 | } |
102 | kConfigRegKey.pushValue(L_); // L_: config | 112 | kConfigRegKey.pushValue(L_); // L_: config |
103 | STACK_CHECK(L_, 1); | 113 | STACK_CHECK(L_, 1); |
104 | LuaType const _funcType{ luaG_getfield(L_, kIdxTop, kOnStateCreate) }; // L_: config on_state_create() | 114 | LuaType const _funcType{ luaW_getfield(L_, kIdxTop, kOnStateCreate) }; // L_: config on_state_create() |
105 | if (_funcType != LuaType::FUNCTION) { | 115 | if (_funcType != LuaType::FUNCTION) { |
106 | raise_luaL_error(L_, "INTERNAL ERROR: %s is a %s, not a function", kOnStateCreate.data(), luaG_typename(L_, _funcType).data()); | 116 | raise_luaL_error(L_, "INTERNAL ERROR: %s is a %s, not a function", kOnStateCreate.data(), luaW_typename(L_, _funcType).data()); |
107 | } | 117 | } |
108 | lua_remove(L_, -2); // L_: on_state_create() | 118 | lua_remove(L_, -2); // L_: on_state_create() |
109 | } | 119 | } |
110 | STACK_CHECK(L_, 1); | 120 | STACK_CHECK(L_, 1); |
111 | // capture error and raise it in caller state | 121 | // capture error and raise it in caller state |
112 | std::string_view const _stateType{ mode_ == LookupMode::LaneBody ? "lane" : "keeper" }; | 122 | std::string_view const _stateType{ mode_ == LookupMode::LaneBody ? "lane" : "keeper" }; |
113 | luaG_pushstring(L_, _stateType); // L_: on_state_create() "<type>" | 123 | luaW_pushstring(L_, _stateType); // L_: on_state_create() "<type>" |
114 | if (lua_pcall(L_, 1, 0, 0) != LUA_OK) { | 124 | if (lua_pcall(L_, 1, 0, 0) != LUA_OK) { |
115 | raise_luaL_error(from_, "%s failed in %s: \"%s\"", kOnStateCreate.data(), _stateType.data(), lua_isstring(L_, -1) ? luaG_tostring(L_, kIdxTop).data() : luaG_typename(L_, kIdxTop).data()); | 125 | raise_luaL_error(from_, "%s failed in %s: \"%s\"", kOnStateCreate.data(), _stateType.data(), lua_isstring(L_, -1) ? luaW_tostring(L_, kIdxTop).data() : luaW_typename(L_, kIdxTop).data()); |
116 | } | 126 | } |
117 | STACK_CHECK(L_, 0); | 127 | STACK_CHECK(L_, 0); |
118 | } | 128 | } |
@@ -127,14 +137,14 @@ Universe* Universe::Create(lua_State* const L_) | |||
127 | static constexpr StackIndex kIdxSettings{ 1 }; | 137 | static constexpr StackIndex kIdxSettings{ 1 }; |
128 | LUA_ASSERT(L_, lua_gettop(L_) == 1 && lua_istable(L_, 1)); | 138 | LUA_ASSERT(L_, lua_gettop(L_) == 1 && lua_istable(L_, 1)); |
129 | STACK_CHECK_START_REL(L_, 0); // L_: settings | 139 | STACK_CHECK_START_REL(L_, 0); // L_: settings |
130 | std::ignore = luaG_getfield(L_, kIdxSettings, "nb_user_keepers"); // L_: settings nb_user_keepers | 140 | std::ignore = luaW_getfield(L_, kIdxSettings, "nb_user_keepers"); // L_: settings nb_user_keepers |
131 | int const _nbUserKeepers{ static_cast<int>(lua_tointeger(L_, -1)) + 1}; | 141 | int const _nbUserKeepers{ static_cast<int>(lua_tointeger(L_, -1)) + 1}; |
132 | lua_pop(L_, 1); // L_: settings | 142 | lua_pop(L_, 1); // L_: settings |
133 | if (_nbUserKeepers < 1) { | 143 | if (_nbUserKeepers < 1) { |
134 | raise_luaL_error(L_, "Bad number of additional keepers (%d)", _nbUserKeepers); | 144 | raise_luaL_error(L_, "Bad number of additional keepers (%d)", _nbUserKeepers); |
135 | } | 145 | } |
136 | STACK_CHECK(L_, 0); | 146 | STACK_CHECK(L_, 0); |
137 | std::ignore = luaG_getfield(L_, kIdxSettings, "keepers_gc_threshold"); // L_: settings keepers_gc_threshold | 147 | std::ignore = luaW_getfield(L_, kIdxSettings, "keepers_gc_threshold"); // L_: settings keepers_gc_threshold |
138 | int const _keepers_gc_threshold{ static_cast<int>(lua_tointeger(L_, -1)) }; | 148 | int const _keepers_gc_threshold{ static_cast<int>(lua_tointeger(L_, -1)) }; |
139 | lua_pop(L_, 1); // L_: settings | 149 | lua_pop(L_, 1); // L_: settings |
140 | STACK_CHECK(L_, 0); | 150 | STACK_CHECK(L_, 0); |
@@ -147,30 +157,30 @@ Universe* Universe::Create(lua_State* const L_) | |||
147 | 157 | ||
148 | DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); | 158 | DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); |
149 | lua_createtable(L_, 0, 1); // L_: settings universe {mt} | 159 | lua_createtable(L_, 0, 1); // L_: settings universe {mt} |
150 | std::ignore = luaG_getfield(L_, kIdxSettings, "shutdown_timeout"); // L_: settings universe {mt} shutdown_timeout | 160 | std::ignore = luaW_getfield(L_, kIdxSettings, "shutdown_timeout"); // L_: settings universe {mt} shutdown_timeout |
151 | lua_pushcclosure(L_, UniverseGC, 1); // L_: settings universe {mt} UniverseGC | 161 | lua_pushcclosure(L_, UniverseGC, 1); // L_: settings universe {mt} UniverseGC |
152 | lua_setfield(L_, -2, "__gc"); // L_: settings universe {mt} | 162 | lua_setfield(L_, -2, "__gc"); // L_: settings universe {mt} |
153 | lua_setmetatable(L_, -2); // L_: settings universe | 163 | lua_setmetatable(L_, -2); // L_: settings universe |
154 | lua_pop(L_, 1); // L_: settings | 164 | lua_pop(L_, 1); // L_: settings |
155 | 165 | ||
156 | std::ignore = luaG_getfield(L_, kIdxSettings, "linda_wake_period"); // L_: settings linda_wake_period | 166 | std::ignore = luaW_getfield(L_, kIdxSettings, "linda_wake_period"); // L_: settings linda_wake_period |
157 | if (luaG_type(L_, kIdxTop) == LuaType::NUMBER) { | 167 | if (luaW_type(L_, kIdxTop) == LuaType::NUMBER) { |
158 | _U->lindaWakePeriod = lua_Duration{ lua_tonumber(L_, kIdxTop) }; | 168 | _U->lindaWakePeriod = lua_Duration{ lua_tonumber(L_, kIdxTop) }; |
159 | } else { | 169 | } else { |
160 | LUA_ASSERT(L_, luaG_tostring(L_, kIdxTop) == "never"); | 170 | LUA_ASSERT(L_, luaW_tostring(L_, kIdxTop) == "never"); |
161 | } | 171 | } |
162 | lua_pop(L_, 1); // L_: settings | 172 | lua_pop(L_, 1); // L_: settings |
163 | 173 | ||
164 | std::ignore = luaG_getfield(L_, kIdxSettings, "strip_functions"); // L_: settings strip_functions | 174 | std::ignore = luaW_getfield(L_, kIdxSettings, "strip_functions"); // L_: settings strip_functions |
165 | _U->stripFunctions = lua_toboolean(L_, -1) ? true : false; | 175 | _U->stripFunctions = lua_toboolean(L_, -1) ? true : false; |
166 | lua_pop(L_, 1); // L_: settings | 176 | lua_pop(L_, 1); // L_: settings |
167 | 177 | ||
168 | std::ignore = luaG_getfield(L_, kIdxSettings, "verbose_errors"); // L_: settings verbose_errors | 178 | std::ignore = luaW_getfield(L_, kIdxSettings, "verbose_errors"); // L_: settings verbose_errors |
169 | _U->verboseErrors = lua_toboolean(L_, -1) ? true : false; | 179 | _U->verboseErrors = lua_toboolean(L_, -1) ? true : false; |
170 | lua_pop(L_, 1); // L_: settings | 180 | lua_pop(L_, 1); // L_: settings |
171 | 181 | ||
172 | // tracking | 182 | // tracking |
173 | std::ignore = luaG_getfield(L_, kIdxSettings, "track_lanes"); // L_: settings track_lanes | 183 | std::ignore = luaW_getfield(L_, kIdxSettings, "track_lanes"); // L_: settings track_lanes |
174 | if (lua_toboolean(L_, -1)) { | 184 | if (lua_toboolean(L_, -1)) { |
175 | _U->tracker.activate(); | 185 | _U->tracker.activate(); |
176 | } | 186 | } |
@@ -178,8 +188,8 @@ Universe* Universe::Create(lua_State* const L_) | |||
178 | 188 | ||
179 | // Linked chains handling | 189 | // Linked chains handling |
180 | _U->selfdestructFirst = SELFDESTRUCT_END; | 190 | _U->selfdestructFirst = SELFDESTRUCT_END; |
181 | _U->initializeAllocatorFunction(L_); | 191 | _U->initializeAllocatorFunction(L_); // this can raise an error |
182 | _U->initializeOnStateCreate(L_); | 192 | _U->initializeOnStateCreate(L_); // this can raise an error |
183 | _U->keepers.initialize(*_U, L_, static_cast<size_t>(_nbUserKeepers), _keepers_gc_threshold); | 193 | _U->keepers.initialize(*_U, L_, static_cast<size_t>(_nbUserKeepers), _keepers_gc_threshold); |
184 | STACK_CHECK(L_, 0); | 194 | STACK_CHECK(L_, 0); |
185 | 195 | ||
@@ -207,7 +217,7 @@ static void* libc_lua_Alloc([[maybe_unused]] void* const ud_, [[maybe_unused]] v | |||
207 | // ################################################################################################# | 217 | // ################################################################################################# |
208 | 218 | ||
209 | [[nodiscard]] | 219 | [[nodiscard]] |
210 | static int luaG_provide_protected_allocator(lua_State* const L_) | 220 | static int luaW_provide_protected_allocator(lua_State* const L_) |
211 | { | 221 | { |
212 | Universe* const _U{ Universe::Get(L_) }; | 222 | Universe* const _U{ Universe::Get(L_) }; |
213 | // push a new full userdata on the stack, giving access to the universe's protected allocator | 223 | // push a new full userdata on the stack, giving access to the universe's protected allocator |
@@ -217,9 +227,9 @@ static int luaG_provide_protected_allocator(lua_State* const L_) | |||
217 | 227 | ||
218 | // ################################################################################################# | 228 | // ################################################################################################# |
219 | 229 | ||
230 | // already called under protection of selfdestructMutex | ||
220 | void Universe::flagDanglingLanes() const | 231 | void Universe::flagDanglingLanes() const |
221 | { | 232 | { |
222 | std::lock_guard<std::mutex> _guard{ selfdestructMutex }; | ||
223 | Lane* _lane{ selfdestructFirst }; | 233 | Lane* _lane{ selfdestructFirst }; |
224 | while (_lane != SELFDESTRUCT_END) { | 234 | while (_lane != SELFDESTRUCT_END) { |
225 | _lane->flaggedAfterUniverseGC.store(true, std::memory_order_relaxed); | 235 | _lane->flaggedAfterUniverseGC.store(true, std::memory_order_relaxed); |
@@ -235,17 +245,17 @@ void Universe::initializeAllocatorFunction(lua_State* const L_) | |||
235 | // start by just grabbing whatever allocator was provided to the master state | 245 | // start by just grabbing whatever allocator was provided to the master state |
236 | protectedAllocator.initFrom(L_); | 246 | protectedAllocator.initFrom(L_); |
237 | STACK_CHECK_START_REL(L_, 1); // L_: settings | 247 | STACK_CHECK_START_REL(L_, 1); // L_: settings |
238 | switch (luaG_getfield(L_, kIdxTop, "allocator")) { // L_: settings allocator|nil|"protected" | 248 | switch (luaW_getfield(L_, kIdxTop, "allocator")) { // L_: settings allocator|nil|"protected" |
239 | case LuaType::NIL: | 249 | case LuaType::NIL: |
240 | // nothing else to do | 250 | // nothing else to do |
241 | break; | 251 | break; |
242 | 252 | ||
243 | case LuaType::STRING: | 253 | case LuaType::STRING: |
244 | LUA_ASSERT(L_, luaG_tostring(L_, kIdxTop) == "protected"); | 254 | LUA_ASSERT(L_, luaW_tostring(L_, kIdxTop) == "protected"); |
245 | // set the original allocator to call from inside protection by the mutex | 255 | // set the original allocator to call from inside protection by the mutex |
246 | protectedAllocator.installIn(L_); | 256 | protectedAllocator.installIn(L_); |
247 | // before a state is created, this function will be called to obtain the allocator | 257 | // before a state is created, this function will be called to obtain the allocator |
248 | provideAllocator = luaG_provide_protected_allocator; | 258 | provideAllocator = luaW_provide_protected_allocator; |
249 | break; | 259 | break; |
250 | 260 | ||
251 | case LuaType::FUNCTION: | 261 | case LuaType::FUNCTION: |
@@ -266,14 +276,14 @@ void Universe::initializeAllocatorFunction(lua_State* const L_) | |||
266 | break; | 276 | break; |
267 | 277 | ||
268 | default: // should be filtered out in lanes.lua | 278 | default: // should be filtered out in lanes.lua |
269 | raise_luaL_error(L_, "Bad config.allocator type %s", luaG_typename(L_, kIdxTop).data()); | 279 | raise_luaL_error(L_, "Bad config.allocator type %s", luaW_typename(L_, kIdxTop).data()); |
270 | } | 280 | } |
271 | lua_pop(L_, 1); // L_: settings | 281 | lua_pop(L_, 1); // L_: settings |
272 | STACK_CHECK(L_, 1); | 282 | STACK_CHECK(L_, 1); |
273 | 283 | ||
274 | std::ignore = luaG_getfield(L_, kIdxTop, "internal_allocator"); // L_: settings "libc"|"allocator" | 284 | std::ignore = luaW_getfield(L_, kIdxTop, "internal_allocator"); // L_: settings "libc"|"allocator" |
275 | LUA_ASSERT(L_, lua_isstring(L_, kIdxTop)); // should be the case due to lanes.lua parameter validation | 285 | LUA_ASSERT(L_, lua_isstring(L_, kIdxTop)); // should be the case due to lanes.lua parameter validation |
276 | std::string_view const _allocator{ luaG_tostring(L_, kIdxTop) }; | 286 | std::string_view const _allocator{ luaW_tostring(L_, kIdxTop) }; |
277 | // use whatever the provider provides. This performs validation of what provideAllocator is giving | 287 | // use whatever the provider provides. This performs validation of what provideAllocator is giving |
278 | // we do this even if _allocator == "libc", to have the validation part | 288 | // we do this even if _allocator == "libc", to have the validation part |
279 | internalAllocator = resolveAndValidateAllocator(L_, "internal"); | 289 | internalAllocator = resolveAndValidateAllocator(L_, "internal"); |
@@ -295,7 +305,7 @@ int Universe::InitializeFinalizer(lua_State* const L_) | |||
295 | 305 | ||
296 | // make sure we are only called from the Master Lua State! | 306 | // make sure we are only called from the Master Lua State! |
297 | kUniverseFullRegKey.pushValue(L_); // L_: f U | 307 | kUniverseFullRegKey.pushValue(L_); // L_: f U |
298 | if (luaG_type(L_, kIdxTop) != LuaType::USERDATA) { | 308 | if (luaW_type(L_, kIdxTop) != LuaType::USERDATA) { |
299 | raise_luaL_error(L_, "lanes.%s called from inside a lane", kFinally); | 309 | raise_luaL_error(L_, "lanes.%s called from inside a lane", kFinally); |
300 | } | 310 | } |
301 | lua_pop(L_, 1); // L_: f | 311 | lua_pop(L_, 1); // L_: f |
@@ -311,8 +321,8 @@ int Universe::InitializeFinalizer(lua_State* const L_) | |||
311 | void Universe::initializeOnStateCreate(lua_State* const L_) | 321 | void Universe::initializeOnStateCreate(lua_State* const L_) |
312 | { | 322 | { |
313 | STACK_CHECK_START_REL(L_, 0); // L_: settings | 323 | STACK_CHECK_START_REL(L_, 0); // L_: settings |
314 | if (luaG_getfield(L_, kIdxTop, kOnStateCreate) != LuaType::NIL) { // L_: settings on_state_create|nil | 324 | if (luaW_getfield(L_, kIdxTop, kOnStateCreate) != LuaType::NIL) { // L_: settings on_state_create|nil |
315 | LUA_ASSERT(L_, luaG_type(L_, kIdxTop) == LuaType::FUNCTION); // ensured by lanes.lua parameter validation | 325 | LUA_ASSERT(L_, luaW_type(L_, kIdxTop) == LuaType::FUNCTION); // ensured by lanes.lua parameter validation |
316 | // store C function pointer in an internal variable | 326 | // store C function pointer in an internal variable |
317 | lua_CFunction const _func{ lua_tocfunction(L_, -1) }; // L_: settings on_state_create | 327 | lua_CFunction const _func{ lua_tocfunction(L_, -1) }; // L_: settings on_state_create |
318 | if (_func) { | 328 | if (_func) { |
@@ -325,7 +335,7 @@ void Universe::initializeOnStateCreate(lua_State* const L_) | |||
325 | // remove this C function from the config table so that it doesn't cause problems | 335 | // remove this C function from the config table so that it doesn't cause problems |
326 | // when we transfer the config table in newly created Lua states | 336 | // when we transfer the config table in newly created Lua states |
327 | lua_pushnil(L_); // L_: settings on_state_create nil | 337 | lua_pushnil(L_); // L_: settings on_state_create nil |
328 | luaG_setfield(L_, StackIndex{ -3 }, kOnStateCreate); // L_: settings on_state_create | 338 | luaW_setfield(L_, StackIndex{ -3 }, kOnStateCreate); // L_: settings on_state_create |
329 | } else { | 339 | } else { |
330 | // the function is still in the config table. we indicate this with the uintptr_t alternative (actual value is irrelevant) | 340 | // the function is still in the config table. we indicate this with the uintptr_t alternative (actual value is irrelevant) |
331 | onStateCreateFunc.emplace<uintptr_t>(std::bit_cast<uintptr_t>(kOnStateCreate.data())); | 341 | onStateCreateFunc.emplace<uintptr_t>(std::bit_cast<uintptr_t>(kOnStateCreate.data())); |
@@ -348,7 +358,7 @@ lanes::AllocatorDefinition Universe::resolveAndValidateAllocator(lua_State* cons | |||
348 | 358 | ||
349 | STACK_CHECK_START_REL(L_, 0); // here, we have a function we can call to obtain an allocator | 359 | STACK_CHECK_START_REL(L_, 0); // here, we have a function we can call to obtain an allocator |
350 | lua_pushcclosure(L_, provideAllocator, 0); // L_: provideAllocator() | 360 | lua_pushcclosure(L_, provideAllocator, 0); // L_: provideAllocator() |
351 | luaG_pushstring(L_, hint_); // L_: provideAllocator() "<hint>" | 361 | luaW_pushstring(L_, hint_); // L_: provideAllocator() "<hint>" |
352 | lua_call(L_, 1, 1); // L_: result | 362 | lua_call(L_, 1, 1); // L_: result |
353 | // make sure we have a valid AllocatorDefinition on the stack (an error is raised instead if it is not the case) | 363 | // make sure we have a valid AllocatorDefinition on the stack (an error is raised instead if it is not the case) |
354 | _ret = lanes::AllocatorDefinition::Validated(L_, kIdxTop); | 364 | _ret = lanes::AllocatorDefinition::Validated(L_, kIdxTop); |
@@ -423,7 +433,7 @@ int Universe::UniverseGC(lua_State* const L_) | |||
423 | { | 433 | { |
424 | lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; | 434 | lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; |
425 | STACK_CHECK_START_ABS(L_, 1); | 435 | STACK_CHECK_START_ABS(L_, 1); |
426 | Universe* const _U{ luaG_tofulluserdata<Universe>(L_, StackIndex{ 1 }) }; // L_: U | 436 | Universe* const _U{ luaW_tofulluserdata<Universe>(L_, StackIndex{ 1 }) }; // L_: U |
427 | 437 | ||
428 | // attempt to terminate all lanes with increasingly stronger cancel methods | 438 | // attempt to terminate all lanes with increasingly stronger cancel methods |
429 | bool const _allLanesTerminated{ | 439 | bool const _allLanesTerminated{ |
@@ -437,7 +447,7 @@ int Universe::UniverseGC(lua_State* const L_) | |||
437 | if (!lua_isnil(L_, -1)) { | 447 | if (!lua_isnil(L_, -1)) { |
438 | lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool | 448 | lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool |
439 | // no protection. Lua rules for errors in finalizers apply normally: | 449 | // no protection. Lua rules for errors in finalizers apply normally: |
440 | // Lua 5.4: error is propagated in the warn system | 450 | // Lua 5.4+: error is propagated in the warn system |
441 | // older: error is swallowed | 451 | // older: error is swallowed |
442 | lua_call(L_, 1, 1); // L_: U msg? | 452 | lua_call(L_, 1, 1); // L_: U msg? |
443 | // phew, no error in finalizer, since we reached that point | 453 | // phew, no error in finalizer, since we reached that point |
@@ -446,24 +456,31 @@ int Universe::UniverseGC(lua_State* const L_) | |||
446 | if (lua_isnil(L_, kIdxTop)) { | 456 | if (lua_isnil(L_, kIdxTop)) { |
447 | lua_pop(L_, 1); // L_: U | 457 | lua_pop(L_, 1); // L_: U |
448 | // no finalizer, or it returned no value: push some default message on the stack, in case it is necessary | 458 | // no finalizer, or it returned no value: push some default message on the stack, in case it is necessary |
449 | luaG_pushstring(L_, "uncooperative lanes detected at shutdown"); // L_: U "msg" | 459 | luaW_pushstring(L_, "uncooperative lanes detected at shutdown"); // L_: U "msg" |
450 | } | 460 | } |
451 | STACK_CHECK(L_, 2); | 461 | STACK_CHECK(L_, 2); |
452 | 462 | ||
453 | // now, all remaining lanes are flagged. if they crash because we remove keepers and the Universe from under them, it is their fault | 463 | { |
454 | bool const _detectedUncooperativeLanes{ _U->selfdestructFirst != SELFDESTRUCT_END }; | 464 | std::lock_guard<std::mutex> _guard{ _U->selfdestructMutex }; |
455 | if (_detectedUncooperativeLanes) { | 465 | // now, all remaining lanes are flagged. if they crash because we remove keepers and the Universe from under them, it is their fault |
456 | _U->flagDanglingLanes(); | 466 | bool const _detectedUncooperativeLanes{ _U->selfdestructFirst != SELFDESTRUCT_END }; |
457 | if (luaG_tostring(L_, kIdxTop) == "freeze") { | 467 | if (_detectedUncooperativeLanes) { |
458 | std::this_thread::sleep_until(std::chrono::time_point<std::chrono::steady_clock>::max()); | 468 | _U->flagDanglingLanes(); |
469 | if (luaW_tostring(L_, kIdxTop) == "freeze") { | ||
470 | std::this_thread::sleep_until(std::chrono::time_point<std::chrono::steady_clock>::max()); | ||
471 | } else { | ||
472 | // take the value returned by the finalizer (or our default message) and throw it as an error | ||
473 | // since we are inside Lua's GCTM, it will be propagated through the warning system (Lua 5.4) or swallowed silently | ||
474 | // IMPORTANT: lua_error() is used here instead of the wrapper raise_lua_error() to circumvent what looks like a MSVC compiler bug | ||
475 | // that manifests as a crash inside ntdll!longjmp() function, in optimized builds only | ||
476 | lua_error(L_); | ||
477 | } | ||
459 | } else { | 478 | } else { |
460 | // take the value returned by the finalizer (or our default message) and throw it as an error | 479 | // we didn't use the error message, let's keep a clean stack |
461 | // since we are inside Lua's GCTM, it will be propagated through the warning system (Lua 5.4) or swallowed silently | 480 | lua_pop(L_, 1); // L_: U |
462 | // IMPORTANT: lua_error() is used here instead of the wrapper raise_lua_error() to circumvent what looks like a MSVC compiler bug | ||
463 | // that manifests as a crash inside ntdll!longjmp() function, in optimized builds only | ||
464 | lua_error(L_); | ||
465 | } | 481 | } |
466 | } | 482 | } |
483 | STACK_CHECK(L_, 1); | ||
467 | 484 | ||
468 | // --------------------------------------------------------- | 485 | // --------------------------------------------------------- |
469 | // we don't reach that point if some lanes are still running | 486 | // we don't reach that point if some lanes are still running |
@@ -472,7 +489,9 @@ int Universe::UniverseGC(lua_State* const L_) | |||
472 | // no need to mutex-protect this as all lanes in the universe are gone at that point | 489 | // no need to mutex-protect this as all lanes in the universe are gone at that point |
473 | Linda::DeleteTimerLinda(L_, std::exchange(_U->timerLinda, nullptr), PK); | 490 | Linda::DeleteTimerLinda(L_, std::exchange(_U->timerLinda, nullptr), PK); |
474 | 491 | ||
475 | _U->keepers.close(); | 492 | if (!_U->keepers.close()) { |
493 | raise_luaL_error(L_, "INTERNAL ERROR: Keepers closed more than once"); | ||
494 | } | ||
476 | 495 | ||
477 | // remove the protected allocator, if any | 496 | // remove the protected allocator, if any |
478 | _U->protectedAllocator.removeFrom(L_); | 497 | _U->protectedAllocator.removeFrom(L_); |