diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-07-03 09:28:33 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-07-03 09:28:33 +0200 |
| commit | f81fe873dd24f93306f0f667fc47766990a9321b (patch) | |
| tree | e537fe83c6ad34c8de6a57be0ac94de391834192 /src | |
| parent | 2bc1eee14394d1c277b2dfc3a68e591c4bef66a9 (diff) | |
| download | lanes-f81fe873dd24f93306f0f667fc47766990a9321b.tar.gz lanes-f81fe873dd24f93306f0f667fc47766990a9321b.tar.bz2 lanes-f81fe873dd24f93306f0f667fc47766990a9321b.zip | |
Add minimal coroutine support: no doc, no error handling, no cancellation handling (yet)
Diffstat (limited to 'src')
| -rw-r--r-- | src/lane.cpp | 142 | ||||
| -rw-r--r-- | src/lane.h | 26 | ||||
| -rw-r--r-- | src/lanes.cpp | 28 |
3 files changed, 160 insertions, 36 deletions
diff --git a/src/lane.cpp b/src/lane.cpp index ba24af3..dd038a3 100644 --- a/src/lane.cpp +++ b/src/lane.cpp | |||
| @@ -186,6 +186,55 @@ static LUAG_FUNC(thread_join) | |||
| 186 | 186 | ||
| 187 | // ################################################################################################# | 187 | // ################################################################################################# |
| 188 | 188 | ||
| 189 | LUAG_FUNC(thread_resume) | ||
| 190 | { | ||
| 191 | static constexpr int kSelf{ 1 }; | ||
| 192 | Lane* const _lane{ ToLane(L_, kSelf) }; | ||
| 193 | lua_State* const _L2{ _lane->L }; | ||
| 194 | |||
| 195 | // wait until the lane yields | ||
| 196 | std::optional<Lane::Status> _hadToWait{}; // for debugging, if we ever raise the error just below | ||
| 197 | { | ||
| 198 | std::unique_lock _guard{ _lane->doneMutex }; | ||
| 199 | if (_lane->status == Lane::Pending || _lane->status == Lane::Running || _lane->status == Lane::Resuming) { | ||
| 200 | _hadToWait = _lane->status; | ||
| 201 | _lane->doneCondVar.wait(_guard, [_lane]() { return _lane->status == Lane::Suspended; }); | ||
| 202 | } | ||
| 203 | } | ||
| 204 | if (_lane->status != Lane::Suspended) { | ||
| 205 | if (_hadToWait) { | ||
| 206 | raise_luaL_error(L_, "INTERNAL ERROR: Lane status is %s instead of 'suspended'", _lane->threadStatusString().data()); | ||
| 207 | } else { | ||
| 208 | raise_luaL_error(L_, "Can't resume a non-suspended coroutine-type Lane"); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | int const _nargs{ lua_gettop(L_) - 1 }; | ||
| 212 | int const _nresults{ lua_gettop(_L2) }; | ||
| 213 | STACK_CHECK_START_ABS(L_, 1 + _nargs); // L_: self args... _L2: results... | ||
| 214 | STACK_CHECK_START_ABS(_L2, _nresults); | ||
| 215 | |||
| 216 | // to retrieve the yielded value of the coroutine on our stack | ||
| 217 | InterCopyContext _cin{ _lane->U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} }; | ||
| 218 | if (_cin.interMove(_nresults) != InterCopyResult::Success) { // L_: self args... results... _L2: | ||
| 219 | raise_luaL_error(L_, "Failed to retrieve yielded values"); | ||
| 220 | } | ||
| 221 | |||
| 222 | // to send our args on the coroutine stack | ||
| 223 | InterCopyContext _cout{ _lane->U, DestState{ _L2 }, SourceState{ L_ }, {}, SourceIndex{ 2 }, {}, {}, {} }; | ||
| 224 | if (_cout.interCopy(_nargs) != InterCopyResult::Success) { // L_: self args... results... _L2: args... | ||
| 225 | raise_luaL_error(L_, "Failed to send resumed values"); | ||
| 226 | } | ||
| 227 | |||
| 228 | STACK_CHECK(_L2, _nargs); // we should have removed everything from the lane's stack, and pushed our args | ||
| 229 | STACK_CHECK(L_, 1 + _nargs + _nresults); // and the results of the coroutine are on top here | ||
| 230 | std::unique_lock _guard{ _lane->doneMutex }; | ||
| 231 | _lane->status = Lane::Resuming; | ||
| 232 | _lane->doneCondVar.notify_one(); | ||
| 233 | return _nresults; | ||
| 234 | } | ||
| 235 | |||
| 236 | // ################################################################################################# | ||
| 237 | |||
| 189 | // key is numeric, wait until the thread returns and populate the environment with the return values | 238 | // key is numeric, wait until the thread returns and populate the environment with the return values |
| 190 | // If the return values signal an error, propagate it | 239 | // If the return values signal an error, propagate it |
| 191 | // Else If key is found in the environment, return it | 240 | // Else If key is found in the environment, return it |
| @@ -697,12 +746,37 @@ static void lane_main(Lane* const lane_) | |||
| 697 | if (lane_->status == Lane::Pending) { // nothing wrong happened during preparation, we can work | 746 | if (lane_->status == Lane::Pending) { // nothing wrong happened during preparation, we can work |
| 698 | // At this point, the lane function and arguments are on the stack, possibly preceded by the error handler | 747 | // At this point, the lane function and arguments are on the stack, possibly preceded by the error handler |
| 699 | int const _errorHandlerCount{ lane_->errorTraceLevel == Lane::Minimal ? 0 : 1}; | 748 | int const _errorHandlerCount{ lane_->errorTraceLevel == Lane::Minimal ? 0 : 1}; |
| 700 | int const _nargs{ lua_gettop(_L) - 1 - _errorHandlerCount }; | 749 | int _nargs{ lua_gettop(_L) - 1 - _errorHandlerCount }; |
| 701 | lane_->status = Lane::Running; // Pending -> Running | 750 | { |
| 751 | std::unique_lock _guard{ lane_->doneMutex }; | ||
| 752 | lane_->status = Lane::Running; // Pending -> Running | ||
| 753 | } | ||
| 702 | 754 | ||
| 703 | PrepareLaneHelpers(lane_); | 755 | PrepareLaneHelpers(lane_); |
| 704 | 756 | if (lane_->S == lane_->L) { // L: eh? f args... | |
| 705 | _rc = ToLuaError(lua_pcall(_L, _nargs, LUA_MULTRET, _errorHandlerCount)); // L: eh? retvals|err | 757 | _rc = ToLuaError(lua_pcall(_L, _nargs, LUA_MULTRET, _errorHandlerCount)); // L: eh? retvals|err |
| 758 | } else { | ||
| 759 | // S and L are different: we run as a coroutine in Lua thread L created in state S | ||
| 760 | do { | ||
| 761 | int _nresults{}; | ||
| 762 | _rc = luaG_resume(_L, nullptr, _nargs, &_nresults); // L: eh? retvals|err | ||
| 763 | if (_rc == LuaError::YIELD) { | ||
| 764 | // change our status to suspended, and wait until someone wants us to resume | ||
| 765 | std::unique_lock _guard{ lane_->doneMutex }; | ||
| 766 | lane_->status = Lane::Suspended; // Running -> Suspended | ||
| 767 | lane_->doneCondVar.notify_one(); | ||
| 768 | // wait until the user wants us to resume | ||
| 769 | // TODO: do I update waiting_on or not, so that the lane can be woken by cancellation requests here? | ||
| 770 | // lane_->waiting_on = &lane_->doneCondVar; | ||
| 771 | lane_->doneCondVar.wait(_guard, [lane_]() { return lane_->status == Lane::Resuming; }); | ||
| 772 | // here lane_->doneMutex is locked again | ||
| 773 | // lane_->waiting_on = nullptr; | ||
| 774 | lane_->status = Lane::Running; // Resuming -> Running | ||
| 775 | // on the stack we find the values pushed by lane:resume() | ||
| 776 | _nargs = lua_gettop(_L); | ||
| 777 | } | ||
| 778 | } while (_rc == LuaError::YIELD); | ||
| 779 | } | ||
| 706 | 780 | ||
| 707 | if (_errorHandlerCount) { | 781 | if (_errorHandlerCount) { |
| 708 | lua_remove(_L, 1); // L: retvals|error | 782 | lua_remove(_L, 1); // L: retvals|error |
| @@ -713,7 +787,6 @@ static void lane_main(Lane* const lane_) | |||
| 713 | 787 | ||
| 714 | DEBUGSPEW_CODE(DebugSpew(lane_->U) << "Lane " << _L << " body: " << GetErrcodeName(_rc) << " (" << (kCancelError.equals(_L, 1) ? "cancelled" : luaG_typename(_L, 1)) << ")" << std::endl); | 788 | DEBUGSPEW_CODE(DebugSpew(lane_->U) << "Lane " << _L << " body: " << GetErrcodeName(_rc) << " (" << (kCancelError.equals(_L, 1) ? "cancelled" : luaG_typename(_L, 1)) << ")" << std::endl); |
| 715 | // Call finalizers, if the script has set them up. | 789 | // Call finalizers, if the script has set them up. |
| 716 | // | ||
| 717 | LuaError const _rc2{ run_finalizers(_L, lane_->errorTraceLevel, _rc) }; | 790 | LuaError const _rc2{ run_finalizers(_L, lane_->errorTraceLevel, _rc) }; |
| 718 | DEBUGSPEW_CODE(DebugSpew(lane_->U) << "Lane " << _L << " finalizer: " << GetErrcodeName(_rc2) << std::endl); | 791 | DEBUGSPEW_CODE(DebugSpew(lane_->U) << "Lane " << _L << " finalizer: " << GetErrcodeName(_rc2) << std::endl); |
| 719 | if (_rc2 != LuaError::OK) { // Error within a finalizer! | 792 | if (_rc2 != LuaError::OK) { // Error within a finalizer! |
| @@ -722,7 +795,7 @@ static void lane_main(Lane* const lane_) | |||
| 722 | } | 795 | } |
| 723 | lane_->waiting_on = nullptr; // just in case | 796 | lane_->waiting_on = nullptr; // just in case |
| 724 | if (selfdestruct_remove(lane_)) { // check and remove (under lock!) | 797 | if (selfdestruct_remove(lane_)) { // check and remove (under lock!) |
| 725 | // We're a free-running thread and no-one's there to clean us up. | 798 | // We're a free-running thread and no-one is there to clean us up. |
| 726 | lane_->closeState(); | 799 | lane_->closeState(); |
| 727 | lane_->U->selfdestructMutex.lock(); | 800 | lane_->U->selfdestructMutex.lock(); |
| 728 | // done with lua_close(), terminal shutdown sequence may proceed | 801 | // done with lua_close(), terminal shutdown sequence may proceed |
| @@ -739,7 +812,7 @@ static void lane_main(Lane* const lane_) | |||
| 739 | 812 | ||
| 740 | // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them | 813 | // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them |
| 741 | Lane::Status const _st{ (_rc == LuaError::OK) ? Lane::Done : kCancelError.equals(_L, 1) ? Lane::Cancelled : Lane::Error }; | 814 | Lane::Status const _st{ (_rc == LuaError::OK) ? Lane::Done : kCancelError.equals(_L, 1) ? Lane::Cancelled : Lane::Error }; |
| 742 | // 'doneMutex' protects the -> Done|Error|Cancelled state change | 815 | // 'doneMutex' protects the -> Done|Error|Cancelled state change, and the Running|Suspended|Resuming state change too |
| 743 | std::lock_guard _guard{ lane_->doneMutex }; | 816 | std::lock_guard _guard{ lane_->doneMutex }; |
| 744 | lane_->status = _st; | 817 | lane_->status = _st; |
| 745 | lane_->doneCondVar.notify_one(); // wake up master (while 'lane_->doneMutex' is on) | 818 | lane_->doneCondVar.notify_one(); // wake up master (while 'lane_->doneMutex' is on) |
| @@ -825,14 +898,21 @@ static LUAG_FUNC(lane_gc) | |||
| 825 | // #################################### Lane implementation ######################################## | 898 | // #################################### Lane implementation ######################################## |
| 826 | // ################################################################################################# | 899 | // ################################################################################################# |
| 827 | 900 | ||
| 828 | Lane::Lane(Universe* U_, lua_State* L_, ErrorTraceLevel errorTraceLevel_) | 901 | Lane::Lane(Universe* const U_, lua_State* const L_, ErrorTraceLevel const errorTraceLevel_, bool const asCoroutine_) |
| 829 | : U{ U_ } | 902 | : U{ U_ } |
| 903 | , S{ L_ } | ||
| 830 | , L{ L_ } | 904 | , L{ L_ } |
| 831 | , errorTraceLevel{ errorTraceLevel_ } | 905 | , errorTraceLevel{ errorTraceLevel_ } |
| 832 | { | 906 | { |
| 907 | STACK_CHECK_START_REL(S, 0); | ||
| 833 | assert(errorTraceLevel == ErrorTraceLevel::Minimal || errorTraceLevel == ErrorTraceLevel::Basic || errorTraceLevel == ErrorTraceLevel::Extended); | 908 | assert(errorTraceLevel == ErrorTraceLevel::Minimal || errorTraceLevel == ErrorTraceLevel::Basic || errorTraceLevel == ErrorTraceLevel::Extended); |
| 834 | kExtendedStackTraceRegKey.setValue(L_, [yes = errorTraceLevel == ErrorTraceLevel::Extended ? 1 : 0](lua_State* L_) { lua_pushboolean(L_, yes); }); | 909 | kExtendedStackTraceRegKey.setValue(S, [yes = errorTraceLevel == ErrorTraceLevel::Extended ? 1 : 0](lua_State* L_) { lua_pushboolean(L_, yes); }); |
| 835 | U->tracker.tracking_add(this); | 910 | U->tracker.tracking_add(this); |
| 911 | if (asCoroutine_) { | ||
| 912 | L = lua_newthread(S); // S: thread | ||
| 913 | //kCoroutineRegKey.setValue(S, [](lua_State* const L_) { lua_insert(L_, -2); }); // S: | ||
| 914 | } | ||
| 915 | STACK_CHECK(S, asCoroutine_ ? 1 : 0); | ||
| 836 | } | 916 | } |
| 837 | 917 | ||
| 838 | // ################################################################################################# | 918 | // ################################################################################################# |
| @@ -970,6 +1050,7 @@ namespace { | |||
| 970 | { "cancel", LG_thread_cancel }, | 1050 | { "cancel", LG_thread_cancel }, |
| 971 | { "get_debug_threadname", LG_get_debug_threadname }, | 1051 | { "get_debug_threadname", LG_get_debug_threadname }, |
| 972 | { "join", LG_thread_join }, | 1052 | { "join", LG_thread_join }, |
| 1053 | { "resume", LG_thread_resume }, | ||
| 973 | { nullptr, nullptr } | 1054 | { nullptr, nullptr } |
| 974 | }; | 1055 | }; |
| 975 | } // namespace local | 1056 | } // namespace local |
| @@ -1059,25 +1140,38 @@ void Lane::startThread(int priority_) | |||
| 1059 | //--- | 1140 | //--- |
| 1060 | // str= thread_status( lane ) | 1141 | // str= thread_status( lane ) |
| 1061 | // | 1142 | // |
| 1062 | // Returns: "pending" not started yet | 1143 | // "pending" -> | ("running" <-> "waiting") <-> "suspended" <-> "resuming" | -> "done"/"error"/"cancelled" |
| 1063 | // -> "running" started, doing its work.. | 1144 | |
| 1064 | // <-> "waiting" blocked in a receive() | 1145 | // "pending" not started yet |
| 1065 | // -> "done" finished, results are there | 1146 | // "running" started, doing its work.. |
| 1066 | // / "error" finished at an error, error value is there | 1147 | // "suspended" returned from a lua_resume |
| 1067 | // / "cancelled" execution cancelled by M (state gone) | 1148 | // "resuming" told by its parent state to resume |
| 1149 | // "waiting" blocked in a send()/receive() | ||
| 1150 | // "done" finished, results are there | ||
| 1151 | // "error" finished at an error, error value is there | ||
| 1152 | // "cancelled" execution cancelled (state gone) | ||
| 1068 | // | 1153 | // |
| 1069 | [[nodiscard]] std::string_view Lane::threadStatusString() const | 1154 | [[nodiscard]] std::string_view Lane::threadStatusString() const |
| 1070 | { | 1155 | { |
| 1071 | std::string_view const _str{ | 1156 | static constexpr std::string_view kStrs[] = { |
| 1072 | (status == Lane::Pending) ? "pending" : | 1157 | "pending", |
| 1073 | (status == Lane::Running) ? "running" : // like in 'co.status()' | 1158 | "running", "suspended", "resuming", |
| 1074 | (status == Lane::Waiting) ? "waiting" : | 1159 | "waiting", |
| 1075 | (status == Lane::Done) ? "done" : | 1160 | "done", "error", "cancelled" |
| 1076 | (status == Lane::Error) ? "error" : | ||
| 1077 | (status == Lane::Cancelled) ? "cancelled" : | ||
| 1078 | "" | ||
| 1079 | }; | 1161 | }; |
| 1080 | return _str; | 1162 | static_assert(0 == static_cast<std::underlying_type_t<Lane::Status>>(Pending)); |
| 1163 | static_assert(1 == static_cast<std::underlying_type_t<Lane::Status>>(Running)); | ||
| 1164 | static_assert(2 == static_cast<std::underlying_type_t<Lane::Status>>(Suspended)); | ||
| 1165 | static_assert(3 == static_cast<std::underlying_type_t<Lane::Status>>(Resuming)); | ||
| 1166 | static_assert(4 == static_cast<std::underlying_type_t<Lane::Status>>(Waiting)); | ||
| 1167 | static_assert(5 == static_cast<std::underlying_type_t<Lane::Status>>(Done)); | ||
| 1168 | static_assert(6 == static_cast<std::underlying_type_t<Lane::Status>>(Error)); | ||
| 1169 | static_assert(7 == static_cast<std::underlying_type_t<Lane::Status>>(Cancelled)); | ||
| 1170 | auto const _status{ static_cast<std::underlying_type_t<Lane::Status>>(status) }; | ||
| 1171 | if (_status < 0 || _status > 7) { // should never happen, but better safe than sorry | ||
| 1172 | return ""; | ||
| 1173 | } | ||
| 1174 | return kStrs[_status]; | ||
| 1081 | } | 1175 | } |
| 1082 | 1176 | ||
| 1083 | // ################################################################################################# | 1177 | // ################################################################################################# |
| @@ -17,6 +17,9 @@ static constexpr RegistryUniqueKey kExtendedStackTraceRegKey{ 0x38147AD48FB426E2 | |||
| 17 | * error (and maybe stack trace) arguments to the finalizer functions would | 17 | * error (and maybe stack trace) arguments to the finalizer functions would |
| 18 | * anyways complicate that approach. | 18 | * anyways complicate that approach. |
| 19 | */ | 19 | */ |
| 20 | // xxh64 of string "kCoroutineRegKey" generated at https://www.pelock.com/products/hash-calculator | ||
| 21 | static constexpr RegistryUniqueKey kCoroutineRegKey{ 0x72B049B0D130F009ull }; | ||
| 22 | |||
| 20 | // xxh64 of string "kFinalizerRegKey" generated at https://www.pelock.com/products/hash-calculator | 23 | // xxh64 of string "kFinalizerRegKey" generated at https://www.pelock.com/products/hash-calculator |
| 21 | static constexpr RegistryUniqueKey kFinalizerRegKey{ 0xFE936BFAA718FEEAull }; | 24 | static constexpr RegistryUniqueKey kFinalizerRegKey{ 0xFE936BFAA718FEEAull }; |
| 22 | 25 | ||
| @@ -45,13 +48,17 @@ class Lane | |||
| 45 | public: | 48 | public: |
| 46 | /* | 49 | /* |
| 47 | Pending: The Lua VM hasn't done anything yet. | 50 | Pending: The Lua VM hasn't done anything yet. |
| 48 | Running, Waiting: Thread is inside the Lua VM. If the thread is forcefully stopped, we can't lua_close() the Lua State. | 51 | Resuming: The user requested the lane to resume execution from Suspended state. |
| 52 | Suspended: returned from lua_resume, waiting for the client to request a lua_resume. | ||
| 53 | Running, Suspended, Waiting: Thread is inside the Lua VM. | ||
| 49 | Done, Error, Cancelled: Thread execution is outside the Lua VM. It can be lua_close()d. | 54 | Done, Error, Cancelled: Thread execution is outside the Lua VM. It can be lua_close()d. |
| 50 | */ | 55 | */ |
| 51 | enum class Status | 56 | enum class Status |
| 52 | { | 57 | { |
| 53 | Pending, | 58 | Pending, |
| 54 | Running, | 59 | Running, |
| 60 | Suspended, | ||
| 61 | Resuming, | ||
| 55 | Waiting, | 62 | Waiting, |
| 56 | Done, | 63 | Done, |
| 57 | Error, | 64 | Error, |
| @@ -84,8 +91,9 @@ class Lane | |||
| 84 | 91 | ||
| 85 | std::string_view debugName{ "<unnamed>" }; | 92 | std::string_view debugName{ "<unnamed>" }; |
| 86 | 93 | ||
| 87 | Universe* const U; | 94 | Universe* const U{}; |
| 88 | lua_State* L; | 95 | lua_State* S{}; // the master state of the lane |
| 96 | lua_State* L{}; // the state we run things in (either S or a lua_newthread() state if we run in coroutine mode) | ||
| 89 | // | 97 | // |
| 90 | // M: prepares the state, and reads results | 98 | // M: prepares the state, and reads results |
| 91 | // S: while S is running, M must keep out of modifying the state | 99 | // S: while S is running, M must keep out of modifying the state |
| @@ -93,7 +101,7 @@ class Lane | |||
| 93 | Status volatile status{ Pending }; | 101 | Status volatile status{ Pending }; |
| 94 | // | 102 | // |
| 95 | // M: sets to Pending (before launching) | 103 | // M: sets to Pending (before launching) |
| 96 | // S: updates -> Running/Waiting -> Done/Error/Cancelled | 104 | // S: updates -> Running/Waiting/Suspended -> Done/Error/Cancelled |
| 97 | 105 | ||
| 98 | std::condition_variable* volatile waiting_on{ nullptr }; | 106 | std::condition_variable* volatile waiting_on{ nullptr }; |
| 99 | // | 107 | // |
| @@ -121,7 +129,7 @@ class Lane | |||
| 121 | // this one is for us, to make sure memory is freed by the correct allocator | 129 | // this one is for us, to make sure memory is freed by the correct allocator |
| 122 | static void operator delete(void* p_) { static_cast<Lane*>(p_)->U->internalAllocator.free(p_, sizeof(Lane)); } | 130 | static void operator delete(void* p_) { static_cast<Lane*>(p_)->U->internalAllocator.free(p_, sizeof(Lane)); } |
| 123 | 131 | ||
| 124 | Lane(Universe* U_, lua_State* L_, ErrorTraceLevel errorTraceLevel_); | 132 | Lane(Universe* U_, lua_State* L_, ErrorTraceLevel errorTraceLevel_, bool asCoroutine_); |
| 125 | ~Lane(); | 133 | ~Lane(); |
| 126 | 134 | ||
| 127 | private: | 135 | private: |
| @@ -133,7 +141,13 @@ class Lane | |||
| 133 | 141 | ||
| 134 | CancelResult cancel(CancelOp op_, int hookCount_, std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_); | 142 | CancelResult cancel(CancelOp op_, int hookCount_, std::chrono::time_point<std::chrono::steady_clock> until_, bool wakeLane_); |
| 135 | void changeDebugName(int const nameIdx_); | 143 | void changeDebugName(int const nameIdx_); |
| 136 | void closeState() { lua_State* _L{ L }; L = nullptr; lua_close(_L); } | 144 | void closeState() |
| 145 | { | ||
| 146 | lua_State* _L{ S }; | ||
| 147 | S = nullptr; | ||
| 148 | L = nullptr; | ||
| 149 | lua_close(_L); // this collects our coroutine thread at the same time | ||
| 150 | } | ||
| 137 | [[nodiscard]] std::string_view errorTraceLevelString() const; | 151 | [[nodiscard]] std::string_view errorTraceLevelString() const; |
| 138 | [[nodiscard]] int pushErrorHandler() const; | 152 | [[nodiscard]] int pushErrorHandler() const; |
| 139 | [[nodiscard]] std::string_view pushErrorTraceLevel(lua_State* L_) const; | 153 | [[nodiscard]] std::string_view pushErrorTraceLevel(lua_State* L_) const; |
diff --git a/src/lanes.cpp b/src/lanes.cpp index 473e150..c11dd6a 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp | |||
| @@ -268,11 +268,16 @@ LUAG_FUNC(lane_new) | |||
| 268 | DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: setup" << std::endl); | 268 | DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: setup" << std::endl); |
| 269 | 269 | ||
| 270 | std::optional<std::string_view> _libs_str{ lua_isnil(L_, kLibsIdx) ? std::nullopt : std::make_optional(luaG_tostring(L_, kLibsIdx)) }; | 270 | std::optional<std::string_view> _libs_str{ lua_isnil(L_, kLibsIdx) ? std::nullopt : std::make_optional(luaG_tostring(L_, kLibsIdx)) }; |
| 271 | lua_State* const _L2{ state::NewLaneState(_U, SourceState{ L_ }, _libs_str) }; // L_: [fixed] ... L2: | 271 | lua_State* const _S{ state::NewLaneState(_U, SourceState{ L_ }, _libs_str) }; // L_: [fixed] ... L2: |
| 272 | STACK_CHECK_START_REL(_L2, 0); | 272 | STACK_CHECK_START_REL(_S, 0); |
| 273 | 273 | ||
| 274 | // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) | 274 | // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) |
| 275 | Lane* const _lane{ new (_U) Lane{ _U, _L2, static_cast<Lane::ErrorTraceLevel>(lua_tointeger(L_, kErTlIdx)) } }; | 275 | Lane::ErrorTraceLevel const _errorTraceLevel{ static_cast<Lane::ErrorTraceLevel>(lua_tointeger(L_, kErTlIdx)) }; |
| 276 | bool const _asCoroutine{ lua_toboolean(L_, kAsCoro) ? true : false }; | ||
| 277 | Lane* const _lane{ new (_U) Lane{ _U, _S, _errorTraceLevel, _asCoroutine } }; | ||
| 278 | STACK_CHECK(_S, _asCoroutine ? 1 : 0); // the Lane's thread is on the Lane's state stack | ||
| 279 | lua_State* const _L2{ _lane->L }; | ||
| 280 | STACK_CHECK_START_REL(_L2, 0); | ||
| 276 | if (_lane == nullptr) { | 281 | if (_lane == nullptr) { |
| 277 | raise_luaL_error(L_, "could not create lane: out of memory"); | 282 | raise_luaL_error(L_, "could not create lane: out of memory"); |
| 278 | } | 283 | } |
| @@ -347,7 +352,7 @@ LUAG_FUNC(lane_new) | |||
| 347 | 352 | ||
| 348 | lua_setiuservalue(L, -2, 1); // L: ... lane | 353 | lua_setiuservalue(L, -2, 1); // L: ... lane |
| 349 | 354 | ||
| 350 | lua_State* _L2{ lane->L }; | 355 | lua_State* const _L2{ lane->L }; |
| 351 | STACK_CHECK_START_REL(_L2, 0); | 356 | STACK_CHECK_START_REL(_L2, 0); |
| 352 | int const _name_idx{ lua_isnoneornil(L, kNameIdx) ? 0 : kNameIdx }; | 357 | int const _name_idx{ lua_isnoneornil(L, kNameIdx) ? 0 : kNameIdx }; |
| 353 | std::string_view const _debugName{ (_name_idx > 0) ? luaG_tostring(L, _name_idx) : std::string_view{} }; | 358 | std::string_view const _debugName{ (_name_idx > 0) ? luaG_tostring(L, _name_idx) : std::string_view{} }; |
| @@ -357,7 +362,7 @@ LUAG_FUNC(lane_new) | |||
| 357 | luaG_pushstring(_L2, _debugName); // L: ... lane L2: "<name>" | 362 | luaG_pushstring(_L2, _debugName); // L: ... lane L2: "<name>" |
| 358 | } else { | 363 | } else { |
| 359 | lua_Debug _ar; | 364 | lua_Debug _ar; |
| 360 | lua_pushvalue(L, 1); // L: ... lane func | 365 | lua_pushvalue(L, kFuncIdx); // L: ... lane func |
| 361 | lua_getinfo(L, ">S", &_ar); // L: ... lane | 366 | lua_getinfo(L, ">S", &_ar); // L: ... lane |
| 362 | luaG_pushstring(_L2, "%s:%d", _ar.short_src, _ar.linedefined); // L: ... lane L2: "<name>" | 367 | luaG_pushstring(_L2, "%s:%d", _ar.short_src, _ar.linedefined); // L: ... lane L2: "<name>" |
| 363 | } | 368 | } |
| @@ -415,6 +420,8 @@ LUAG_FUNC(lane_new) | |||
| 415 | [[maybe_unused]] InterCopyResult const _ret{ _c.interCopyPackage() }; | 420 | [[maybe_unused]] InterCopyResult const _ret{ _c.interCopyPackage() }; |
| 416 | LUA_ASSERT(L_, _ret == InterCopyResult::Success); // either all went well, or we should not even get here | 421 | LUA_ASSERT(L_, _ret == InterCopyResult::Success); // either all went well, or we should not even get here |
| 417 | } | 422 | } |
| 423 | STACK_CHECK(L_, 0); | ||
| 424 | STACK_CHECK(_L2, 0); | ||
| 418 | 425 | ||
| 419 | // modules to require in the target lane *before* the function is transfered! | 426 | // modules to require in the target lane *before* the function is transfered! |
| 420 | int const _required_idx{ lua_isnoneornil(L_, kRequIdx) ? 0 : kRequIdx }; | 427 | int const _required_idx{ lua_isnoneornil(L_, kRequIdx) ? 0 : kRequIdx }; |
| @@ -526,6 +533,7 @@ LUAG_FUNC(lane_new) | |||
| 526 | } | 533 | } |
| 527 | STACK_CHECK(L_, -_nargs); | 534 | STACK_CHECK(L_, -_nargs); |
| 528 | LUA_ASSERT(L_, lua_gettop(L_) == kFixedArgsIdx); | 535 | LUA_ASSERT(L_, lua_gettop(L_) == kFixedArgsIdx); |
| 536 | STACK_CHECK(_L2, _errorHandlerCount + 1 + _nargs); | ||
| 529 | 537 | ||
| 530 | // Store 'lane' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive). | 538 | // Store 'lane' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive). |
| 531 | kLanePointerRegKey.setValue( | 539 | kLanePointerRegKey.setValue( |
| @@ -533,6 +541,14 @@ LUAG_FUNC(lane_new) | |||
| 533 | ); | 541 | ); |
| 534 | STACK_CHECK(_L2, _errorHandlerCount + 1 + _nargs); | 542 | STACK_CHECK(_L2, _errorHandlerCount + 1 + _nargs); |
| 535 | 543 | ||
| 544 | // if in coroutine mode, the Lane's master state stack should contain the thread | ||
| 545 | if (_asCoroutine) { | ||
| 546 | LUA_ASSERT(L_, _S != _L2); | ||
| 547 | STACK_CHECK(_S, 1); | ||
| 548 | } | ||
| 549 | // and the thread's stack has whatever is needed to run | ||
| 550 | STACK_CHECK(_L2, _errorHandlerCount + 1 + _nargs); | ||
| 551 | |||
| 536 | STACK_CHECK_RESET_REL(L_, 0); | 552 | STACK_CHECK_RESET_REL(L_, 0); |
| 537 | // all went well, the lane's thread can start working | 553 | // all went well, the lane's thread can start working |
| 538 | _onExit.success(); // L_: [fixed] lane L2: <living its own life> | 554 | _onExit.success(); // L_: [fixed] lane L2: <living its own life> |
| @@ -541,7 +557,7 @@ LUAG_FUNC(lane_new) | |||
| 541 | return 1; | 557 | return 1; |
| 542 | } | 558 | } |
| 543 | 559 | ||
| 544 | // ################################################################################################ | 560 | // ################################################################################################# |
| 545 | 561 | ||
| 546 | // threads() -> {}|nil | 562 | // threads() -> {}|nil |
| 547 | // Return a list of all known lanes | 563 | // Return a list of all known lanes |
