aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-05-17 18:35:34 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-05-17 18:35:34 +0200
commit742cfdd2f9009ed12d7109e152df8c760851a58a (patch)
tree08334e478055b7a58953abcff983c2fd6e1613f1
parent823d3d570236e32bdbe270306df5602ff586c4a7 (diff)
downloadlanes-742cfdd2f9009ed12d7109e152df8c760851a58a.tar.gz
lanes-742cfdd2f9009ed12d7109e152df8c760851a58a.tar.bz2
lanes-742cfdd2f9009ed12d7109e152df8c760851a58a.zip
Minor code tweaks
-rw-r--r--src/intercopycontext.cpp10
-rw-r--r--src/keeper.cpp2
-rw-r--r--src/lane.cpp2
-rw-r--r--src/lane.h4
-rw-r--r--src/lanes.cpp143
-rw-r--r--src/lanes.lua25
-rw-r--r--src/universe.h2
-rw-r--r--tests/atexit.lua3
8 files changed, 101 insertions, 90 deletions
diff --git a/src/intercopycontext.cpp b/src/intercopycontext.cpp
index 6a26018..e2e3d31 100644
--- a/src/intercopycontext.cpp
+++ b/src/intercopycontext.cpp
@@ -257,7 +257,7 @@ void InterCopyContext::copy_func() const
257 * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy! 257 * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy!
258 * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state! 258 * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state!
259 */ 259 */
260 int n{ 0 }; 260 int _n{ 0 };
261 { 261 {
262 InterCopyContext _c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} }; 262 InterCopyContext _c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} };
263#if LUA_VERSION_NUM >= 502 263#if LUA_VERSION_NUM >= 502
@@ -266,7 +266,7 @@ void InterCopyContext::copy_func() const
266 // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table 266 // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table
267 lua_pushglobaltable(L1); // L1: ... _G 267 lua_pushglobaltable(L1); // L1: ... _G
268#endif // LUA_VERSION_NUM 268#endif // LUA_VERSION_NUM
269 for (n = 0; (_c.name = lua_getupvalue(L1, L1_i, 1 + n)) != nullptr; ++n) { // L1: ... _G up[n] 269 for (_n = 0; (_c.name = lua_getupvalue(L1, L1_i, 1 + _n)) != nullptr; ++_n) { // L1: ... _G up[n]
270 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END(U), n, _c.name)); 270 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END(U), n, _c.name));
271#if LUA_VERSION_NUM >= 502 271#if LUA_VERSION_NUM >= 502
272 if (lua_rawequal(L1, -1, -2)) { // is the upvalue equal to the global table? 272 if (lua_rawequal(L1, -1, -2)) { // is the upvalue equal to the global table?
@@ -292,13 +292,13 @@ void InterCopyContext::copy_func() const
292 STACK_CHECK(L1, 0); 292 STACK_CHECK(L1, 0);
293 293
294 // Set upvalues (originally set to 'nil' by 'lua_load') 294 // Set upvalues (originally set to 'nil' by 'lua_load')
295 for (int const _func_index{ lua_gettop(L2) - n }; n > 0; --n) { 295 for (int const _func_index{ lua_gettop(L2) - _n }; _n > 0; --_n) {
296 char const* _rc{ lua_setupvalue(L2, _func_index, n) }; // L2: ... {cache} ... function 296 [[maybe_unused]] char const* _upname{ lua_setupvalue(L2, _func_index, _n) }; // L2: ... {cache} ... function
297 // 297 //
298 // "assigns the value at the top of the stack to the upvalue and returns its name. 298 // "assigns the value at the top of the stack to the upvalue and returns its name.
299 // It also pops the value from the stack." 299 // It also pops the value from the stack."
300 300
301 LUA_ASSERT(L1, _rc); // not having enough slots? 301 LUA_ASSERT(L1, _upname); // not having enough slots?
302 } 302 }
303 // once all upvalues have been set we are left 303 // once all upvalues have been set we are left
304 // with the function at the top of the stack // L2: ... {cache} ... function 304 // with the function at the top of the stack // L2: ... {cache} ... function
diff --git a/src/keeper.cpp b/src/keeper.cpp
index dcfb431..2f8817c 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -61,7 +61,7 @@ class keeper_fifo
61 [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, KeeperState L_) noexcept { return lua_newuserdatauv<keeper_fifo>(L_, 1); } 61 [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, KeeperState L_) noexcept { return lua_newuserdatauv<keeper_fifo>(L_, 1); }
62 // always embedded somewhere else or "in-place constructed" as a full userdata 62 // always embedded somewhere else or "in-place constructed" as a full userdata
63 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception 63 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
64 static void operator delete([[maybe_unused]] void* p_, KeeperState L_) { LUA_ASSERT(L_, !"should never be called"); } 64 static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] KeeperState L_) { LUA_ASSERT(L_, !"should never be called"); }
65 65
66 [[nodiscard]] static keeper_fifo* getPtr(lua_State* L_, int idx_) 66 [[nodiscard]] static keeper_fifo* getPtr(lua_State* L_, int idx_)
67 { 67 {
diff --git a/src/lane.cpp b/src/lane.cpp
index e321a52..3017806 100644
--- a/src/lane.cpp
+++ b/src/lane.cpp
@@ -474,7 +474,7 @@ static constexpr RegistryUniqueKey kStackTraceRegKey{ 0x3F327747CACAA904ull };
474// ########################################## Finalizer ############################################ 474// ########################################## Finalizer ############################################
475// ################################################################################################# 475// #################################################################################################
476 476
477static void push_stack_trace(lua_State* L_, Lane::ErrorTraceLevel errorTraceLevel_, LuaError rc_, int stk_base_) 477static void push_stack_trace(lua_State* L_, Lane::ErrorTraceLevel errorTraceLevel_, LuaError rc_, [[maybe_unused]] int stk_base_)
478{ 478{
479 // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry 479 // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry
480 switch (rc_) { 480 switch (rc_) {
diff --git a/src/lane.h b/src/lane.h
index 984d433..03f795b 100644
--- a/src/lane.h
+++ b/src/lane.h
@@ -42,9 +42,7 @@ static constexpr RegistryUniqueKey kLanePointerRegKey{ 0x2D8CF03FE9F0A51Aull };
42#define kLanesLibName "lanes" 42#define kLanesLibName "lanes"
43#define kLanesCoreLibName kLanesLibName ".core" 43#define kLanesCoreLibName kLanesLibName ".core"
44 44
45// NOTE: values to be changed by either thread, during execution, without 45// NOTE: values to be changed by either thread, during execution, without locking, are marked "volatile"
46// locking, are marked "volatile"
47//
48class Lane 46class Lane
49{ 47{
50 public: 48 public:
diff --git a/src/lanes.cpp b/src/lanes.cpp
index 7b730cd..afcac1d 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -221,38 +221,29 @@ LUAG_FUNC(register)
221// 221//
222LUAG_FUNC(lane_new) 222LUAG_FUNC(lane_new)
223{ 223{
224 // first 9 args: func libs priority globals package required gc_cb name error_trace_level 224 static constexpr int kFuncIdx{ 1 };
225 char const* const _libs_str{ lua_tostring(L_, 2) }; 225 static constexpr int kLibsIdx{ 2 };
226 bool const _have_priority{ !lua_isnoneornil(L_, 3) }; 226 static constexpr int kPrioIdx{ 3 };
227 int const _globals_idx{ lua_isnoneornil(L_, 4) ? 0 : 4 }; 227 static constexpr int kGlobIdx{ 4 };
228 int const _package_idx{ lua_isnoneornil(L_, 5) ? 0 : 5 }; 228 static constexpr int kPackIdx{ 5 };
229 int const _required_idx{ lua_isnoneornil(L_, 6) ? 0 : 6 }; 229 static constexpr int kRequIdx{ 6 };
230 int const _gc_cb_idx{ lua_isnoneornil(L_, 7) ? 0 : 7 }; 230 static constexpr int kGcCbIdx{ 7 };
231 int const _name_idx{ lua_isnoneornil(L_, 8) ? 0 : 8 }; 231 static constexpr int kNameIdx{ 8 };
232 int const _error_trace_level_idx{ 9 }; 232 static constexpr int kErTlIdx{ 9 };
233
234 static constexpr int kFixedArgsIdx{ 9 }; 233 static constexpr int kFixedArgsIdx{ 9 };
234
235 int const _nargs{ lua_gettop(L_) - kFixedArgsIdx }; 235 int const _nargs{ lua_gettop(L_) - kFixedArgsIdx };
236 Universe* const _U{ universe_get(L_) };
237 LUA_ASSERT(L_, _nargs >= 0); 236 LUA_ASSERT(L_, _nargs >= 0);
238 237
239 // public Lanes API accepts a generic range -3/+3 238 Universe* const _U{ universe_get(L_) };
240 // that will be remapped into the platform-specific scheduler priority scheme
241 // On some platforms, -3 is equivalent to -2 and +3 to +2
242 int const _priority{ _have_priority ? static_cast<int>(lua_tointeger(L_, 3)) : kThreadPrioDefault };
243 if (_have_priority && (_priority < kThreadPrioMin || _priority > kThreadPrioMax)) {
244 raise_luaL_error(L_, "Priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _priority);
245 }
246
247 /* --- Create and prepare the sub state --- */
248 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END(_U))); 239 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END(_U)));
249 240
250 // populate with selected libraries at the same time. 241 char const* const _libs_str{ lua_tostring(L_, kLibsIdx) };
251 lua_State* const _L2{ luaG_newstate(_U, SourceState{ L_ }, _libs_str) }; // L_: [9 args] ... L2: 242 lua_State* const _L2{ luaG_newstate(_U, SourceState{ L_ }, _libs_str) }; // L_: [fixed] ... L2:
252 STACK_CHECK_START_REL(_L2, 0); 243 STACK_CHECK_START_REL(_L2, 0);
253 244
254 // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) 245 // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread)
255 Lane* const _lane{ new (_U) Lane{ _U, _L2, static_cast<Lane::ErrorTraceLevel>(lua_tointeger(L_, _error_trace_level_idx)) } }; 246 Lane* const _lane{ new (_U) Lane{ _U, _L2, static_cast<Lane::ErrorTraceLevel>(lua_tointeger(L_, kErTlIdx)) } };
256 if (_lane == nullptr) { 247 if (_lane == nullptr) {
257 raise_luaL_error(L_, "could not create lane: out of memory"); 248 raise_luaL_error(L_, "could not create lane: out of memory");
258 } 249 }
@@ -262,19 +253,13 @@ LUAG_FUNC(lane_new)
262 private: 253 private:
263 lua_State* const L; 254 lua_State* const L;
264 Lane* lane{ nullptr }; 255 Lane* lane{ nullptr };
265 int const gc_cb_idx;
266 int const name_idx;
267 DEBUGSPEW_CODE(Universe* const U);
268 DEBUGSPEW_CODE(DebugSpewIndentScope scope); 256 DEBUGSPEW_CODE(DebugSpewIndentScope scope);
269 257
270 public: 258 public:
271 OnExit(lua_State* L_, Lane* lane_, int gc_cb_idx_, int name_idx_ DEBUGSPEW_COMMA_PARAM(Universe* U_)) 259 OnExit(lua_State* L_, Lane* lane_)
272 : L{ L_ } 260 : L{ L_ }
273 , lane{ lane_ } 261 , lane{ lane_ }
274 , gc_cb_idx{ gc_cb_idx_ } 262 DEBUGSPEW_COMMA_PARAM(scope{ lane_->U })
275 , name_idx{ name_idx_ }
276 DEBUGSPEW_COMMA_PARAM(U{ U_ })
277 DEBUGSPEW_COMMA_PARAM(scope{ U_ })
278 { 263 {
279 } 264 }
280 265
@@ -303,7 +288,7 @@ LUAG_FUNC(lane_new)
303 private: 288 private:
304 void prepareUserData() 289 void prepareUserData()
305 { 290 {
306 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: preparing lane userdata\n" INDENT_END(U))); 291 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: preparing lane userdata\n" INDENT_END(lane->U)));
307 STACK_CHECK_START_REL(L, 0); 292 STACK_CHECK_START_REL(L, 0);
308 // a Lane full userdata needs a single uservalue 293 // a Lane full userdata needs a single uservalue
309 Lane** const _ud{ lua_newuserdatauv<Lane*>(L, 1) }; // L: ... lane 294 Lane** const _ud{ lua_newuserdatauv<Lane*>(L, 1) }; // L: ... lane
@@ -320,9 +305,10 @@ LUAG_FUNC(lane_new)
320 lua_newtable(L); // L: ... lane {uv} 305 lua_newtable(L); // L: ... lane {uv}
321 306
322 // Store the gc_cb callback in the uservalue 307 // Store the gc_cb callback in the uservalue
323 if (gc_cb_idx > 0) { 308 int const _gc_cb_idx{ lua_isnoneornil(L, kGcCbIdx) ? 0 : kGcCbIdx };
309 if (_gc_cb_idx > 0) {
324 kLaneGC.pushKey(L); // L: ... lane {uv} k 310 kLaneGC.pushKey(L); // L: ... lane {uv} k
325 lua_pushvalue(L, gc_cb_idx); // L: ... lane {uv} k gc_cb 311 lua_pushvalue(L, _gc_cb_idx); // L: ... lane {uv} k gc_cb
326 lua_rawset(L, -3); // L: ... lane {uv} 312 lua_rawset(L, -3); // L: ... lane {uv}
327 } 313 }
328 314
@@ -330,19 +316,20 @@ LUAG_FUNC(lane_new)
330 316
331 lua_State* _L2{ lane->L }; 317 lua_State* _L2{ lane->L };
332 STACK_CHECK_START_REL(_L2, 0); 318 STACK_CHECK_START_REL(_L2, 0);
333 char const* const debugName{ (name_idx > 0) ? lua_tostring(L, name_idx) : nullptr }; 319 int const _name_idx{ lua_isnoneornil(L, kNameIdx) ? 0 : kNameIdx };
320 char const* const debugName{ (_name_idx > 0) ? lua_tostring(L, _name_idx) : nullptr };
334 if (debugName) 321 if (debugName)
335 { 322 {
336 if (strcmp(debugName, "auto") != 0) { 323 if (strcmp(debugName, "auto") != 0) {
337 lua_pushstring(_L2, debugName); // L: ... lane L2: "<name>" 324 lua_pushstring(_L2, debugName); // L: ... lane L2: "<name>"
338 } else { 325 } else {
339 lua_Debug ar; 326 lua_Debug ar;
340 lua_pushvalue(L, 1); // L: ... lane func 327 lua_pushvalue(L, 1); // L: ... lane func
341 lua_getinfo(L, ">S", &ar); // L: ... lane 328 lua_getinfo(L, ">S", &ar); // L: ... lane
342 lua_pushfstring(_L2, "%s:%d", ar.short_src, ar.linedefined); // L: ... lane L2: "<name>" 329 lua_pushfstring(_L2, "%s:%d", ar.short_src, ar.linedefined); // L: ... lane L2: "<name>"
343 } 330 }
344 lane->changeDebugName(-1); 331 lane->changeDebugName(-1);
345 lua_pop(_L2, 1); // L: ... lane L2: 332 lua_pop(_L2, 1); // L: ... lane L2:
346 } 333 }
347 STACK_CHECK(_L2, 0); 334 STACK_CHECK(_L2, 0);
348 STACK_CHECK(L, 1); 335 STACK_CHECK(L, 1);
@@ -355,9 +342,26 @@ LUAG_FUNC(lane_new)
355 lane->ready.count_down(); 342 lane->ready.count_down();
356 lane = nullptr; 343 lane = nullptr;
357 } 344 }
358 } onExit{ L_, _lane, _gc_cb_idx, _name_idx DEBUGSPEW_COMMA_PARAM(_U) }; 345 } onExit{ L_, _lane};
359 // launch the thread early, it will sync with a std::latch to parallelize OS thread warmup and L2 preparation 346 // launch the thread early, it will sync with a std::latch to parallelize OS thread warmup and L2 preparation
360 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END(_U))); 347 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END(_U)));
348 // public Lanes API accepts a generic range -3/+3
349 // that will be remapped into the platform-specific scheduler priority scheme
350 // On some platforms, -3 is equivalent to -2 and +3 to +2
351 int const _priority{
352 std::invoke([L = L_]() {
353 int const _prio_idx{ lua_isnoneornil(L, kPrioIdx) ? 0 : kPrioIdx };
354 if (_prio_idx == 0) {
355 return kThreadPrioDefault;
356 }
357 int const _priority{ static_cast<int>(lua_tointeger(L, _prio_idx)) };
358 if ((_priority < kThreadPrioMin || _priority > kThreadPrioMax)) {
359 raise_luaL_error(L, "Priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _priority);
360 }
361 return _priority;
362 })
363 };
364
361 _lane->startThread(_priority); 365 _lane->startThread(_priority);
362 366
363 STACK_GROW(_L2, _nargs + 3); 367 STACK_GROW(_L2, _nargs + 3);
@@ -365,6 +369,7 @@ LUAG_FUNC(lane_new)
365 STACK_CHECK_START_REL(L_, 0); 369 STACK_CHECK_START_REL(L_, 0);
366 370
367 // package 371 // package
372 int const _package_idx{ lua_isnoneornil(L_, kPackIdx) ? 0 : kPackIdx };
368 if (_package_idx != 0) { 373 if (_package_idx != 0) {
369 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END(_U))); 374 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END(_U)));
370 // when copying with mode LookupMode::LaneBody, should raise an error in case of problem, not leave it one the stack 375 // when copying with mode LookupMode::LaneBody, should raise an error in case of problem, not leave it one the stack
@@ -374,6 +379,7 @@ LUAG_FUNC(lane_new)
374 } 379 }
375 380
376 // modules to require in the target lane *before* the function is transfered! 381 // modules to require in the target lane *before* the function is transfered!
382 int const _required_idx{ lua_isnoneornil(L_, kRequIdx) ? 0 : kRequIdx };
377 if (_required_idx != 0) { 383 if (_required_idx != 0) {
378 int _nbRequired{ 1 }; 384 int _nbRequired{ 1 };
379 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END(_U))); 385 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END(_U)));
@@ -383,8 +389,8 @@ LUAG_FUNC(lane_new)
383 raise_luaL_error(L_, "expected required module list as a table, got %s", luaL_typename(L_, _required_idx)); 389 raise_luaL_error(L_, "expected required module list as a table, got %s", luaL_typename(L_, _required_idx));
384 } 390 }
385 391
386 lua_pushnil(L_); // L_: [8 args] args... nil L2: 392 lua_pushnil(L_); // L_: [fixed] args... nil L2:
387 while (lua_next(L_, _required_idx) != 0) { // L_: [8 args] args... n "modname" L2: 393 while (lua_next(L_, _required_idx) != 0) { // L_: [fixed] args... n "modname" L2:
388 if (lua_type(L_, -1) != LUA_TSTRING || lua_type(L_, -2) != LUA_TNUMBER || lua_tonumber(L_, -2) != _nbRequired) { 394 if (lua_type(L_, -1) != LUA_TSTRING || lua_type(L_, -2) != LUA_TNUMBER || lua_tonumber(L_, -2) != _nbRequired) {
389 raise_luaL_error(L_, "required module list should be a list of strings"); 395 raise_luaL_error(L_, "required module list should be a list of strings");
390 } else { 396 } else {
@@ -394,34 +400,35 @@ LUAG_FUNC(lane_new)
394 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END(_U), name)); 400 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END(_U), name));
395 401
396 // require the module in the target lane 402 // require the module in the target lane
397 lua_getglobal(_L2, "require"); // L_: [8 args] args... n "modname" L2: require()? 403 lua_getglobal(_L2, "require"); // L_: [fixed] args... n "modname" L2: require()?
398 if (lua_isnil(_L2, -1)) { 404 if (lua_isnil(_L2, -1)) {
399 lua_pop(_L2, 1); // L_: [8 args] args... n "modname" L2: 405 lua_pop(_L2, 1); // L_: [fixed] args... n "modname" L2:
400 raise_luaL_error(L_, "cannot pre-require modules without loading 'package' library first"); 406 raise_luaL_error(L_, "cannot pre-require modules without loading 'package' library first");
401 } else { 407 } else {
402 lua_pushlstring(_L2, name, len); // L_: [8 args] args... n "modname" L2: require() name 408 lua_pushlstring(_L2, name, len); // L_: [fixed] args... n "modname" L2: require() name
403 if (lua_pcall(_L2, 1, 1, 0) != LUA_OK) { // L_: [8 args] args... n "modname" L2: ret/errcode 409 if (lua_pcall(_L2, 1, 1, 0) != LUA_OK) { // L_: [fixed] args... n "modname" L2: ret/errcode
404 // propagate error to main state if any 410 // propagate error to main state if any
405 InterCopyContext _c{ _U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} }; 411 InterCopyContext _c{ _U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} };
406 std::ignore = _c.inter_move(1); // L_: [8 args] args... n "modname" error L2: 412 std::ignore = _c.inter_move(1); // L_: [fixed] args... n "modname" error L2:
407 raise_lua_error(L_); 413 raise_lua_error(L_);
408 } 414 }
409 // here the module was successfully required // L_: [8 args] args... n "modname" L2: ret 415 // here the module was successfully required // L_: [fixed] args... n "modname" L2: ret
410 // after requiring the module, register the functions it exported in our name<->function database 416 // after requiring the module, register the functions it exported in our name<->function database
411 populate_func_lookup_table(_L2, -1, name); 417 populate_func_lookup_table(_L2, -1, name);
412 lua_pop(_L2, 1); // L_: [8 args] args... n "modname" L2: 418 lua_pop(_L2, 1); // L_: [fixed] args... n "modname" L2:
413 } 419 }
414 } 420 }
415 lua_pop(L_, 1); // L_: func libs priority globals package required gc_cb [... args ...] n 421 lua_pop(L_, 1); // L_: [fixed] args... n L2:
416 ++_nbRequired; 422 ++_nbRequired;
417 } // L_: [8 args] args... 423 } // L_: [fixed] args...
418 } 424 }
419 STACK_CHECK(L_, 0); 425 STACK_CHECK(L_, 0);
420 STACK_CHECK(_L2, 0); // L_: [8 args] args... L2: 426 STACK_CHECK(_L2, 0); // L_: [fixed] args... L2:
421 427
422 // Appending the specified globals to the global environment 428 // Appending the specified globals to the global environment
423 // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... 429 // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed...
424 // 430 //
431 int const _globals_idx{ lua_isnoneornil(L_, kGlobIdx) ? 0 : kGlobIdx };
425 if (_globals_idx != 0) { 432 if (_globals_idx != 0) {
426 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer globals\n" INDENT_END(_U))); 433 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer globals\n" INDENT_END(_U)));
427 if (!lua_istable(L_, _globals_idx)) { 434 if (!lua_istable(L_, _globals_idx)) {
@@ -429,37 +436,37 @@ LUAG_FUNC(lane_new)
429 } 436 }
430 437
431 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); 438 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U });
432 lua_pushnil(L_); // L_: [8 args] args... nil L2: 439 lua_pushnil(L_); // L_: [fixed] args... nil L2:
433 // Lua 5.2 wants us to push the globals table on the stack 440 // Lua 5.2 wants us to push the globals table on the stack
434 InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; 441 InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
435 lua_pushglobaltable(_L2); // L_: [8 args] args... nil L2: _G 442 lua_pushglobaltable(_L2); // L_: [fixed] args... nil L2: _G
436 while (lua_next(L_, _globals_idx)) { // L_: [8 args] args... k v L2: _G 443 while (lua_next(L_, _globals_idx)) { // L_: [fixed] args... k v L2: _G
437 std::ignore = _c.inter_copy(2); // L_: [8 args] args... k v L2: _G k v 444 std::ignore = _c.inter_copy(2); // L_: [fixed] args... k v L2: _G k v
438 // assign it in L2's globals table 445 // assign it in L2's globals table
439 lua_rawset(_L2, -3); // L_: [8 args] args... k v L2: _G 446 lua_rawset(_L2, -3); // L_: [fixed] args... k v L2: _G
440 lua_pop(L_, 1); // L_: [8 args] args... k 447 lua_pop(L_, 1); // L_: [fixed] args... k
441 } // L_: [8 args] args... 448 } // L_: [fixed] args...
442 lua_pop(_L2, 1); // L_: [8 args] args... L2: 449 lua_pop(_L2, 1); // L_: [fixed] args... L2:
443 } 450 }
444 STACK_CHECK(L_, 0); 451 STACK_CHECK(L_, 0);
445 STACK_CHECK(_L2, 0); 452 STACK_CHECK(_L2, 0);
446 453
447 // Lane main function 454 // Lane main function
448 int const errorHandlerCount{ _lane->pushErrorHandler() }; // L2: eh? 455 [[maybe_unused]] int const errorHandlerCount{ _lane->pushErrorHandler() }; // L_: [fixed] args... L2: eh?
449 LuaType const _func_type{ lua_type_as_enum(L_, 1) }; 456 LuaType const _func_type{ lua_type_as_enum(L_, kFuncIdx) };
450 if (_func_type == LuaType::FUNCTION) { 457 if (_func_type == LuaType::FUNCTION) {
451 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END(_U))); 458 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END(_U)));
452 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); 459 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U });
453 lua_pushvalue(L_, 1); // L_: [8 args] args... func L2: eh? 460 lua_pushvalue(L_, kFuncIdx); // L_: [fixed] args... func L2: eh?
454 InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; 461 InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
455 InterCopyResult const _res{ _c.inter_move(1) }; // L_: [8 args] args... L2: eh? func 462 InterCopyResult const _res{ _c.inter_move(1) }; // L_: [fixed] args... L2: eh? func
456 if (_res != InterCopyResult::Success) { 463 if (_res != InterCopyResult::Success) {
457 raise_luaL_error(L_, "tried to copy unsupported types"); 464 raise_luaL_error(L_, "tried to copy unsupported types");
458 } 465 }
459 } else if (_func_type == LuaType::STRING) { 466 } else if (_func_type == LuaType::STRING) {
460 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: compile lane body\n" INDENT_END(_U))); 467 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: compile lane body\n" INDENT_END(_U)));
461 // compile the string 468 // compile the string
462 if (luaL_loadstring(_L2, lua_tostring(L_, 1)) != 0) { // L_: [8 args] args... L2: eh? func 469 if (luaL_loadstring(_L2, lua_tostring(L_, kFuncIdx)) != 0) { // L_: [fixed] args... L2: eh? func
463 raise_luaL_error(L_, "error when parsing lane function code"); 470 raise_luaL_error(L_, "error when parsing lane function code");
464 } 471 }
465 } else { 472 } else {
@@ -474,7 +481,7 @@ LUAG_FUNC(lane_new)
474 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END(_U))); 481 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END(_U)));
475 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); 482 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U });
476 InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; 483 InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
477 InterCopyResult const res{ _c.inter_move(_nargs) }; // L_: [8 args] L2: eh? func args... 484 InterCopyResult const res{ _c.inter_move(_nargs) }; // L_: [fixed] L2: eh? func args...
478 if (res != InterCopyResult::Success) { 485 if (res != InterCopyResult::Success) {
479 raise_luaL_error(L_, "tried to copy unsupported types"); 486 raise_luaL_error(L_, "tried to copy unsupported types");
480 } 487 }
@@ -483,12 +490,14 @@ LUAG_FUNC(lane_new)
483 LUA_ASSERT(L_, lua_gettop(L_) == kFixedArgsIdx); 490 LUA_ASSERT(L_, lua_gettop(L_) == kFixedArgsIdx);
484 491
485 // Store 'lane' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive). 492 // Store 'lane' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive).
486 kLanePointerRegKey.setValue(_L2, [lane = _lane](lua_State* L_) { lua_pushlightuserdata(L_, lane); });// L_: [8 args] L2: eh? func args... 493 kLanePointerRegKey.setValue(
494 _L2, [lane = _lane](lua_State* L_) { lua_pushlightuserdata(L_, lane); } // L_: [fixed] L2: eh? func args...
495 );
487 STACK_CHECK(_L2, errorHandlerCount + 1 + _nargs); 496 STACK_CHECK(_L2, errorHandlerCount + 1 + _nargs);
488 497
489 STACK_CHECK_RESET_REL(L_, 0); 498 STACK_CHECK_RESET_REL(L_, 0);
490 // all went well, the lane's thread can start working 499 // all went well, the lane's thread can start working
491 onExit.success(); // L_: [8 args] lane L2: <living its own life> 500 onExit.success(); // L_: [fixed] lane L2: <living its own life>
492 // we should have the lane userdata on top of the stack 501 // we should have the lane userdata on top of the stack
493 STACK_CHECK(L_, 1); 502 STACK_CHECK(L_, 1);
494 return 1; 503 return 1;
diff --git a/src/lanes.lua b/src/lanes.lua
index ff5fdf3..e75e840 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -40,8 +40,7 @@ local core = require "lanes.core"
40-- Lua 5.2: module() is gone 40-- Lua 5.2: module() is gone
41-- almost everything module() does is done by require() anyway 41-- almost everything module() does is done by require() anyway
42-- -> simply create a table, populate it, return it, and be done 42-- -> simply create a table, populate it, return it, and be done
43local lanesMeta = {} 43local lanes = {}
44local lanes = setmetatable({}, lanesMeta)
45 44
46-- ################################################################################################# 45-- #################################################################################################
47 46
@@ -752,8 +751,15 @@ end -- genatomic
752-- ################################## lanes.configure() ############################################ 751-- ################################## lanes.configure() ############################################
753-- ################################################################################################# 752-- #################################################################################################
754 753
754-- start with a protected metatable
755local lanesMeta = { __metatable = "Lanes" }
756
755-- this function is available in the public interface until it is called, after which it disappears 757-- this function is available in the public interface until it is called, after which it disappears
756lanes.configure = function(settings_) 758local configure = function(settings_)
759 -- Configure called so remove metatable from lanes
760 lanesMeta.__metatable = nil -- unprotect the metatable
761 setmetatable(lanes, nil) -- remove it
762 lanes.configure = nil -- no need to call configure() ever again
757 763
758 -- This check is for sublanes requiring Lanes 764 -- This check is for sublanes requiring Lanes
759 -- 765 --
@@ -762,8 +768,6 @@ lanes.configure = function(settings_)
762 if not string then 768 if not string then
763 error("To use 'lanes', you will also need to have 'string' available.", 2) 769 error("To use 'lanes', you will also need to have 'string' available.", 2)
764 end 770 end
765 -- Configure called so remove metatable from lanes
766 setmetatable(lanes, nil)
767 771
768 -- now we can configure Lanes core 772 -- now we can configure Lanes core
769 local settings = core.configure and core.configure(params_checker(settings_)) or core.settings 773 local settings = core.configure and core.configure(params_checker(settings_)) or core.settings
@@ -800,7 +804,6 @@ lanes.configure = function(settings_)
800 lanes.set_thread_priority = core.set_thread_priority 804 lanes.set_thread_priority = core.set_thread_priority
801 lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false 805 lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false
802 806
803 lanes.configure = nil -- no need to call configure() ever again
804 lanes.gen = gen 807 lanes.gen = gen
805 lanes.genatomic = genatomic 808 lanes.genatomic = genatomic
806 lanes.genlock = genlock 809 lanes.genlock = genlock
@@ -813,18 +816,20 @@ end -- lanes.configure
813 816
814-- ################################################################################################# 817-- #################################################################################################
815 818
816lanesMeta.__index = function(_, k_) 819lanesMeta.__index = function(lanes_, k_)
817 -- This is called when some functionality is accessed without calling configure() 820 -- This is called when some functionality is accessed without calling configure()
818 lanes.configure() -- initialize with default settings 821 configure() -- initialize with default settings
819 -- Access the required key 822 -- Access the required key
820 return lanes[k_] 823 return lanes_[k_]
821end 824end
825lanes.configure = configure
826setmetatable(lanes, lanesMeta)
822 827
823-- ################################################################################################# 828-- #################################################################################################
824 829
825-- no need to force calling configure() manually excepted the first time (other times will reuse the internally stored settings of the first call) 830-- no need to force calling configure() manually excepted the first time (other times will reuse the internally stored settings of the first call)
826if core.settings then 831if core.settings then
827 return lanes.configure() 832 return configure()
828else 833else
829 return lanes 834 return lanes
830end 835end
diff --git a/src/universe.h b/src/universe.h
index 4ed1776..f5327a6 100644
--- a/src/universe.h
+++ b/src/universe.h
@@ -36,7 +36,7 @@ class AllocatorDefinition
36 [[nodiscard]] static void* operator new(size_t size_, lua_State* L_) noexcept { return lua_newuserdatauv(L_, size_, 0); } 36 [[nodiscard]] static void* operator new(size_t size_, lua_State* L_) noexcept { return lua_newuserdatauv(L_, size_, 0); }
37 // always embedded somewhere else or "in-place constructed" as a full userdata 37 // always embedded somewhere else or "in-place constructed" as a full userdata
38 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception 38 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
39 static void operator delete([[maybe_unused]] void* p_, lua_State* L_) { LUA_ASSERT(L_, !"should never be called"); } 39 static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] lua_State* L_) { LUA_ASSERT(L_, !"should never be called"); }
40 40
41 AllocatorDefinition(lua_Alloc allocF_, void* allocUD_) noexcept 41 AllocatorDefinition(lua_Alloc allocF_, void* allocUD_) noexcept
42 : allocF{ allocF_ } 42 : allocF{ allocF_ }
diff --git a/tests/atexit.lua b/tests/atexit.lua
index 31e1199..9fc3070 100644
--- a/tests/atexit.lua
+++ b/tests/atexit.lua
@@ -1,5 +1,4 @@
1local lanes = require "lanes" 1local lanes = require "lanes" -- auto configure
2lanes.configure()
3 2
4-- create a free-running lane 3-- create a free-running lane
5 4