diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-07-03 16:57:38 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-07-03 16:57:38 +0200 |
| commit | 0880a486b0f3bb289ec160fc8e199e126e4f45a0 (patch) | |
| tree | 7a970ea9943d6d498ebb87efc9b7d76dca637e1a /src | |
| parent | ffedd175233975f3ca9ac940df9883898d5ace25 (diff) | |
| download | lanes-0880a486b0f3bb289ec160fc8e199e126e4f45a0.tar.gz lanes-0880a486b0f3bb289ec160fc8e199e126e4f45a0.tar.bz2 lanes-0880a486b0f3bb289ec160fc8e199e126e4f45a0.zip | |
It is possible to index and join a suspended coroutine lane
Diffstat (limited to 'src')
| -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 }; |
