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 /src | |
| parent | 823d3d570236e32bdbe270306df5602ff586c4a7 (diff) | |
| download | lanes-742cfdd2f9009ed12d7109e152df8c760851a58a.tar.gz lanes-742cfdd2f9009ed12d7109e152df8c760851a58a.tar.bz2 lanes-742cfdd2f9009ed12d7109e152df8c760851a58a.zip | |
Minor code tweaks
Diffstat (limited to 'src')
| -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 |
7 files changed, 100 insertions, 88 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_ } |
