diff options
Diffstat (limited to 'src/lane.cpp')
-rw-r--r-- | src/lane.cpp | 142 |
1 files changed, 118 insertions, 24 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 | // ################################################################################################# |