diff options
Diffstat (limited to '')
-rw-r--r-- | src/universe.cpp | 137 |
1 files changed, 96 insertions, 41 deletions
diff --git a/src/universe.cpp b/src/universe.cpp index 3da0801..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,6 +227,17 @@ static int luaG_provide_protected_allocator(lua_State* const L_) | |||
209 | 227 | ||
210 | // ################################################################################################# | 228 | // ################################################################################################# |
211 | 229 | ||
230 | // already called under protection of selfdestructMutex | ||
231 | void Universe::flagDanglingLanes() const | ||
232 | { | ||
233 | Lane* _lane{ selfdestructFirst }; | ||
234 | while (_lane != SELFDESTRUCT_END) { | ||
235 | _lane->flaggedAfterUniverseGC.store(true, std::memory_order_relaxed); | ||
236 | _lane = _lane->selfdestruct_next; | ||
237 | } | ||
238 | } | ||
239 | // ################################################################################################# | ||
240 | |||
212 | // called once at the creation of the universe (therefore L_ is the master Lua state everything originates from) | 241 | // 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? | 242 | // Do I need to disable this when compiling for LuaJIT to prevent issues? |
214 | void Universe::initializeAllocatorFunction(lua_State* const L_) | 243 | void Universe::initializeAllocatorFunction(lua_State* const L_) |
@@ -216,24 +245,24 @@ void Universe::initializeAllocatorFunction(lua_State* const L_) | |||
216 | // 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 |
217 | protectedAllocator.initFrom(L_); | 246 | protectedAllocator.initFrom(L_); |
218 | STACK_CHECK_START_REL(L_, 1); // L_: settings | 247 | STACK_CHECK_START_REL(L_, 1); // L_: settings |
219 | switch (luaG_getfield(L_, kIdxTop, "allocator")) { // L_: settings allocator|nil|"protected" | 248 | switch (luaW_getfield(L_, kIdxTop, "allocator")) { // L_: settings allocator|nil|"protected" |
220 | case LuaType::NIL: | 249 | case LuaType::NIL: |
221 | // nothing else to do | 250 | // nothing else to do |
222 | break; | 251 | break; |
223 | 252 | ||
224 | case LuaType::STRING: | 253 | case LuaType::STRING: |
225 | LUA_ASSERT(L_, luaG_tostring(L_, kIdxTop) == "protected"); | 254 | LUA_ASSERT(L_, luaW_tostring(L_, kIdxTop) == "protected"); |
226 | // 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 |
227 | protectedAllocator.installIn(L_); | 256 | protectedAllocator.installIn(L_); |
228 | // 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 |
229 | provideAllocator = luaG_provide_protected_allocator; | 258 | provideAllocator = luaW_provide_protected_allocator; |
230 | break; | 259 | break; |
231 | 260 | ||
232 | case LuaType::FUNCTION: | 261 | case LuaType::FUNCTION: |
233 | provideAllocator = lua_tocfunction(L_, -1); // L_: settings allocator | 262 | provideAllocator = lua_tocfunction(L_, -1); // L_: settings allocator |
234 | if (provideAllocator != nullptr) { | 263 | if (provideAllocator != nullptr) { |
235 | // make sure the function doesn't have upvalues | 264 | // make sure the function doesn't have upvalues |
236 | char const* _upname = lua_getupvalue(L_, -1, 1); // L_: settings allocator upval? | 265 | char const* _upname{ lua_getupvalue(L_, -1, 1) }; // L_: settings allocator upval? |
237 | if (_upname != nullptr) { // should be "" for C functions with upvalues if any | 266 | if (_upname != nullptr) { // should be "" for C functions with upvalues if any |
238 | raise_luaL_error(L_, "config.allocator() shouldn't have upvalues"); | 267 | raise_luaL_error(L_, "config.allocator() shouldn't have upvalues"); |
239 | } | 268 | } |
@@ -247,19 +276,19 @@ void Universe::initializeAllocatorFunction(lua_State* const L_) | |||
247 | break; | 276 | break; |
248 | 277 | ||
249 | default: // should be filtered out in lanes.lua | 278 | default: // should be filtered out in lanes.lua |
250 | 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()); |
251 | } | 280 | } |
252 | lua_pop(L_, 1); // L_: settings | 281 | lua_pop(L_, 1); // L_: settings |
253 | STACK_CHECK(L_, 1); | 282 | STACK_CHECK(L_, 1); |
254 | 283 | ||
255 | 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" |
256 | 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 |
257 | std::string_view const _allocator{ luaG_tostring(L_, kIdxTop) }; | 286 | std::string_view const _allocator{ luaW_tostring(L_, kIdxTop) }; |
287 | // use whatever the provider provides. This performs validation of what provideAllocator is giving | ||
288 | // we do this even if _allocator == "libc", to have the validation part | ||
289 | internalAllocator = resolveAndValidateAllocator(L_, "internal"); | ||
258 | if (_allocator == "libc") { | 290 | if (_allocator == "libc") { |
259 | internalAllocator = lanes::AllocatorDefinition{ libc_lua_Alloc, nullptr }; | 291 | internalAllocator = lanes::AllocatorDefinition{ libc_lua_Alloc, nullptr }; |
260 | } else { | ||
261 | // use whatever the provider provides | ||
262 | internalAllocator = resolveAllocator(L_, "internal"); | ||
263 | } | 292 | } |
264 | lua_pop(L_, 1); // L_: settings | 293 | lua_pop(L_, 1); // L_: settings |
265 | STACK_CHECK(L_, 1); | 294 | STACK_CHECK(L_, 1); |
@@ -276,7 +305,7 @@ int Universe::InitializeFinalizer(lua_State* const L_) | |||
276 | 305 | ||
277 | // make sure we are only called from the Master Lua State! | 306 | // make sure we are only called from the Master Lua State! |
278 | kUniverseFullRegKey.pushValue(L_); // L_: f U | 307 | kUniverseFullRegKey.pushValue(L_); // L_: f U |
279 | if (luaG_type(L_, kIdxTop) != LuaType::USERDATA) { | 308 | if (luaW_type(L_, kIdxTop) != LuaType::USERDATA) { |
280 | 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); |
281 | } | 310 | } |
282 | lua_pop(L_, 1); // L_: f | 311 | lua_pop(L_, 1); // L_: f |
@@ -292,8 +321,8 @@ int Universe::InitializeFinalizer(lua_State* const L_) | |||
292 | void Universe::initializeOnStateCreate(lua_State* const L_) | 321 | void Universe::initializeOnStateCreate(lua_State* const L_) |
293 | { | 322 | { |
294 | STACK_CHECK_START_REL(L_, 0); // L_: settings | 323 | STACK_CHECK_START_REL(L_, 0); // L_: settings |
295 | 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 |
296 | 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 |
297 | // store C function pointer in an internal variable | 326 | // store C function pointer in an internal variable |
298 | 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 |
299 | if (_func) { | 328 | if (_func) { |
@@ -306,7 +335,7 @@ void Universe::initializeOnStateCreate(lua_State* const L_) | |||
306 | // 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 |
307 | // when we transfer the config table in newly created Lua states | 336 | // when we transfer the config table in newly created Lua states |
308 | lua_pushnil(L_); // L_: settings on_state_create nil | 337 | lua_pushnil(L_); // L_: settings on_state_create nil |
309 | luaG_setfield(L_, StackIndex{ -3 }, kOnStateCreate); // L_: settings on_state_create | 338 | luaW_setfield(L_, StackIndex{ -3 }, kOnStateCreate); // L_: settings on_state_create |
310 | } else { | 339 | } else { |
311 | // 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) |
312 | onStateCreateFunc.emplace<uintptr_t>(std::bit_cast<uintptr_t>(kOnStateCreate.data())); | 341 | onStateCreateFunc.emplace<uintptr_t>(std::bit_cast<uintptr_t>(kOnStateCreate.data())); |
@@ -320,7 +349,7 @@ void Universe::initializeOnStateCreate(lua_State* const L_) | |||
320 | 349 | ||
321 | // ################################################################################################# | 350 | // ################################################################################################# |
322 | 351 | ||
323 | lanes::AllocatorDefinition Universe::resolveAllocator(lua_State* const L_, std::string_view const& hint_) const | 352 | lanes::AllocatorDefinition Universe::resolveAndValidateAllocator(lua_State* const L_, std::string_view const& hint_) const |
324 | { | 353 | { |
325 | lanes::AllocatorDefinition _ret{ protectedAllocator }; | 354 | lanes::AllocatorDefinition _ret{ protectedAllocator }; |
326 | if (provideAllocator == nullptr) { | 355 | if (provideAllocator == nullptr) { |
@@ -329,7 +358,7 @@ lanes::AllocatorDefinition Universe::resolveAllocator(lua_State* const L_, std:: | |||
329 | 358 | ||
330 | 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 |
331 | lua_pushcclosure(L_, provideAllocator, 0); // L_: provideAllocator() | 360 | lua_pushcclosure(L_, provideAllocator, 0); // L_: provideAllocator() |
332 | luaG_pushstring(L_, hint_); // L_: provideAllocator() "<hint>" | 361 | luaW_pushstring(L_, hint_); // L_: provideAllocator() "<hint>" |
333 | lua_call(L_, 1, 1); // L_: result | 362 | lua_call(L_, 1, 1); // L_: result |
334 | // 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) |
335 | _ret = lanes::AllocatorDefinition::Validated(L_, kIdxTop); | 364 | _ret = lanes::AllocatorDefinition::Validated(L_, kIdxTop); |
@@ -399,11 +428,12 @@ bool Universe::terminateFreeRunningLanes(lua_Duration const shutdownTimeout_, Ca | |||
399 | // ################################################################################################# | 428 | // ################################################################################################# |
400 | 429 | ||
401 | // process end: cancel any still free-running threads | 430 | // process end: cancel any still free-running threads |
431 | // as far as I can tell, this can only by called only from lua_close() | ||
402 | int Universe::UniverseGC(lua_State* const L_) | 432 | int Universe::UniverseGC(lua_State* const L_) |
403 | { | 433 | { |
404 | lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; | 434 | lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; |
405 | STACK_CHECK_START_ABS(L_, 1); | 435 | STACK_CHECK_START_ABS(L_, 1); |
406 | Universe* const _U{ luaG_tofulluserdata<Universe>(L_, StackIndex{ 1 }) }; // L_: U | 436 | Universe* const _U{ luaW_tofulluserdata<Universe>(L_, StackIndex{ 1 }) }; // L_: U |
407 | 437 | ||
408 | // attempt to terminate all lanes with increasingly stronger cancel methods | 438 | // attempt to terminate all lanes with increasingly stronger cancel methods |
409 | bool const _allLanesTerminated{ | 439 | bool const _allLanesTerminated{ |
@@ -416,27 +446,52 @@ int Universe::UniverseGC(lua_State* const L_) | |||
416 | kFinalizerRegKey.pushValue(L_); // L_: U finalizer|nil | 446 | kFinalizerRegKey.pushValue(L_); // L_: U finalizer|nil |
417 | if (!lua_isnil(L_, -1)) { | 447 | if (!lua_isnil(L_, -1)) { |
418 | lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool | 448 | lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool |
419 | // no protection. Lua rules for errors in finalizers apply normally | 449 | // no protection. Lua rules for errors in finalizers apply normally: |
420 | lua_call(L_, 1, 1); // L_: U ret|error | 450 | // Lua 5.4+: error is propagated in the warn system |
451 | // older: error is swallowed | ||
452 | lua_call(L_, 1, 1); // L_: U msg? | ||
453 | // phew, no error in finalizer, since we reached that point | ||
421 | } | 454 | } |
422 | STACK_CHECK(L_, 2); | ||
423 | 455 | ||
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 | 456 | if (lua_isnil(L_, kIdxTop)) { |
425 | bool const _throw{ luaG_tostring(L_, kIdxTop) == "throw" }; | 457 | lua_pop(L_, 1); // L_: U |
426 | lua_pop(L_, 1); // L_: U | 458 | // no finalizer, or it returned no value: push some default message on the stack, in case it is necessary |
459 | luaW_pushstring(L_, "uncooperative lanes detected at shutdown"); // L_: U "msg" | ||
460 | } | ||
461 | STACK_CHECK(L_, 2); | ||
427 | 462 | ||
428 | while (_U->selfdestructFirst != SELFDESTRUCT_END) { | 463 | { |
429 | if (_throw) { | 464 | std::lock_guard<std::mutex> _guard{ _U->selfdestructMutex }; |
430 | throw std::logic_error{ "Some lanes are still running at shutdown" }; | 465 | // now, all remaining lanes are flagged. if they crash because we remove keepers and the Universe from under them, it is their fault |
466 | bool const _detectedUncooperativeLanes{ _U->selfdestructFirst != SELFDESTRUCT_END }; | ||
467 | if (_detectedUncooperativeLanes) { | ||
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 | } | ||
431 | } else { | 478 | } else { |
432 | std::this_thread::yield(); | 479 | // we didn't use the error message, let's keep a clean stack |
480 | lua_pop(L_, 1); // L_: U | ||
433 | } | 481 | } |
434 | } | 482 | } |
483 | STACK_CHECK(L_, 1); | ||
484 | |||
485 | // --------------------------------------------------------- | ||
486 | // we don't reach that point if some lanes are still running | ||
487 | // --------------------------------------------------------- | ||
435 | 488 | ||
436 | // 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 |
437 | Linda::DeleteTimerLinda(L_, std::exchange(_U->timerLinda, nullptr), PK); | 490 | Linda::DeleteTimerLinda(L_, std::exchange(_U->timerLinda, nullptr), PK); |
438 | 491 | ||
439 | _U->keepers.close(); | 492 | if (!_U->keepers.close()) { |
493 | raise_luaL_error(L_, "INTERNAL ERROR: Keepers closed more than once"); | ||
494 | } | ||
440 | 495 | ||
441 | // remove the protected allocator, if any | 496 | // remove the protected allocator, if any |
442 | _U->protectedAllocator.removeFrom(L_); | 497 | _U->protectedAllocator.removeFrom(L_); |