diff options
-rw-r--r-- | src/lane.cpp | 65 | ||||
-rw-r--r-- | src/lane.h | 2 | ||||
-rw-r--r-- | src/lanes.cpp | 6 |
3 files changed, 57 insertions, 16 deletions
diff --git a/src/lane.cpp b/src/lane.cpp index 8c4062c..d6c9960 100644 --- a/src/lane.cpp +++ b/src/lane.cpp | |||
@@ -88,11 +88,11 @@ static LUAG_FUNC(lane_threadname) | |||
88 | Lane* const _lane{ luaG_tolightuserdata<Lane>(L_, lua_upvalueindex(1)) }; | 88 | Lane* const _lane{ luaG_tolightuserdata<Lane>(L_, lua_upvalueindex(1)) }; |
89 | LUA_ASSERT(L_, L_ == _lane->L); // this function is exported in a lane's state, therefore it is callable only from inside the Lane's state | 89 | LUA_ASSERT(L_, L_ == _lane->L); // this function is exported in a lane's state, therefore it is callable only from inside the Lane's state |
90 | if (lua_gettop(L_) == 1) { | 90 | if (lua_gettop(L_) == 1) { |
91 | lua_settop(L_, 1); | 91 | lua_settop(L_, 1); |
92 | STACK_CHECK_START_REL(L_, 0); | 92 | STACK_CHECK_START_REL(L_, 0); |
93 | _lane->changeDebugName(-1); | 93 | _lane->changeDebugName(-1); |
94 | STACK_CHECK(L_, 0); | 94 | STACK_CHECK(L_, 0); |
95 | return 0; | 95 | return 0; |
96 | } else if (lua_gettop(L_) == 0) { | 96 | } else if (lua_gettop(L_) == 0) { |
97 | luaG_pushstring(L_, _lane->getDebugName()); | 97 | luaG_pushstring(L_, _lane->getDebugName()); |
98 | return 1; | 98 | return 1; |
@@ -138,14 +138,15 @@ static LUAG_FUNC(thread_join) | |||
138 | } | 138 | } |
139 | 139 | ||
140 | STACK_CHECK_START_REL(L_, 0); // L_: lane | 140 | STACK_CHECK_START_REL(L_, 0); // L_: lane |
141 | // Thread is Done/Error/Cancelled; all ours now | 141 | // Thread is Suspended or Done/Error/Cancelled; the Lane thread isn't working with it, therefore we can. |
142 | 142 | ||
143 | int _ret{ 0 }; | 143 | int _ret{ 0 }; |
144 | // debugName is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed | 144 | // debugName is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed |
145 | // so store it in the userdata uservalue at a key that can't possibly collide | 145 | // so store it in the userdata uservalue at a key that can't possibly collide |
146 | _lane->securizeDebugName(L_); | 146 | _lane->securizeDebugName(L_); |
147 | switch (_lane->status) { | 147 | switch (_lane->status) { |
148 | case Lane::Done: | 148 | case Lane::Suspended: // got yielded values |
149 | case Lane::Done: // got regular return values | ||
149 | { | 150 | { |
150 | bool const _calledFromLua{ lua_toboolean(L_, lua_upvalueindex(1)) ? false : true }; // this upvalue doesn't exist when called from Lua | 151 | bool const _calledFromLua{ lua_toboolean(L_, lua_upvalueindex(1)) ? false : true }; // this upvalue doesn't exist when called from Lua |
151 | int const _n{ lua_gettop(_L2) }; // whole L2 stack | 152 | int const _n{ lua_gettop(_L2) }; // whole L2 stack |
@@ -189,7 +190,10 @@ static LUAG_FUNC(thread_join) | |||
189 | LUA_ASSERT(L_, false); | 190 | LUA_ASSERT(L_, false); |
190 | _ret = 0; | 191 | _ret = 0; |
191 | } | 192 | } |
192 | _lane->closeState(); | 193 | // if we are suspended, all we want to do is gather the current yielded values |
194 | if (_lane->status != Lane::Suspended) { | ||
195 | _lane->closeState(); | ||
196 | } | ||
193 | STACK_CHECK(L_, _ret); | 197 | STACK_CHECK(L_, _ret); |
194 | return _ret; | 198 | return _ret; |
195 | } | 199 | } |
@@ -220,9 +224,12 @@ LUAG_FUNC(thread_resume) | |||
220 | } | 224 | } |
221 | int const _nargs{ lua_gettop(L_) - 1 }; | 225 | int const _nargs{ lua_gettop(L_) - 1 }; |
222 | int const _nresults{ lua_gettop(_L2) }; | 226 | int const _nresults{ lua_gettop(_L2) }; |
223 | STACK_CHECK_START_ABS(L_, 1 + _nargs); // L_: self args... _L2: results... | 227 | STACK_CHECK_START_ABS(L_, 1 + _nargs); // L_: self args... _L2: results... |
224 | STACK_CHECK_START_ABS(_L2, _nresults); | 228 | STACK_CHECK_START_ABS(_L2, _nresults); |
225 | 229 | ||
230 | // clear any fetched returned values that we might have stored previously | ||
231 | _lane->resetResultsStorage(L_, 1); | ||
232 | |||
226 | // to retrieve the yielded value of the coroutine on our stack | 233 | // to retrieve the yielded value of the coroutine on our stack |
227 | InterCopyContext _cin{ _lane->U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} }; | 234 | InterCopyContext _cin{ _lane->U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} }; |
228 | if (_cin.interMove(_nresults) != InterCopyResult::Success) { // L_: self args... results... _L2: | 235 | if (_cin.interMove(_nresults) != InterCopyResult::Success) { // L_: self args... results... _L2: |
@@ -291,6 +298,7 @@ static int thread_index_number(lua_State* L_) | |||
291 | lua_concat(L_, 2); // L_: "Unexpected status: <status>" | 298 | lua_concat(L_, 2); // L_: "Unexpected status: <status>" |
292 | raise_lua_error(L_); | 299 | raise_lua_error(L_); |
293 | 300 | ||
301 | case Lane::Suspended: // got yielded values | ||
294 | case Lane::Done: // got regular return values | 302 | case Lane::Done: // got regular return values |
295 | { | 303 | { |
296 | int const _nvalues{ lua_gettop(L_) - 3 }; // L_: lane n {uv} ... | 304 | int const _nvalues{ lua_gettop(L_) - 3 }; // L_: lane n {uv} ... |
@@ -980,7 +988,7 @@ CancelResult Lane::cancel(CancelOp const op_, int const hookCount_, std::chrono: | |||
980 | _waiting_on->notify_all(); | 988 | _waiting_on->notify_all(); |
981 | } | 989 | } |
982 | } | 990 | } |
983 | 991 | // wait until the lane stops working with its state (either Suspended or Done+) | |
984 | CancelResult result{ waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout }; | 992 | CancelResult result{ waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout }; |
985 | return result; | 993 | return result; |
986 | } | 994 | } |
@@ -998,6 +1006,7 @@ CancelResult Lane::cancel(CancelOp const op_, int const hookCount_, std::chrono: | |||
998 | } | 1006 | } |
999 | } | 1007 | } |
1000 | 1008 | ||
1009 | // wait until the lane stops working with its state (either Suspended or Done+) | ||
1001 | return waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout; | 1010 | return waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout; |
1002 | } | 1011 | } |
1003 | 1012 | ||
@@ -1013,7 +1022,7 @@ void Lane::changeDebugName(int const nameIdx_) | |||
1013 | // keep a direct pointer on the string | 1022 | // keep a direct pointer on the string |
1014 | { | 1023 | { |
1015 | std::lock_guard<std::mutex> _guard{ debugNameMutex }; | 1024 | std::lock_guard<std::mutex> _guard{ debugNameMutex }; |
1016 | debugName = luaG_tostring(L, _nameIdx); | 1025 | debugName = luaG_tostring(L, _nameIdx); |
1017 | } | 1026 | } |
1018 | if constexpr (HAVE_DECODA_SUPPORT()) { | 1027 | if constexpr (HAVE_DECODA_SUPPORT()) { |
1019 | // to see VM name in Decoda debugger Virtual Machine window | 1028 | // to see VM name in Decoda debugger Virtual Machine window |
@@ -1120,6 +1129,34 @@ void Lane::pushStatusString(lua_State* L_) const | |||
1120 | 1129 | ||
1121 | // ################################################################################################# | 1130 | // ################################################################################################# |
1122 | 1131 | ||
1132 | // replace the current uservalue (a table holding the returned values of the lane body) | ||
1133 | // by a new empty one, but transfer the gc_cb that is stored in there so that it is not lost | ||
1134 | void Lane::resetResultsStorage(lua_State* const L_, int const self_idx_) | ||
1135 | { | ||
1136 | STACK_GROW(L_, 4); | ||
1137 | STACK_CHECK_START_REL(L_, 0); | ||
1138 | int const _self_idx{ luaG_absindex(L_, self_idx_) }; | ||
1139 | LUA_ASSERT(L_, ToLane(L_, _self_idx) == this); // L_: ... self ... | ||
1140 | // create the new table | ||
1141 | lua_newtable(L_); // L_: ... self ... {} | ||
1142 | // get the current table | ||
1143 | lua_getiuservalue(L_, _self_idx, 1); // L_: ... self ... {} {uv} | ||
1144 | LUA_ASSERT(L_, lua_istable(L_, -1)); | ||
1145 | // read gc_cb from the current table | ||
1146 | kLaneGC.pushKey(L_); // L_: ... self ... {} {uv} kLaneGC | ||
1147 | kLaneGC.pushKey(L_); // L_: ... self ... {} {uv} kLaneGC kLaneGC | ||
1148 | lua_rawget(L_, -3); // L_: ... self ... {} {uv} kLaneGC gc_cb|nil | ||
1149 | // store it in the new table | ||
1150 | lua_rawset(L_, -4); // L_: ... self ... {} {uv} | ||
1151 | // we can forget the old table | ||
1152 | lua_pop(L_, 1); // L_: ... self ... {} | ||
1153 | // and store the new one | ||
1154 | lua_setiuservalue(L_, _self_idx, 1); // L_: ... self ... | ||
1155 | STACK_CHECK(L_, 0); | ||
1156 | } | ||
1157 | |||
1158 | // ################################################################################################# | ||
1159 | |||
1123 | // intern the debug name in the caller lua state so that the pointer remains valid after the lane's state is closed | 1160 | // intern the debug name in the caller lua state so that the pointer remains valid after the lane's state is closed |
1124 | void Lane::securizeDebugName(lua_State* L_) | 1161 | void Lane::securizeDebugName(lua_State* L_) |
1125 | { | 1162 | { |
@@ -1132,7 +1169,7 @@ void Lane::securizeDebugName(lua_State* L_) | |||
1132 | lua_newtable(L_); // L_: lane ... {uv} {} | 1169 | lua_newtable(L_); // L_: lane ... {uv} {} |
1133 | { | 1170 | { |
1134 | std::lock_guard<std::mutex> _guard{ debugNameMutex }; | 1171 | std::lock_guard<std::mutex> _guard{ debugNameMutex }; |
1135 | debugName = luaG_pushstring(L_, debugName); // L_: lane ... {uv} {} name | 1172 | debugName = luaG_pushstring(L_, debugName); // L_: lane ... {uv} {} name |
1136 | } | 1173 | } |
1137 | lua_rawset(L_, -3); // L_: lane ... {uv} | 1174 | lua_rawset(L_, -3); // L_: lane ... {uv} |
1138 | lua_pop(L_, 1); // L_: lane | 1175 | lua_pop(L_, 1); // L_: lane |
@@ -1195,5 +1232,7 @@ bool Lane::waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> | |||
1195 | std::unique_lock _guard{ doneMutex }; | 1232 | std::unique_lock _guard{ doneMutex }; |
1196 | // std::stop_token token{ thread.get_stop_token() }; | 1233 | // std::stop_token token{ thread.get_stop_token() }; |
1197 | // return doneCondVar.wait_until(lock, token, secs_, [this](){ return status >= Lane::Done; }); | 1234 | // return doneCondVar.wait_until(lock, token, secs_, [this](){ return status >= Lane::Done; }); |
1198 | return doneCondVar.wait_until(_guard, until_, [this]() { return status >= Lane::Done; }); | 1235 | |
1236 | // wait until the lane stops working with its state (either Suspended or Done+) | ||
1237 | return doneCondVar.wait_until(_guard, until_, [this]() { return status == Lane::Suspended || status >= Lane::Done; }); | ||
1199 | } | 1238 | } |
@@ -167,9 +167,11 @@ class Lane | |||
167 | [[nodiscard]] std::string_view pushErrorTraceLevel(lua_State* L_) const; | 167 | [[nodiscard]] std::string_view pushErrorTraceLevel(lua_State* L_) const; |
168 | static void PushMetatable(lua_State* L_); | 168 | static void PushMetatable(lua_State* L_); |
169 | void pushStatusString(lua_State* L_) const; | 169 | void pushStatusString(lua_State* L_) const; |
170 | void resetResultsStorage(lua_State* const L_, int gc_cb_idx_); | ||
170 | void securizeDebugName(lua_State* L_); | 171 | void securizeDebugName(lua_State* L_); |
171 | void startThread(int priority_); | 172 | void startThread(int priority_); |
172 | [[nodiscard]] std::string_view threadStatusString() const; | 173 | [[nodiscard]] std::string_view threadStatusString() const; |
174 | // wait until the lane stops working with its state (either Suspended or Done+) | ||
173 | [[nodiscard]] bool waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_); | 175 | [[nodiscard]] bool waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_); |
174 | }; | 176 | }; |
175 | 177 | ||
diff --git a/src/lanes.cpp b/src/lanes.cpp index c11dd6a..f1b5884 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp | |||
@@ -333,12 +333,11 @@ LUAG_FUNC(lane_new) | |||
333 | *_ud = lane; // don't forget to store the pointer in the userdata! | 333 | *_ud = lane; // don't forget to store the pointer in the userdata! |
334 | 334 | ||
335 | // Set metatable for the userdata | 335 | // Set metatable for the userdata |
336 | // | ||
337 | lua_pushvalue(L, lua_upvalueindex(1)); // L: ... lane mt | 336 | lua_pushvalue(L, lua_upvalueindex(1)); // L: ... lane mt |
338 | lua_setmetatable(L, -2); // L: ... lane | 337 | lua_setmetatable(L, -2); // L: ... lane |
339 | STACK_CHECK(L, 1); | 338 | STACK_CHECK(L, 1); |
340 | 339 | ||
341 | // Create uservalue for the userdata | 340 | // Create uservalue for the userdata. There can be only one that must be a table, due to Lua 5.1 compatibility. |
342 | // (this is where lane body return values will be stored when the handle is indexed by a numeric key) | 341 | // (this is where lane body return values will be stored when the handle is indexed by a numeric key) |
343 | lua_newtable(L); // L: ... lane {uv} | 342 | lua_newtable(L); // L: ... lane {uv} |
344 | 343 | ||
@@ -349,7 +348,8 @@ LUAG_FUNC(lane_new) | |||
349 | lua_pushvalue(L, _gc_cb_idx); // L: ... lane {uv} k gc_cb | 348 | lua_pushvalue(L, _gc_cb_idx); // L: ... lane {uv} k gc_cb |
350 | lua_rawset(L, -3); // L: ... lane {uv} | 349 | lua_rawset(L, -3); // L: ... lane {uv} |
351 | } | 350 | } |
352 | 351 | STACK_CHECK(L, 2); | |
352 | // store the uservalue in the Lane full userdata | ||
353 | lua_setiuservalue(L, -2, 1); // L: ... lane | 353 | lua_setiuservalue(L, -2, 1); // L: ... lane |
354 | 354 | ||
355 | lua_State* const _L2{ lane->L }; | 355 | lua_State* const _L2{ lane->L }; |