diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-17 18:35:34 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-17 18:35:34 +0200 |
commit | 742cfdd2f9009ed12d7109e152df8c760851a58a (patch) | |
tree | 08334e478055b7a58953abcff983c2fd6e1613f1 | |
parent | 823d3d570236e32bdbe270306df5602ff586c4a7 (diff) | |
download | lanes-742cfdd2f9009ed12d7109e152df8c760851a58a.tar.gz lanes-742cfdd2f9009ed12d7109e152df8c760851a58a.tar.bz2 lanes-742cfdd2f9009ed12d7109e152df8c760851a58a.zip |
Minor code tweaks
-rw-r--r-- | src/intercopycontext.cpp | 10 | ||||
-rw-r--r-- | src/keeper.cpp | 2 | ||||
-rw-r--r-- | src/lane.cpp | 2 | ||||
-rw-r--r-- | src/lane.h | 4 | ||||
-rw-r--r-- | src/lanes.cpp | 143 | ||||
-rw-r--r-- | src/lanes.lua | 25 | ||||
-rw-r--r-- | src/universe.h | 2 | ||||
-rw-r--r-- | tests/atexit.lua | 3 |
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 | ||
477 | static void push_stack_trace(lua_State* L_, Lane::ErrorTraceLevel errorTraceLevel_, LuaError rc_, int stk_base_) | 477 | static 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_) { |
@@ -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 | // | ||
48 | class Lane | 46 | class 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 | // |
222 | LUAG_FUNC(lane_new) | 222 | LUAG_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 |
43 | local lanesMeta = {} | 43 | local lanes = {} |
44 | local 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 | ||
755 | local 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 |
756 | lanes.configure = function(settings_) | 758 | local 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 | ||
816 | lanesMeta.__index = function(_, k_) | 819 | lanesMeta.__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_] |
821 | end | 824 | end |
825 | lanes.configure = configure | ||
826 | setmetatable(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) |
826 | if core.settings then | 831 | if core.settings then |
827 | return lanes.configure() | 832 | return configure() |
828 | else | 833 | else |
829 | return lanes | 834 | return lanes |
830 | end | 835 | end |
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 @@ | |||
1 | local lanes = require "lanes" | 1 | local lanes = require "lanes" -- auto configure |
2 | lanes.configure() | ||
3 | 2 | ||
4 | -- create a free-running lane | 3 | -- create a free-running lane |
5 | 4 | ||