aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-05-17 10:42:49 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-05-17 10:42:49 +0200
commitd359e8cf7d71c2f80a1706fef38e603478ac003f (patch)
tree8f9562d595ab9d43496fee998b0825b330294b59 /src
parent38a4b6b252dd46dc00e1871886e35c196ed1f245 (diff)
downloadlanes-d359e8cf7d71c2f80a1706fef38e603478ac003f.tar.gz
lanes-d359e8cf7d71c2f80a1706fef38e603478ac003f.tar.bz2
lanes-d359e8cf7d71c2f80a1706fef38e603478ac003f.zip
Fix bad assert when raising a non-string error
Diffstat (limited to 'src')
-rw-r--r--src/lane.cpp93
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
46LUAG_FUNC(get_debug_threadname) 46static 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//
63LUAG_FUNC(set_finalizer) 63static 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
101LUAG_FUNC(set_debug_threadname) 101static 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//
123LUAG_FUNC(thread_join) 123static 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
625static 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
624static void lane_main(Lane* lane_) 647static 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 } 825namespace 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 }
817void Lane::PushMetatable(lua_State* L_) 837void 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