diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-17 10:17:20 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-17 10:17:20 +0200 |
| commit | 3dd36a796c9258f552c893d225e0d9cc0f576a6d (patch) | |
| tree | 2417885e168dfc490413d752f2e01e629ea61a83 /src | |
| parent | be7e5a99aa926d1a823031701453f9aa5c438ef6 (diff) | |
| download | lanes-3dd36a796c9258f552c893d225e0d9cc0f576a6d.tar.gz lanes-3dd36a796c9258f552c893d225e0d9cc0f576a6d.tar.bz2 lanes-3dd36a796c9258f552c893d225e0d9cc0f576a6d.zip | |
Strong typed Lua thread status
Diffstat (limited to 'src')
| -rw-r--r-- | src/compat.h | 28 | ||||
| -rw-r--r-- | src/lane.cpp | 74 |
2 files changed, 64 insertions, 38 deletions
diff --git a/src/compat.h b/src/compat.h index bf22f10..0e95cde 100644 --- a/src/compat.h +++ b/src/compat.h | |||
| @@ -24,6 +24,8 @@ extern "C" | |||
| 24 | #define LUA_JITLIBNAME "jit" | 24 | #define LUA_JITLIBNAME "jit" |
| 25 | #endif // LUA_JITLIBNAME | 25 | #endif // LUA_JITLIBNAME |
| 26 | 26 | ||
| 27 | #include <cassert> | ||
| 28 | |||
| 27 | // code is now preferring Lua 5.4 API | 29 | // code is now preferring Lua 5.4 API |
| 28 | 30 | ||
| 29 | // ################################################################################################# | 31 | // ################################################################################################# |
| @@ -160,6 +162,8 @@ void* lua_newuserdatauv(lua_State* L_, size_t sz_, int nuvalue_); | |||
| 160 | int lua_getiuservalue(lua_State* L_, int idx_, int n_); | 162 | int lua_getiuservalue(lua_State* L_, int idx_, int n_); |
| 161 | int lua_setiuservalue(lua_State* L_, int idx_, int n_); | 163 | int lua_setiuservalue(lua_State* L_, int idx_, int n_); |
| 162 | 164 | ||
| 165 | #define LUA_GNAME "_G" | ||
| 166 | |||
| 163 | #endif // LUA_VERSION_NUM < 504 | 167 | #endif // LUA_VERSION_NUM < 504 |
| 164 | 168 | ||
| 165 | // ################################################################################################# | 169 | // ################################################################################################# |
| @@ -197,7 +201,7 @@ inline int luaL_optint(lua_State* L_, int n_, lua_Integer d_) | |||
| 197 | 201 | ||
| 198 | // ################################################################################################# | 202 | // ################################################################################################# |
| 199 | 203 | ||
| 200 | // a wrapper over lua types to see them easier in a debugger | 204 | // a strong-typed wrapper over lua types to see them easier in a debugger |
| 201 | enum class LuaType | 205 | enum class LuaType |
| 202 | { | 206 | { |
| 203 | NONE = LUA_TNONE, | 207 | NONE = LUA_TNONE, |
| @@ -222,4 +226,26 @@ inline char const* lua_typename(lua_State* L_, LuaType t_) | |||
| 222 | return lua_typename(L_, static_cast<int>(t_)); | 226 | return lua_typename(L_, static_cast<int>(t_)); |
| 223 | } | 227 | } |
| 224 | 228 | ||
| 229 | // ################################################################################################# | ||
| 230 | |||
| 231 | // a strong-typed wrapper over lua error codes to see them easier in a debugger | ||
| 232 | enum class LuaError | ||
| 233 | { | ||
| 234 | OK = LUA_OK, | ||
| 235 | YIELD = LUA_YIELD, | ||
| 236 | ERRRUN = LUA_ERRRUN, | ||
| 237 | ERRSYNTAX = LUA_ERRSYNTAX, | ||
| 238 | ERRMEM = LUA_ERRMEM, | ||
| 239 | ERRGCMM = LUA_ERRGCMM, // pre-5.4 | ||
| 240 | ERRERR = LUA_ERRERR | ||
| 241 | }; | ||
| 242 | |||
| 243 | inline constexpr LuaError ToLuaError(int rc_) | ||
| 244 | { | ||
| 245 | assert(rc_ == LUA_OK || rc_ == LUA_YIELD || rc_ == LUA_ERRRUN || rc_ == LUA_ERRSYNTAX || rc_ == LUA_ERRMEM || rc_ == LUA_ERRGCMM || rc_ == LUA_ERRERR); | ||
| 246 | return static_cast<LuaError>(rc_); | ||
| 247 | } | ||
| 248 | |||
| 249 | // ################################################################################################# | ||
| 250 | |||
| 225 | LuaType luaG_getmodule(lua_State* L_, char const* name_); | 251 | LuaType luaG_getmodule(lua_State* L_, char const* name_); |
diff --git a/src/lane.cpp b/src/lane.cpp index 4c33afb..57e3454 100644 --- a/src/lane.cpp +++ b/src/lane.cpp | |||
| @@ -346,20 +346,20 @@ LUAG_FUNC(thread_index) | |||
| 346 | // LUA_ERRERR doesn't have the same value | 346 | // LUA_ERRERR doesn't have the same value |
| 347 | struct errcode_name | 347 | struct errcode_name |
| 348 | { | 348 | { |
| 349 | int code; | 349 | LuaError code; |
| 350 | char const* name; | 350 | char const* name; |
| 351 | }; | 351 | }; |
| 352 | 352 | ||
| 353 | static struct errcode_name s_errcodes[] = { | 353 | static struct errcode_name s_errcodes[] = { |
| 354 | { LUA_OK, "LUA_OK" }, | 354 | { LuaError::OK, "LUA_OK" }, |
| 355 | { LUA_YIELD, "LUA_YIELD" }, | 355 | { LuaError::YIELD, "LUA_YIELD" }, |
| 356 | { LUA_ERRRUN, "LUA_ERRRUN" }, | 356 | { LuaError::ERRRUN, "LUA_ERRRUN" }, |
| 357 | { LUA_ERRSYNTAX, "LUA_ERRSYNTAX" }, | 357 | { LuaError::ERRSYNTAX, "LUA_ERRSYNTAX" }, |
| 358 | { LUA_ERRMEM, "LUA_ERRMEM" }, | 358 | { LuaError::ERRMEM, "LUA_ERRMEM" }, |
| 359 | { LUA_ERRGCMM, "LUA_ERRGCMM" }, | 359 | { LuaError::ERRGCMM, "LUA_ERRGCMM" }, |
| 360 | { LUA_ERRERR, "LUA_ERRERR" }, | 360 | { LuaError::ERRERR, "LUA_ERRERR" }, |
| 361 | }; | 361 | }; |
| 362 | static char const* get_errcode_name(int _code) | 362 | static char const* get_errcode_name(LuaError _code) |
| 363 | { | 363 | { |
| 364 | for (errcode_name const& _entry : s_errcodes) { | 364 | for (errcode_name const& _entry : s_errcodes) { |
| 365 | if (_entry.code == _code) { | 365 | if (_entry.code == _code) { |
| @@ -464,14 +464,14 @@ static constexpr RegistryUniqueKey kStackTraceRegKey{ 0x3F327747CACAA904ull }; | |||
| 464 | // ########################################## Finalizer ############################################ | 464 | // ########################################## Finalizer ############################################ |
| 465 | // ################################################################################################# | 465 | // ################################################################################################# |
| 466 | 466 | ||
| 467 | static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) | 467 | static void push_stack_trace(lua_State* L_, LuaError rc_, int stk_base_) |
| 468 | { | 468 | { |
| 469 | // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry | 469 | // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry |
| 470 | switch (rc_) { | 470 | switch (rc_) { |
| 471 | case LUA_OK: // no error, body return values are on the stack | 471 | case LuaError::OK: // no error, body return values are on the stack |
| 472 | break; | 472 | break; |
| 473 | 473 | ||
| 474 | case LUA_ERRRUN: // cancellation or a runtime error | 474 | case LuaError::ERRRUN: // cancellation or a runtime error |
| 475 | #if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler | 475 | #if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler |
| 476 | { | 476 | { |
| 477 | STACK_CHECK_START_REL(L_, 0); | 477 | STACK_CHECK_START_REL(L_, 0); |
| @@ -491,8 +491,8 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) | |||
| 491 | [[fallthrough]]; // fall through if not ERROR_FULL_STACK | 491 | [[fallthrough]]; // fall through if not ERROR_FULL_STACK |
| 492 | #endif // !ERROR_FULL_STACK | 492 | #endif // !ERROR_FULL_STACK |
| 493 | 493 | ||
| 494 | case LUA_ERRMEM: // memory allocation error (handler not called) | 494 | case LuaError::ERRMEM: // memory allocation error (handler not called) |
| 495 | case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition) | 495 | case LuaError::ERRERR: // error while running the error handler (if any, for example an out-of-memory condition) |
| 496 | default: | 496 | default: |
| 497 | // we should have a single value which is either a string (the error message) or kCancelError | 497 | // we should have a single value which is either a string (the error message) or kCancelError |
| 498 | LUA_ASSERT(L_, (lua_gettop(L_) == stk_base_) && ((lua_type(L_, stk_base_) == LUA_TSTRING) || kCancelError.equals(L_, stk_base_))); | 498 | LUA_ASSERT(L_, (lua_gettop(L_) == stk_base_) && ((lua_type(L_, stk_base_) == LUA_TSTRING) || kCancelError.equals(L_, stk_base_))); |
| @@ -515,12 +515,12 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) | |||
| 515 | // TBD: should we add stack trace on failing finalizer, wouldn't be hard.. | 515 | // TBD: should we add stack trace on failing finalizer, wouldn't be hard.. |
| 516 | // | 516 | // |
| 517 | 517 | ||
| 518 | [[nodiscard]] static int run_finalizers(lua_State* L_, int lua_rc_) | 518 | [[nodiscard]] static LuaError run_finalizers(lua_State* L_, LuaError lua_rc_) |
| 519 | { | 519 | { |
| 520 | kFinalizerRegKey.pushValue(L_); // L_: ... finalizers? | 520 | kFinalizerRegKey.pushValue(L_); // L_: ... finalizers? |
| 521 | if (lua_isnil(L_, -1)) { | 521 | if (lua_isnil(L_, -1)) { |
| 522 | lua_pop(L_, 1); | 522 | lua_pop(L_, 1); |
| 523 | return 0; // no finalizers | 523 | return LuaError::OK; // no finalizers |
| 524 | } | 524 | } |
| 525 | 525 | ||
| 526 | STACK_GROW(L_, 5); | 526 | STACK_GROW(L_, 5); |
| @@ -528,13 +528,13 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) | |||
| 528 | int const _finalizers_index{ lua_gettop(L_) }; | 528 | int const _finalizers_index{ lua_gettop(L_) }; |
| 529 | int const _err_handler_index{ ERROR_FULL_STACK ? (lua_pushcfunction(L_, lane_error), lua_gettop(L_)) : 0 }; | 529 | int const _err_handler_index{ ERROR_FULL_STACK ? (lua_pushcfunction(L_, lane_error), lua_gettop(L_)) : 0 }; |
| 530 | 530 | ||
| 531 | int rc{ LUA_OK }; | 531 | LuaError _rc{ LuaError::OK }; |
| 532 | for (int n = static_cast<int>(lua_rawlen(L_, _finalizers_index)); n > 0; --n) { | 532 | for (int _n = static_cast<int>(lua_rawlen(L_, _finalizers_index)); _n > 0; --_n) { |
| 533 | int args = 0; | 533 | int _args{ 0 }; |
| 534 | lua_pushinteger(L_, n); // L_: ... finalizers lane_error n | 534 | lua_pushinteger(L_, _n); // L_: ... finalizers lane_error n |
| 535 | lua_rawget(L_, _finalizers_index); // L_: ... finalizers lane_error finalizer | 535 | lua_rawget(L_, _finalizers_index); // L_: ... finalizers lane_error finalizer |
| 536 | LUA_ASSERT(L_, lua_isfunction(L_, -1)); | 536 | LUA_ASSERT(L_, lua_isfunction(L_, -1)); |
| 537 | if (lua_rc_ != LUA_OK) { // we have an error message and an optional stack trace at the bottom of the stack | 537 | if (lua_rc_ != LuaError::OK) { // we have an error message and an optional stack trace at the bottom of the stack |
| 538 | LUA_ASSERT(L_, _finalizers_index == 2 || _finalizers_index == 3); | 538 | LUA_ASSERT(L_, _finalizers_index == 2 || _finalizers_index == 3); |
| 539 | // char const* err_msg = lua_tostring(L_, 1); | 539 | // char const* err_msg = lua_tostring(L_, 1); |
| 540 | lua_pushvalue(L_, 1); // L_: ... finalizers lane_error finalizer err_msg | 540 | lua_pushvalue(L_, 1); // L_: ... finalizers lane_error finalizer err_msg |
| @@ -542,13 +542,13 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) | |||
| 542 | if (_finalizers_index == 3) { | 542 | if (_finalizers_index == 3) { |
| 543 | lua_pushvalue(L_, 2); // L_: ... finalizers lane_error finalizer err_msg stack_trace | 543 | lua_pushvalue(L_, 2); // L_: ... finalizers lane_error finalizer err_msg stack_trace |
| 544 | } | 544 | } |
| 545 | args = _finalizers_index - 1; | 545 | _args = _finalizers_index - 1; |
| 546 | } | 546 | } |
| 547 | 547 | ||
| 548 | // if no error from the main body, finalizer doesn't receive any argument, else it gets the error message and optional stack trace | 548 | // if no error from the main body, finalizer doesn't receive any argument, else it gets the error message and optional stack trace |
| 549 | rc = lua_pcall(L_, args, 0, _err_handler_index); // L_: ... finalizers lane_error err_msg2? | 549 | _rc = ToLuaError(lua_pcall(L_, _args, 0, _err_handler_index)); // L_: ... finalizers lane_error err_msg2? |
| 550 | if (rc != LUA_OK) { | 550 | if (_rc != LuaError::OK) { |
| 551 | push_stack_trace(L_, rc, lua_gettop(L_)); // L_: ... finalizers lane_error err_msg2? trace | 551 | push_stack_trace(L_, _rc, lua_gettop(L_)); // L_: ... finalizers lane_error err_msg2? trace |
| 552 | // If one finalizer fails, don't run the others. Return this | 552 | // If one finalizer fails, don't run the others. Return this |
| 553 | // as the 'real' error, replacing what we could have had (or not) | 553 | // as the 'real' error, replacing what we could have had (or not) |
| 554 | // from the actual code. | 554 | // from the actual code. |
| @@ -557,20 +557,20 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) | |||
| 557 | // no error, proceed to next finalizer // L_: ... finalizers lane_error | 557 | // no error, proceed to next finalizer // L_: ... finalizers lane_error |
| 558 | } | 558 | } |
| 559 | 559 | ||
| 560 | if (rc != LUA_OK) { | 560 | if (_rc != LuaError::OK) { |
| 561 | // ERROR_FULL_STACK accounts for the presence of lane_error on the stack | 561 | // ERROR_FULL_STACK accounts for the presence of lane_error on the stack |
| 562 | int const nb_err_slots{ lua_gettop(L_) - _finalizers_index - ERROR_FULL_STACK }; | 562 | int const _nb_err_slots{ lua_gettop(L_) - _finalizers_index - ERROR_FULL_STACK }; |
| 563 | // a finalizer generated an error, this is what we leave of the stack | 563 | // a finalizer generated an error, this is what we leave of the stack |
| 564 | for (int n = nb_err_slots; n > 0; --n) { | 564 | for (int _n = _nb_err_slots; _n > 0; --_n) { |
| 565 | lua_replace(L_, n); | 565 | lua_replace(L_, _n); |
| 566 | } | 566 | } |
| 567 | // leave on the stack only the error and optional stack trace produced by the error in the finalizer | 567 | // leave on the stack only the error and optional stack trace produced by the error in the finalizer |
| 568 | lua_settop(L_, nb_err_slots); // L_: ... lane_error trace | 568 | lua_settop(L_, _nb_err_slots); // L_: ... lane_error trace |
| 569 | } else { // no error from the finalizers, make sure only the original return values from the lane body remain on the stack | 569 | } else { // no error from the finalizers, make sure only the original return values from the lane body remain on the stack |
| 570 | lua_settop(L_, _finalizers_index - 1); | 570 | lua_settop(L_, _finalizers_index - 1); |
| 571 | } | 571 | } |
| 572 | 572 | ||
| 573 | return rc; | 573 | return _rc; |
| 574 | } | 574 | } |
| 575 | 575 | ||
| 576 | // ################################################################################################# | 576 | // ################################################################################################# |
| @@ -627,7 +627,7 @@ static void lane_main(Lane* lane_) | |||
| 627 | lua_State* const _L{ lane_->L }; | 627 | lua_State* const _L{ lane_->L }; |
| 628 | // wait until the launching thread has finished preparing L | 628 | // wait until the launching thread has finished preparing L |
| 629 | lane_->ready.wait(); | 629 | lane_->ready.wait(); |
| 630 | int _rc{ LUA_ERRRUN }; | 630 | LuaError _rc{ LuaError::ERRRUN }; |
| 631 | if (lane_->status == Lane::Pending) { // nothing wrong happened during preparation, we can work | 631 | if (lane_->status == Lane::Pending) { // nothing wrong happened during preparation, we can work |
| 632 | // At this point, the lane function and arguments are on the stack | 632 | // At this point, the lane function and arguments are on the stack |
| 633 | int const nargs{ lua_gettop(_L) - 1 }; | 633 | int const nargs{ lua_gettop(_L) - 1 }; |
| @@ -662,7 +662,7 @@ static void lane_main(Lane* lane_) | |||
| 662 | lua_insert(_L, 1); // L: handler func args | 662 | lua_insert(_L, 1); // L: handler func args |
| 663 | #endif // L: ERROR_FULL_STACK | 663 | #endif // L: ERROR_FULL_STACK |
| 664 | 664 | ||
| 665 | _rc = lua_pcall(_L, nargs, LUA_MULTRET, ERROR_FULL_STACK); // L: retvals|err | 665 | _rc = ToLuaError(lua_pcall(_L, nargs, LUA_MULTRET, ERROR_FULL_STACK)); // L: retvals|err |
| 666 | 666 | ||
| 667 | #if ERROR_FULL_STACK | 667 | #if ERROR_FULL_STACK |
| 668 | lua_remove(_L, 1); // L: retvals|error | 668 | lua_remove(_L, 1); // L: retvals|error |
| @@ -674,9 +674,9 @@ static void lane_main(Lane* lane_) | |||
| 674 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END(_U), _L, get_errcode_name(_rc), kCancelError.equals(_L, 1) ? "cancelled" : lua_typename(_L, lua_type(_L, 1)))); | 674 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END(_U), _L, get_errcode_name(_rc), kCancelError.equals(_L, 1) ? "cancelled" : lua_typename(_L, lua_type(_L, 1)))); |
| 675 | // Call finalizers, if the script has set them up. | 675 | // Call finalizers, if the script has set them up. |
| 676 | // | 676 | // |
| 677 | int _rc2{ run_finalizers(_L, _rc) }; | 677 | LuaError const _rc2{ run_finalizers(_L, _rc) }; |
| 678 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END(_U), _L, get_errcode_name(_rc2))); | 678 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END(_U), _L, get_errcode_name(_rc2))); |
| 679 | if (_rc2 != LUA_OK) { // Error within a finalizer! | 679 | if (_rc2 != LuaError::OK) { // Error within a finalizer! |
| 680 | // the finalizer generated an error, and left its own error message [and stack trace] on the stack | 680 | // the finalizer generated an error, and left its own error message [and stack trace] on the stack |
| 681 | _rc = _rc2; // we're overruling the earlier script error or normal return | 681 | _rc = _rc2; // we're overruling the earlier script error or normal return |
| 682 | } | 682 | } |
| @@ -699,11 +699,11 @@ static void lane_main(Lane* lane_) | |||
| 699 | if (lane_) { | 699 | if (lane_) { |
| 700 | // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them | 700 | // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them |
| 701 | 701 | ||
| 702 | Lane::Status const _st = (_rc == LUA_OK) ? Lane::Done : kCancelError.equals(_L, 1) ? Lane::Cancelled : Lane::Error; | 702 | Lane::Status const _st{ (_rc == LuaError::OK) ? Lane::Done : kCancelError.equals(_L, 1) ? Lane::Cancelled : Lane::Error }; |
| 703 | 703 | ||
| 704 | { | 704 | { |
| 705 | // 'doneMutex' protects the -> Done|Error|Cancelled state change | 705 | // 'doneMutex' protects the -> Done|Error|Cancelled state change |
| 706 | std::lock_guard lock{ lane_->doneMutex }; | 706 | std::lock_guard _guard{ lane_->doneMutex }; |
| 707 | lane_->status = _st; | 707 | lane_->status = _st; |
| 708 | lane_->doneCondVar.notify_one(); // wake up master (while 'lane_->doneMutex' is on) | 708 | lane_->doneCondVar.notify_one(); // wake up master (while 'lane_->doneMutex' is on) |
| 709 | } | 709 | } |
