aboutsummaryrefslogtreecommitdiff
path: root/src/lane.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lane.cpp')
-rw-r--r--src/lane.cpp142
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
189LUAG_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
828Lane::Lane(Universe* U_, lua_State* L_, ErrorTraceLevel errorTraceLevel_) 901Lane::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// #################################################################################################