diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-17 10:42:49 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-17 10:42:49 +0200 |
| commit | d359e8cf7d71c2f80a1706fef38e603478ac003f (patch) | |
| tree | 8f9562d595ab9d43496fee998b0825b330294b59 | |
| parent | 38a4b6b252dd46dc00e1871886e35c196ed1f245 (diff) | |
| download | lanes-d359e8cf7d71c2f80a1706fef38e603478ac003f.tar.gz lanes-d359e8cf7d71c2f80a1706fef38e603478ac003f.tar.bz2 lanes-d359e8cf7d71c2f80a1706fef38e603478ac003f.zip | |
Fix bad assert when raising a non-string error
| -rw-r--r-- | src/lane.cpp | 93 |
1 files changed, 54 insertions, 39 deletions
diff --git a/src/lane.cpp b/src/lane.cpp index 7165e85..f320071 100644 --- a/src/lane.cpp +++ b/src/lane.cpp | |||
| @@ -43,7 +43,7 @@ THE SOFTWARE. | |||
| 43 | // ######################################### Lua API ############################################### | 43 | // ######################################### Lua API ############################################### |
| 44 | // ################################################################################################# | 44 | // ################################################################################################# |
| 45 | 45 | ||
| 46 | LUAG_FUNC(get_debug_threadname) | 46 | static LUAG_FUNC(get_debug_threadname) |
| 47 | { | 47 | { |
| 48 | Lane* const _lane{ ToLane(L_, 1) }; | 48 | Lane* const _lane{ ToLane(L_, 1) }; |
| 49 | luaL_argcheck(L_, lua_gettop(L_) == 1, 2, "too many arguments"); | 49 | luaL_argcheck(L_, lua_gettop(L_) == 1, 2, "too many arguments"); |
| @@ -60,7 +60,7 @@ LUAG_FUNC(get_debug_threadname) | |||
| 60 | // Add a function that will be called when exiting the lane, either via | 60 | // Add a function that will be called when exiting the lane, either via |
| 61 | // normal return or an error. | 61 | // normal return or an error. |
| 62 | // | 62 | // |
| 63 | LUAG_FUNC(set_finalizer) | 63 | static LUAG_FUNC(set_finalizer) |
| 64 | { | 64 | { |
| 65 | luaL_argcheck(L_, lua_isfunction(L_, 1), 1, "finalizer should be a function"); | 65 | luaL_argcheck(L_, lua_isfunction(L_, 1), 1, "finalizer should be a function"); |
| 66 | luaL_argcheck(L_, lua_gettop(L_) == 1, 1, "too many arguments"); | 66 | luaL_argcheck(L_, lua_gettop(L_) == 1, 1, "too many arguments"); |
| @@ -98,7 +98,7 @@ LUAG_FUNC(set_error_reporting) | |||
| 98 | // ################################################################################################# | 98 | // ################################################################################################# |
| 99 | 99 | ||
| 100 | // upvalue #1 is the lane userdata | 100 | // upvalue #1 is the lane userdata |
| 101 | LUAG_FUNC(set_debug_threadname) | 101 | static LUAG_FUNC(set_debug_threadname) |
| 102 | { | 102 | { |
| 103 | // C s_lane structure is a light userdata upvalue | 103 | // C s_lane structure is a light userdata upvalue |
| 104 | Lane* const _lane{ lua_tolightuserdata<Lane>(L_, lua_upvalueindex(1)) }; | 104 | Lane* const _lane{ lua_tolightuserdata<Lane>(L_, lua_upvalueindex(1)) }; |
| @@ -120,10 +120,9 @@ LUAG_FUNC(set_debug_threadname) | |||
| 120 | // error: returns nil + error value [+ stack table] | 120 | // error: returns nil + error value [+ stack table] |
| 121 | // cancelled: returns nil | 121 | // cancelled: returns nil |
| 122 | // | 122 | // |
| 123 | LUAG_FUNC(thread_join) | 123 | static LUAG_FUNC(thread_join) |
| 124 | { | 124 | { |
| 125 | Lane* const _lane{ ToLane(L_, 1) }; | 125 | Lane* const _lane{ ToLane(L_, 1) }; |
| 126 | lua_State* const _L2{ _lane->L }; | ||
| 127 | 126 | ||
| 128 | std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | 127 | std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; |
| 129 | if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion | 128 | if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion |
| @@ -138,9 +137,10 @@ LUAG_FUNC(thread_join) | |||
| 138 | raise_luaL_argerror(L_, 2, "incorrect duration type"); | 137 | raise_luaL_argerror(L_, 2, "incorrect duration type"); |
| 139 | } | 138 | } |
| 140 | 139 | ||
| 141 | bool const done{ !_lane->thread.joinable() || _lane->waitForCompletion(_until) }; | 140 | bool const _done{ !_lane->thread.joinable() || _lane->waitForCompletion(_until) }; |
| 142 | lua_settop(L_, 1); // L_: lane | 141 | lua_settop(L_, 1); // L_: lane |
| 143 | if (!done || !_L2) { | 142 | lua_State* const _L2{ _lane->L }; |
| 143 | if (!_done || !_L2) { | ||
| 144 | lua_pushnil(L_); // L_: lane nil | 144 | lua_pushnil(L_); // L_: lane nil |
| 145 | lua_pushliteral(L_, "timeout"); // L_: lane nil "timeout" | 145 | lua_pushliteral(L_, "timeout"); // L_: lane nil "timeout" |
| 146 | return 2; | 146 | return 2; |
| @@ -484,17 +484,18 @@ static void push_stack_trace(lua_State* L_, LuaError rc_, int stk_base_) | |||
| 484 | // For other errors, the message can be whatever was thrown, and we should have a stack trace table | 484 | // For other errors, the message can be whatever was thrown, and we should have a stack trace table |
| 485 | LUA_ASSERT(L_, lua_type(L_, 1 + stk_base_) == (kCancelError.equals(L_, stk_base_) ? LUA_TNIL : LUA_TTABLE)); | 485 | LUA_ASSERT(L_, lua_type(L_, 1 + stk_base_) == (kCancelError.equals(L_, stk_base_) ? LUA_TNIL : LUA_TTABLE)); |
| 486 | // Just leaving the stack trace table on the stack is enough to get it through to the master. | 486 | // Just leaving the stack trace table on the stack is enough to get it through to the master. |
| 487 | break; | ||
| 488 | } | 487 | } |
| 489 | #else // !ERROR_FULL_STACK | 488 | #else // !ERROR_FULL_STACK |
| 490 | [[fallthrough]]; // fall through if not ERROR_FULL_STACK | 489 | // any kind of error can be thrown with error(), or through a lane/linda cancellation |
| 490 | LUA_ASSERT(L_, lua_gettop(L_) == stk_base_); | ||
| 491 | #endif // !ERROR_FULL_STACK | 491 | #endif // !ERROR_FULL_STACK |
| 492 | break; | ||
| 492 | 493 | ||
| 493 | case LuaError::ERRMEM: // memory allocation error (handler not called) | 494 | case LuaError::ERRMEM: // memory allocation error (handler not called) |
| 494 | case LuaError::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) |
| 495 | default: | 496 | default: |
| 496 | // we should have a single value which is either a string (the error message) or kCancelError | 497 | // the Lua core provides a string error message in those situations |
| 497 | 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)); |
| 498 | break; | 499 | break; |
| 499 | } | 500 | } |
| 500 | } | 501 | } |
| @@ -621,6 +622,28 @@ static void selfdestruct_add(Lane* lane_) | |||
| 621 | // ########################################## Main ################################################# | 622 | // ########################################## Main ################################################# |
| 622 | // ################################################################################################# | 623 | // ################################################################################################# |
| 623 | 624 | ||
| 625 | static void PrepareLaneHelpers(Lane* lane_) | ||
| 626 | { | ||
| 627 | lua_State* const _L{ lane_->L }; | ||
| 628 | // Tie "set_finalizer()" to the state | ||
| 629 | lua_pushcfunction(_L, LG_set_finalizer); | ||
| 630 | populate_func_lookup_table(_L, -1, "set_finalizer"); | ||
| 631 | lua_setglobal(_L, "set_finalizer"); | ||
| 632 | |||
| 633 | // Tie "set_debug_threadname()" to the state | ||
| 634 | // But don't register it in the lookup database because of the Lane pointer upvalue | ||
| 635 | lua_pushlightuserdata(_L, lane_); | ||
| 636 | lua_pushcclosure(_L, LG_set_debug_threadname, 1); | ||
| 637 | lua_setglobal(_L, "set_debug_threadname"); | ||
| 638 | |||
| 639 | // Tie "cancel_test()" to the state | ||
| 640 | lua_pushcfunction(_L, LG_cancel_test); | ||
| 641 | populate_func_lookup_table(_L, -1, "cancel_test"); | ||
| 642 | lua_setglobal(_L, "cancel_test"); | ||
| 643 | } | ||
| 644 | |||
| 645 | // ################################################################################################# | ||
| 646 | |||
| 624 | static void lane_main(Lane* lane_) | 647 | static void lane_main(Lane* lane_) |
| 625 | { | 648 | { |
| 626 | lua_State* const _L{ lane_->L }; | 649 | lua_State* const _L{ lane_->L }; |
| @@ -629,25 +652,11 @@ static void lane_main(Lane* lane_) | |||
| 629 | LuaError _rc{ LuaError::ERRRUN }; | 652 | LuaError _rc{ LuaError::ERRRUN }; |
| 630 | if (lane_->status == Lane::Pending) { // nothing wrong happened during preparation, we can work | 653 | if (lane_->status == Lane::Pending) { // nothing wrong happened during preparation, we can work |
| 631 | // At this point, the lane function and arguments are on the stack | 654 | // At this point, the lane function and arguments are on the stack |
| 632 | int const nargs{ lua_gettop(_L) - 1 }; | 655 | int const _nargs{ lua_gettop(_L) - 1 }; |
| 633 | DEBUGSPEW_CODE(Universe* _U = universe_get(_L)); | 656 | DEBUGSPEW_CODE(Universe* _U = universe_get(_L)); |
| 634 | lane_->status = Lane::Running; // Pending -> Running | 657 | lane_->status = Lane::Running; // Pending -> Running |
| 635 | 658 | ||
| 636 | // Tie "set_finalizer()" to the state | 659 | PrepareLaneHelpers(lane_); |
| 637 | lua_pushcfunction(_L, LG_set_finalizer); | ||
| 638 | populate_func_lookup_table(_L, -1, "set_finalizer"); | ||
| 639 | lua_setglobal(_L, "set_finalizer"); | ||
| 640 | |||
| 641 | // Tie "set_debug_threadname()" to the state | ||
| 642 | // But don't register it in the lookup database because of the Lane pointer upvalue | ||
| 643 | lua_pushlightuserdata(_L, lane_); | ||
| 644 | lua_pushcclosure(_L, LG_set_debug_threadname, 1); | ||
| 645 | lua_setglobal(_L, "set_debug_threadname"); | ||
| 646 | |||
| 647 | // Tie "cancel_test()" to the state | ||
| 648 | lua_pushcfunction(_L, LG_cancel_test); | ||
| 649 | populate_func_lookup_table(_L, -1, "cancel_test"); | ||
| 650 | lua_setglobal(_L, "cancel_test"); | ||
| 651 | 660 | ||
| 652 | // this could be done in lane_new before the lane body function is pushed on the stack to avoid unnecessary stack slot shifting around | 661 | // this could be done in lane_new before the lane body function is pushed on the stack to avoid unnecessary stack slot shifting around |
| 653 | #if ERROR_FULL_STACK | 662 | #if ERROR_FULL_STACK |
| @@ -661,7 +670,7 @@ static void lane_main(Lane* lane_) | |||
| 661 | lua_insert(_L, 1); // L: handler func args | 670 | lua_insert(_L, 1); // L: handler func args |
| 662 | #endif // L: ERROR_FULL_STACK | 671 | #endif // L: ERROR_FULL_STACK |
| 663 | 672 | ||
| 664 | _rc = ToLuaError(lua_pcall(_L, nargs, LUA_MULTRET, ERROR_FULL_STACK)); // L: retvals|err | 673 | _rc = ToLuaError(lua_pcall(_L, _nargs, LUA_MULTRET, ERROR_FULL_STACK)); // L: retvals|err |
| 665 | 674 | ||
| 666 | #if ERROR_FULL_STACK | 675 | #if ERROR_FULL_STACK |
| 667 | lua_remove(_L, 1); // L: retvals|error | 676 | lua_remove(_L, 1); // L: retvals|error |
| @@ -813,29 +822,35 @@ void Lane::changeDebugName(int nameIdx_) | |||
| 813 | 822 | ||
| 814 | // ################################################################################################# | 823 | // ################################################################################################# |
| 815 | 824 | ||
| 816 | // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname } | 825 | namespace global { |
| 826 | static struct luaL_Reg const sLaneFunctions[] = { | ||
| 827 | { "__gc", lane_gc }, | ||
| 828 | { "__index", LG_thread_index }, | ||
| 829 | { "cancel", LG_thread_cancel }, | ||
| 830 | { "get_debug_threadname", LG_get_debug_threadname }, | ||
| 831 | { "join", LG_thread_join }, | ||
| 832 | { nullptr, nullptr } | ||
| 833 | }; | ||
| 834 | } // namespace global | ||
| 835 | |||
| 836 | // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname } | ||
| 817 | void Lane::PushMetatable(lua_State* L_) | 837 | void Lane::PushMetatable(lua_State* L_) |
| 818 | { | 838 | { |
| 839 | STACK_CHECK_START_REL(L_, 0); | ||
| 819 | if (luaL_newmetatable(L_, kLaneMetatableName)) { // L_: mt | 840 | if (luaL_newmetatable(L_, kLaneMetatableName)) { // L_: mt |
| 820 | lua_pushcfunction(L_, lane_gc); // L_: mt lane_gc | 841 | luaG_registerlibfuncs(L_, global::sLaneFunctions); |
| 821 | lua_setfield(L_, -2, "__gc"); // L_: mt | 842 | // cache error() and tostring() |
| 822 | lua_pushcfunction(L_, LG_thread_index); // L_: mt LG_thread_index | ||
| 823 | lua_setfield(L_, -2, "__index"); // L_: mt | ||
| 824 | lua_getglobal(L_, "error"); // L_: mt error | 843 | lua_getglobal(L_, "error"); // L_: mt error |
| 825 | LUA_ASSERT(L_, lua_isfunction(L_, -1)); | 844 | LUA_ASSERT(L_, lua_isfunction(L_, -1)); |
| 826 | lua_setfield(L_, -2, "cached_error"); // L_: mt | 845 | lua_setfield(L_, -2, "cached_error"); // L_: mt |
| 827 | lua_getglobal(L_, "tostring"); // L_: mt tostring | 846 | lua_getglobal(L_, "tostring"); // L_: mt tostring |
| 828 | LUA_ASSERT(L_, lua_isfunction(L_, -1)); | 847 | LUA_ASSERT(L_, lua_isfunction(L_, -1)); |
| 829 | lua_setfield(L_, -2, "cached_tostring"); // L_: mt | 848 | lua_setfield(L_, -2, "cached_tostring"); // L_: mt |
| 830 | lua_pushcfunction(L_, LG_thread_join); // L_: mt LG_thread_join | 849 | // hide the actual metatable from getmetatable() |
| 831 | lua_setfield(L_, -2, "join"); // L_: mt | ||
| 832 | lua_pushcfunction(L_, LG_get_debug_threadname); // L_: mt LG_get_debug_threadname | ||
| 833 | lua_setfield(L_, -2, "get_debug_threadname"); // L_: mt | ||
| 834 | lua_pushcfunction(L_, LG_thread_cancel); // L_: mt LG_thread_cancel | ||
| 835 | lua_setfield(L_, -2, "cancel"); // L_: mt | ||
| 836 | lua_pushliteral(L_, kLaneMetatableName); // L_: mt "Lane" | 850 | lua_pushliteral(L_, kLaneMetatableName); // L_: mt "Lane" |
| 837 | lua_setfield(L_, -2, "__metatable"); // L_: mt | 851 | lua_setfield(L_, -2, "__metatable"); // L_: mt |
| 838 | } | 852 | } |
| 853 | STACK_CHECK(L_, 1); | ||
| 839 | } | 854 | } |
| 840 | // ################################################################################################# | 855 | // ################################################################################################# |
| 841 | 856 | ||
