diff options
-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 |