diff options
Diffstat (limited to 'src/universe.cpp')
-rw-r--r-- | src/universe.cpp | 107 |
1 files changed, 67 insertions, 40 deletions
diff --git a/src/universe.cpp b/src/universe.cpp index 89ad02a..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,22 +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, "strip_functions"); // L_: settings strip_functions | 166 | std::ignore = luaW_getfield(L_, kIdxSettings, "linda_wake_period"); // L_: settings linda_wake_period |
167 | if (luaW_type(L_, kIdxTop) == LuaType::NUMBER) { | ||
168 | _U->lindaWakePeriod = lua_Duration{ lua_tonumber(L_, kIdxTop) }; | ||
169 | } else { | ||
170 | LUA_ASSERT(L_, luaW_tostring(L_, kIdxTop) == "never"); | ||
171 | } | ||
172 | lua_pop(L_, 1); // L_: settings | ||
173 | |||
174 | std::ignore = luaW_getfield(L_, kIdxSettings, "strip_functions"); // L_: settings strip_functions | ||
157 | _U->stripFunctions = lua_toboolean(L_, -1) ? true : false; | 175 | _U->stripFunctions = lua_toboolean(L_, -1) ? true : false; |
158 | lua_pop(L_, 1); // L_: settings | 176 | lua_pop(L_, 1); // L_: settings |
159 | 177 | ||
160 | 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 |
161 | _U->verboseErrors = lua_toboolean(L_, -1) ? true : false; | 179 | _U->verboseErrors = lua_toboolean(L_, -1) ? true : false; |
162 | lua_pop(L_, 1); // L_: settings | 180 | lua_pop(L_, 1); // L_: settings |
163 | 181 | ||
164 | // tracking | 182 | // tracking |
165 | 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 |
166 | if (lua_toboolean(L_, -1)) { | 184 | if (lua_toboolean(L_, -1)) { |
167 | _U->tracker.activate(); | 185 | _U->tracker.activate(); |
168 | } | 186 | } |
@@ -170,8 +188,8 @@ Universe* Universe::Create(lua_State* const L_) | |||
170 | 188 | ||
171 | // Linked chains handling | 189 | // Linked chains handling |
172 | _U->selfdestructFirst = SELFDESTRUCT_END; | 190 | _U->selfdestructFirst = SELFDESTRUCT_END; |
173 | _U->initializeAllocatorFunction(L_); | 191 | _U->initializeAllocatorFunction(L_); // this can raise an error |
174 | _U->initializeOnStateCreate(L_); | 192 | _U->initializeOnStateCreate(L_); // this can raise an error |
175 | _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); |
176 | STACK_CHECK(L_, 0); | 194 | STACK_CHECK(L_, 0); |
177 | 195 | ||
@@ -199,7 +217,7 @@ static void* libc_lua_Alloc([[maybe_unused]] void* const ud_, [[maybe_unused]] v | |||
199 | // ################################################################################################# | 217 | // ################################################################################################# |
200 | 218 | ||
201 | [[nodiscard]] | 219 | [[nodiscard]] |
202 | static int luaG_provide_protected_allocator(lua_State* const L_) | 220 | static int luaW_provide_protected_allocator(lua_State* const L_) |
203 | { | 221 | { |
204 | Universe* const _U{ Universe::Get(L_) }; | 222 | Universe* const _U{ Universe::Get(L_) }; |
205 | // 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 |
@@ -209,9 +227,9 @@ static int luaG_provide_protected_allocator(lua_State* const L_) | |||
209 | 227 | ||
210 | // ################################################################################################# | 228 | // ################################################################################################# |
211 | 229 | ||
230 | // already called under protection of selfdestructMutex | ||
212 | void Universe::flagDanglingLanes() const | 231 | void Universe::flagDanglingLanes() const |
213 | { | 232 | { |
214 | std::lock_guard<std::mutex> _guard{ selfdestructMutex }; | ||
215 | Lane* _lane{ selfdestructFirst }; | 233 | Lane* _lane{ selfdestructFirst }; |
216 | while (_lane != SELFDESTRUCT_END) { | 234 | while (_lane != SELFDESTRUCT_END) { |
217 | _lane->flaggedAfterUniverseGC.store(true, std::memory_order_relaxed); | 235 | _lane->flaggedAfterUniverseGC.store(true, std::memory_order_relaxed); |
@@ -227,17 +245,17 @@ void Universe::initializeAllocatorFunction(lua_State* const L_) | |||
227 | // 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 |
228 | protectedAllocator.initFrom(L_); | 246 | protectedAllocator.initFrom(L_); |
229 | STACK_CHECK_START_REL(L_, 1); // L_: settings | 247 | STACK_CHECK_START_REL(L_, 1); // L_: settings |
230 | switch (luaG_getfield(L_, kIdxTop, "allocator")) { // L_: settings allocator|nil|"protected" | 248 | switch (luaW_getfield(L_, kIdxTop, "allocator")) { // L_: settings allocator|nil|"protected" |
231 | case LuaType::NIL: | 249 | case LuaType::NIL: |
232 | // nothing else to do | 250 | // nothing else to do |
233 | break; | 251 | break; |
234 | 252 | ||
235 | case LuaType::STRING: | 253 | case LuaType::STRING: |
236 | LUA_ASSERT(L_, luaG_tostring(L_, kIdxTop) == "protected"); | 254 | LUA_ASSERT(L_, luaW_tostring(L_, kIdxTop) == "protected"); |
237 | // 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 |
238 | protectedAllocator.installIn(L_); | 256 | protectedAllocator.installIn(L_); |
239 | // 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 |
240 | provideAllocator = luaG_provide_protected_allocator; | 258 | provideAllocator = luaW_provide_protected_allocator; |
241 | break; | 259 | break; |
242 | 260 | ||
243 | case LuaType::FUNCTION: | 261 | case LuaType::FUNCTION: |
@@ -258,14 +276,14 @@ void Universe::initializeAllocatorFunction(lua_State* const L_) | |||
258 | break; | 276 | break; |
259 | 277 | ||
260 | default: // should be filtered out in lanes.lua | 278 | default: // should be filtered out in lanes.lua |
261 | 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()); |
262 | } | 280 | } |
263 | lua_pop(L_, 1); // L_: settings | 281 | lua_pop(L_, 1); // L_: settings |
264 | STACK_CHECK(L_, 1); | 282 | STACK_CHECK(L_, 1); |
265 | 283 | ||
266 | 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" |
267 | 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 |
268 | std::string_view const _allocator{ luaG_tostring(L_, kIdxTop) }; | 286 | std::string_view const _allocator{ luaW_tostring(L_, kIdxTop) }; |
269 | // 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 |
270 | // 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 |
271 | internalAllocator = resolveAndValidateAllocator(L_, "internal"); | 289 | internalAllocator = resolveAndValidateAllocator(L_, "internal"); |
@@ -287,7 +305,7 @@ int Universe::InitializeFinalizer(lua_State* const L_) | |||
287 | 305 | ||
288 | // make sure we are only called from the Master Lua State! | 306 | // make sure we are only called from the Master Lua State! |
289 | kUniverseFullRegKey.pushValue(L_); // L_: f U | 307 | kUniverseFullRegKey.pushValue(L_); // L_: f U |
290 | if (luaG_type(L_, kIdxTop) != LuaType::USERDATA) { | 308 | if (luaW_type(L_, kIdxTop) != LuaType::USERDATA) { |
291 | 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); |
292 | } | 310 | } |
293 | lua_pop(L_, 1); // L_: f | 311 | lua_pop(L_, 1); // L_: f |
@@ -303,8 +321,8 @@ int Universe::InitializeFinalizer(lua_State* const L_) | |||
303 | void Universe::initializeOnStateCreate(lua_State* const L_) | 321 | void Universe::initializeOnStateCreate(lua_State* const L_) |
304 | { | 322 | { |
305 | STACK_CHECK_START_REL(L_, 0); // L_: settings | 323 | STACK_CHECK_START_REL(L_, 0); // L_: settings |
306 | 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 |
307 | 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 |
308 | // store C function pointer in an internal variable | 326 | // store C function pointer in an internal variable |
309 | 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 |
310 | if (_func) { | 328 | if (_func) { |
@@ -317,7 +335,7 @@ void Universe::initializeOnStateCreate(lua_State* const L_) | |||
317 | // 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 |
318 | // when we transfer the config table in newly created Lua states | 336 | // when we transfer the config table in newly created Lua states |
319 | lua_pushnil(L_); // L_: settings on_state_create nil | 337 | lua_pushnil(L_); // L_: settings on_state_create nil |
320 | luaG_setfield(L_, StackIndex{ -3 }, kOnStateCreate); // L_: settings on_state_create | 338 | luaW_setfield(L_, StackIndex{ -3 }, kOnStateCreate); // L_: settings on_state_create |
321 | } else { | 339 | } else { |
322 | // 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) |
323 | onStateCreateFunc.emplace<uintptr_t>(std::bit_cast<uintptr_t>(kOnStateCreate.data())); | 341 | onStateCreateFunc.emplace<uintptr_t>(std::bit_cast<uintptr_t>(kOnStateCreate.data())); |
@@ -340,7 +358,7 @@ lanes::AllocatorDefinition Universe::resolveAndValidateAllocator(lua_State* cons | |||
340 | 358 | ||
341 | 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 |
342 | lua_pushcclosure(L_, provideAllocator, 0); // L_: provideAllocator() | 360 | lua_pushcclosure(L_, provideAllocator, 0); // L_: provideAllocator() |
343 | luaG_pushstring(L_, hint_); // L_: provideAllocator() "<hint>" | 361 | luaW_pushstring(L_, hint_); // L_: provideAllocator() "<hint>" |
344 | lua_call(L_, 1, 1); // L_: result | 362 | lua_call(L_, 1, 1); // L_: result |
345 | // 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) |
346 | _ret = lanes::AllocatorDefinition::Validated(L_, kIdxTop); | 364 | _ret = lanes::AllocatorDefinition::Validated(L_, kIdxTop); |
@@ -415,7 +433,7 @@ int Universe::UniverseGC(lua_State* const L_) | |||
415 | { | 433 | { |
416 | lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; | 434 | lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; |
417 | STACK_CHECK_START_ABS(L_, 1); | 435 | STACK_CHECK_START_ABS(L_, 1); |
418 | Universe* const _U{ luaG_tofulluserdata<Universe>(L_, StackIndex{ 1 }) }; // L_: U | 436 | Universe* const _U{ luaW_tofulluserdata<Universe>(L_, StackIndex{ 1 }) }; // L_: U |
419 | 437 | ||
420 | // attempt to terminate all lanes with increasingly stronger cancel methods | 438 | // attempt to terminate all lanes with increasingly stronger cancel methods |
421 | bool const _allLanesTerminated{ | 439 | bool const _allLanesTerminated{ |
@@ -429,7 +447,7 @@ int Universe::UniverseGC(lua_State* const L_) | |||
429 | if (!lua_isnil(L_, -1)) { | 447 | if (!lua_isnil(L_, -1)) { |
430 | lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool | 448 | lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool |
431 | // no protection. Lua rules for errors in finalizers apply normally: | 449 | // no protection. Lua rules for errors in finalizers apply normally: |
432 | // Lua 5.4: error is propagated in the warn system | 450 | // Lua 5.4+: error is propagated in the warn system |
433 | // older: error is swallowed | 451 | // older: error is swallowed |
434 | lua_call(L_, 1, 1); // L_: U msg? | 452 | lua_call(L_, 1, 1); // L_: U msg? |
435 | // phew, no error in finalizer, since we reached that point | 453 | // phew, no error in finalizer, since we reached that point |
@@ -438,24 +456,31 @@ int Universe::UniverseGC(lua_State* const L_) | |||
438 | if (lua_isnil(L_, kIdxTop)) { | 456 | if (lua_isnil(L_, kIdxTop)) { |
439 | lua_pop(L_, 1); // L_: U | 457 | 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 | 458 | // 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" | 459 | luaW_pushstring(L_, "uncooperative lanes detected at shutdown"); // L_: U "msg" |
442 | } | 460 | } |
443 | STACK_CHECK(L_, 2); | 461 | STACK_CHECK(L_, 2); |
444 | 462 | ||
445 | // now, all remaining lanes are flagged. if they crash because we remove keepers and the Universe from under them, it is their fault | 463 | { |
446 | bool const _detectedUncooperativeLanes{ _U->selfdestructFirst != SELFDESTRUCT_END }; | 464 | std::lock_guard<std::mutex> _guard{ _U->selfdestructMutex }; |
447 | 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 |
448 | _U->flagDanglingLanes(); | 466 | bool const _detectedUncooperativeLanes{ _U->selfdestructFirst != SELFDESTRUCT_END }; |
449 | if (luaG_tostring(L_, kIdxTop) == "freeze") { | 467 | if (_detectedUncooperativeLanes) { |
450 | 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 | } | ||
451 | } else { | 478 | } else { |
452 | // 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 |
453 | // 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 |
454 | // IMPORTANT: lua_error() is used here instead of the wrapper raise_lua_error() to circumvent what looks like a MSVC compiler bug | ||
455 | // that manifests as a crash inside ntdll!longjmp() function, in optimized builds only | ||
456 | lua_error(L_); | ||
457 | } | 481 | } |
458 | } | 482 | } |
483 | STACK_CHECK(L_, 1); | ||
459 | 484 | ||
460 | // --------------------------------------------------------- | 485 | // --------------------------------------------------------- |
461 | // 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 |
@@ -464,7 +489,9 @@ int Universe::UniverseGC(lua_State* const L_) | |||
464 | // 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 |
465 | Linda::DeleteTimerLinda(L_, std::exchange(_U->timerLinda, nullptr), PK); | 490 | Linda::DeleteTimerLinda(L_, std::exchange(_U->timerLinda, nullptr), PK); |
466 | 491 | ||
467 | _U->keepers.close(); | 492 | if (!_U->keepers.close()) { |
493 | raise_luaL_error(L_, "INTERNAL ERROR: Keepers closed more than once"); | ||
494 | } | ||
468 | 495 | ||
469 | // remove the protected allocator, if any | 496 | // remove the protected allocator, if any |
470 | _U->protectedAllocator.removeFrom(L_); | 497 | _U->protectedAllocator.removeFrom(L_); |