diff options
Diffstat (limited to 'src/lane.cpp')
-rw-r--r-- | src/lane.cpp | 424 |
1 files changed, 262 insertions, 162 deletions
diff --git a/src/lane.cpp b/src/lane.cpp index 5cebdfa..b23ff78 100644 --- a/src/lane.cpp +++ b/src/lane.cpp | |||
@@ -49,7 +49,7 @@ static LUAG_FUNC(lane_get_threadname) | |||
49 | { | 49 | { |
50 | Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) }; | 50 | Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) }; |
51 | luaL_argcheck(L_, lua_gettop(L_) == 1, 2, "too many arguments"); | 51 | luaL_argcheck(L_, lua_gettop(L_) == 1, 2, "too many arguments"); |
52 | luaG_pushstring(L_, _lane->getDebugName()); | 52 | luaW_pushstring(L_, _lane->getDebugName()); |
53 | return 1; | 53 | return 1; |
54 | } | 54 | } |
55 | 55 | ||
@@ -85,17 +85,17 @@ static LUAG_FUNC(set_finalizer) | |||
85 | static LUAG_FUNC(lane_threadname) | 85 | static LUAG_FUNC(lane_threadname) |
86 | { | 86 | { |
87 | // C s_lane structure is a light userdata upvalue | 87 | // C s_lane structure is a light userdata upvalue |
88 | Lane* const _lane{ luaG_tolightuserdata<Lane>(L_, StackIndex{ lua_upvalueindex(1) }) }; | 88 | Lane* const _lane{ luaW_tolightuserdata<Lane>(L_, StackIndex{ 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->storeDebugName(luaG_tostring(L_, kIdxTop)); | 93 | _lane->storeDebugName(luaW_tostring(L_, kIdxTop)); |
94 | _lane->applyDebugName(); | 94 | _lane->applyDebugName(); |
95 | STACK_CHECK(L_, 0); | 95 | STACK_CHECK(L_, 0); |
96 | return 0; | 96 | return 0; |
97 | } else if (lua_gettop(L_) == 0) { | 97 | } else if (lua_gettop(L_) == 0) { |
98 | luaG_pushstring(L_, _lane->getDebugName()); | 98 | luaW_pushstring(L_, _lane->getDebugName()); |
99 | return 1; | 99 | return 1; |
100 | } else { | 100 | } else { |
101 | raise_luaL_error(L_, "Wrong number of arguments"); | 101 | raise_luaL_error(L_, "Wrong number of arguments"); |
@@ -105,7 +105,7 @@ static LUAG_FUNC(lane_threadname) | |||
105 | // ################################################################################################# | 105 | // ################################################################################################# |
106 | 106 | ||
107 | //--- | 107 | //--- |
108 | // [...] | [nil, err_any, stack_tbl]= lane:join([wait_secs]) | 108 | // [true, ...] | [nil, err_any, stack_tbl]= lane:join([wait_secs]) |
109 | // | 109 | // |
110 | // timeout: returns nil | 110 | // timeout: returns nil |
111 | // done: returns return values (0..N) | 111 | // done: returns return values (0..N) |
@@ -117,7 +117,7 @@ static LUAG_FUNC(lane_join) | |||
117 | Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) }; | 117 | Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) }; |
118 | 118 | ||
119 | std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | 119 | std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; |
120 | if (luaG_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion | 120 | if (luaW_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion |
121 | lua_Duration const duration{ lua_tonumber(L_, 2) }; | 121 | lua_Duration const duration{ lua_tonumber(L_, 2) }; |
122 | if (duration.count() >= 0.0) { | 122 | if (duration.count() >= 0.0) { |
123 | _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); | 123 | _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); |
@@ -128,71 +128,20 @@ static LUAG_FUNC(lane_join) | |||
128 | } else if (!lua_isnoneornil(L_, 2)) { | 128 | } else if (!lua_isnoneornil(L_, 2)) { |
129 | raise_luaL_argerror(L_, StackIndex{ 2 }, "incorrect duration type"); | 129 | raise_luaL_argerror(L_, StackIndex{ 2 }, "incorrect duration type"); |
130 | } | 130 | } |
131 | |||
132 | lua_settop(L_, 1); // L_: lane | 131 | lua_settop(L_, 1); // L_: lane |
133 | bool const _done{ !_lane->thread.joinable() || _lane->waitForCompletion(_until) }; | ||
134 | 132 | ||
135 | if (!_done) { | 133 | // wait until suspended or done |
136 | lua_pushnil(L_); // L_: lane nil | 134 | STACK_CHECK_START_REL(L_, 0); // L_: lane |
137 | luaG_pushstring(L_, "timeout"); // L_: lane nil "timeout" | 135 | if (!_lane->waitForJoin(L_, _until)) { |
136 | // in that case, should have pushed nil, "timeout" | ||
137 | STACK_CHECK(L_, 2); | ||
138 | return 2; | 138 | return 2; |
139 | } | 139 | } |
140 | STACK_CHECK(L_, 0); // L_: lane | ||
141 | // Thread is Done/Error/Cancelled; the Lane thread isn't working with it, therefore we can. | ||
140 | 142 | ||
141 | STACK_CHECK_START_REL(L_, 0); // L_: lane | 143 | std::ignore = _lane->storeResults(L_); |
142 | // Thread is Suspended or Done/Error/Cancelled; the Lane thread isn't working with it, therefore we can. | 144 | int const _ret{ _lane->pushStoredResults(L_) }; |
143 | |||
144 | int _ret{ 0 }; | ||
145 | int const _stored{ _lane->storeResults(L_) }; | ||
146 | STACK_GROW(L_, std::max(3, _stored + 1)); | ||
147 | switch (_lane->status.load(std::memory_order_acquire)) { | ||
148 | case Lane::Suspended: // got yielded values | ||
149 | case Lane::Done: // got regular return values | ||
150 | { | ||
151 | if (_stored == 0) { | ||
152 | raise_luaL_error(L_, _lane->L ? "First return value must be non-nil when using join()" : "Can't join() more than once or after indexing"); | ||
153 | } | ||
154 | lua_getiuservalue(L_, StackIndex{ 1 }, UserValueIndex{ 1 }); // L_: lane {uv} | ||
155 | for (int _i = 2; _i <= _stored; ++_i) { | ||
156 | lua_rawgeti(L_, 2, _i); // L_: lane {uv} results2...N | ||
157 | } | ||
158 | lua_rawgeti(L_, 2, 1); // L_: lane {uv} results2...N result1 | ||
159 | lua_replace(L_, 2); // L_: lane results | ||
160 | _ret = _stored; | ||
161 | } | ||
162 | break; | ||
163 | |||
164 | case Lane::Error: | ||
165 | { | ||
166 | LUA_ASSERT(L_, _stored == 2 || _stored == 3); | ||
167 | lua_getiuservalue(L_, StackIndex{ 1 }, UserValueIndex{ 1 }); // L_: lane {uv} | ||
168 | lua_rawgeti(L_, 2, 2); // L_: lane {uv} <error> | ||
169 | lua_rawgeti(L_, 2, 3); // L_: lane {uv} <error> <trace>|nil | ||
170 | if (lua_isnil(L_, -1)) { | ||
171 | lua_replace(L_, 2); // L_: lane nil <error> | ||
172 | } else { | ||
173 | lua_rawgeti(L_, 2, 1); // L_: lane {uv} <error> <trace> nil | ||
174 | lua_replace(L_, 2); // L_: lane nil <error> <trace> | ||
175 | } | ||
176 | _ret = lua_gettop(L_) - 1; // 2 or 3 | ||
177 | } | ||
178 | break; | ||
179 | |||
180 | case Lane::Cancelled: | ||
181 | LUA_ASSERT(L_, _stored == 2); | ||
182 | lua_getiuservalue(L_, StackIndex{ 1 }, UserValueIndex{ 1 }); // L_: lane {uv} | ||
183 | lua_rawgeti(L_, 2, 2); // L_: lane {uv} cancel_error | ||
184 | lua_rawgeti(L_, 2, 1); // L_: lane {uv} cancel_error nil | ||
185 | lua_replace(L_, -3); // L_: lane nil cancel_error | ||
186 | LUA_ASSERT(L_, lua_isnil(L_, -2) && kCancelError.equals(L_, kIdxTop)); | ||
187 | _ret = 2; | ||
188 | break; | ||
189 | |||
190 | default: | ||
191 | DEBUGSPEW_CODE(DebugSpew(nullptr) << "Unknown Lane status: " << static_cast<int>(_lane->status.load(std::memory_order_relaxed)) << std::endl); | ||
192 | LUA_ASSERT(L_, false); | ||
193 | _ret = 0; | ||
194 | } | ||
195 | STACK_CHECK(L_, _ret); | ||
196 | return _ret; | 145 | return _ret; |
197 | } | 146 | } |
198 | 147 | ||
@@ -204,23 +153,13 @@ LUAG_FUNC(lane_resume) | |||
204 | Lane* const _lane{ ToLane(L_, kIdxSelf) }; | 153 | Lane* const _lane{ ToLane(L_, kIdxSelf) }; |
205 | lua_State* const _L2{ _lane->L }; | 154 | lua_State* const _L2{ _lane->L }; |
206 | 155 | ||
207 | // wait until the lane yields | 156 | // wait until the lane yields or returns |
208 | std::optional<Lane::Status> _hadToWait{}; // for debugging, if we ever raise the error just below | 157 | std::ignore = _lane->waitForCompletion(std::chrono::time_point<std::chrono::steady_clock>::max(), true); |
209 | { | 158 | |
210 | std::unique_lock _guard{ _lane->doneMutex }; | ||
211 | Lane::Status const _status{ _lane->status.load(std::memory_order_acquire) }; | ||
212 | if (_status == Lane::Pending || _status == Lane::Running || _status == Lane::Resuming) { | ||
213 | _hadToWait = _status; | ||
214 | _lane->doneCondVar.wait(_guard, [_lane]() { return _lane->status.load(std::memory_order_acquire) == Lane::Suspended; }); | ||
215 | } | ||
216 | } | ||
217 | if (_lane->status.load(std::memory_order_acquire) != Lane::Suspended) { | 159 | if (_lane->status.load(std::memory_order_acquire) != Lane::Suspended) { |
218 | if (_hadToWait) { | 160 | raise_luaL_error(L_, "cannot resume non-suspended coroutine Lane"); |
219 | raise_luaL_error(L_, "INTERNAL ERROR: Lane status is %s instead of 'suspended'", _lane->threadStatusString().data()); | ||
220 | } else { | ||
221 | raise_luaL_error(L_, "Can't resume a non-suspended coroutine-type Lane"); | ||
222 | } | ||
223 | } | 161 | } |
162 | |||
224 | int const _nargs{ lua_gettop(L_) - 1 }; | 163 | int const _nargs{ lua_gettop(L_) - 1 }; |
225 | int const _nresults{ lua_gettop(_L2) }; | 164 | int const _nresults{ lua_gettop(_L2) }; |
226 | STACK_CHECK_START_ABS(L_, 1 + _nargs); // L_: self args... _L2: results... | 165 | STACK_CHECK_START_ABS(L_, 1 + _nargs); // L_: self args... _L2: results... |
@@ -263,12 +202,17 @@ static int lane_index_number(lua_State* L_) | |||
263 | int const _key{ static_cast<int>(lua_tointeger(L_, 2)) }; | 202 | int const _key{ static_cast<int>(lua_tointeger(L_, 2)) }; |
264 | lua_pop(L_, 1); // L_: lane | 203 | lua_pop(L_, 1); // L_: lane |
265 | 204 | ||
205 | // wait until suspended or done | ||
206 | STACK_CHECK_START_REL(L_, 0); // L_: lane | ||
266 | std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | 207 | std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; |
267 | if (!_lane->waitForCompletion(_until)) { | 208 | if (!_lane->waitForJoin(L_, _until)) { |
268 | raise_luaL_error(L_, "INTERNAL ERROR: Failed to join"); | 209 | // in that case, should have pushed nil, "timeout" |
210 | STACK_CHECK(L_, 2); | ||
211 | return 2; | ||
269 | } | 212 | } |
213 | STACK_CHECK(L_, 0); // L_: lane | ||
214 | // Thread is Done/Error/Cancelled; the Lane thread isn't working with it, therefore we can. | ||
270 | 215 | ||
271 | // make sure results are stored | ||
272 | int const _stored{ _lane->storeResults(L_) }; | 216 | int const _stored{ _lane->storeResults(L_) }; |
273 | if (_key > _stored) { | 217 | if (_key > _stored) { |
274 | // get nil if indexing beyond the actual returned value count | 218 | // get nil if indexing beyond the actual returned value count |
@@ -276,6 +220,7 @@ static int lane_index_number(lua_State* L_) | |||
276 | } else { | 220 | } else { |
277 | _lane->pushIndexedResult(L_, _key); // L_: lane result | 221 | _lane->pushIndexedResult(L_, _key); // L_: lane result |
278 | } | 222 | } |
223 | |||
279 | return 1; | 224 | return 1; |
280 | } | 225 | } |
281 | 226 | ||
@@ -292,13 +237,13 @@ static int lane_index_string(lua_State* L_) | |||
292 | Lane* const _lane{ ToLane(L_, kIdxSelf) }; | 237 | Lane* const _lane{ ToLane(L_, kIdxSelf) }; |
293 | LUA_ASSERT(L_, lua_gettop(L_) == 2); // L_: lane "key" | 238 | LUA_ASSERT(L_, lua_gettop(L_) == 2); // L_: lane "key" |
294 | 239 | ||
295 | std::string_view const _keystr{ luaG_tostring(L_, kIdxKey) }; | 240 | std::string_view const _keystr{ luaW_tostring(L_, kIdxKey) }; |
296 | lua_settop(L_, 2); // keep only our original arguments on the stack | 241 | lua_settop(L_, 2); // keep only our original arguments on the stack |
297 | 242 | ||
298 | // look in metatable first | 243 | // look in metatable first |
299 | lua_getmetatable(L_, kIdxSelf); // L_: lane "key" mt | 244 | lua_getmetatable(L_, kIdxSelf); // L_: lane "key" mt |
300 | lua_replace(L_, -3); // L_: mt "key" | 245 | lua_replace(L_, -3); // L_: mt "key" |
301 | if (luaG_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // found something? // L_: mt value | 246 | if (luaW_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // found something? // L_: mt value |
302 | return 1; // done | 247 | return 1; // done |
303 | } | 248 | } |
304 | 249 | ||
@@ -324,7 +269,7 @@ static LUAG_FUNC(lane_index) | |||
324 | Lane* const _lane{ ToLane(L_, kIdxSelf) }; | 269 | Lane* const _lane{ ToLane(L_, kIdxSelf) }; |
325 | LUA_ASSERT(L_, lua_gettop(L_) == 2); | 270 | LUA_ASSERT(L_, lua_gettop(L_) == 2); |
326 | 271 | ||
327 | switch (luaG_type(L_, kKey)) { | 272 | switch (luaW_type(L_, kKey)) { |
328 | case LuaType::NUMBER: | 273 | case LuaType::NUMBER: |
329 | return lane_index_number(L_); // stack modification is undefined, returned value is at the top | 274 | return lane_index_number(L_); // stack modification is undefined, returned value is at the top |
330 | 275 | ||
@@ -334,19 +279,19 @@ static LUAG_FUNC(lane_index) | |||
334 | default: // unknown key | 279 | default: // unknown key |
335 | lua_getmetatable(L_, kIdxSelf); // L_: mt | 280 | lua_getmetatable(L_, kIdxSelf); // L_: mt |
336 | kCachedError.pushKey(L_); // L_: mt kCachedError | 281 | kCachedError.pushKey(L_); // L_: mt kCachedError |
337 | if (luaG_rawget(L_, StackIndex{ -2 }) != LuaType::FUNCTION) { // L_: mt error() | 282 | if (luaW_rawget(L_, StackIndex{ -2 }) != LuaType::FUNCTION) { // L_: mt error() |
338 | raise_luaL_error(L_, "INTERNAL ERROR: cached error() is a %s, not a function", luaG_typename(L_, kIdxTop).data()); | 283 | raise_luaL_error(L_, "INTERNAL ERROR: cached error() is a %s, not a function", luaW_typename(L_, kIdxTop).data()); |
339 | } | 284 | } |
340 | luaG_pushstring(L_, "Unknown key: "); // L_: mt error() "Unknown key: " | 285 | luaW_pushstring(L_, "Unknown key: "); // L_: mt error() "Unknown key: " |
341 | kCachedTostring.pushKey(L_); // L_: mt error() "Unknown key: " kCachedTostring | 286 | kCachedTostring.pushKey(L_); // L_: mt error() "Unknown key: " kCachedTostring |
342 | if (luaG_rawget(L_, StackIndex{ -4 }) != LuaType::FUNCTION) { // L_: mt error() "Unknown key: " tostring() | 287 | if (luaW_rawget(L_, StackIndex{ -4 }) != LuaType::FUNCTION) { // L_: mt error() "Unknown key: " tostring() |
343 | raise_luaL_error(L_, "INTERNAL ERROR: cached tostring() is a %s, not a function", luaG_typename(L_, kIdxTop).data()); | 288 | raise_luaL_error(L_, "INTERNAL ERROR: cached tostring() is a %s, not a function", luaW_typename(L_, kIdxTop).data()); |
344 | } | 289 | } |
345 | lua_pushvalue(L_, kKey); // L_: mt error() "Unknown key: " tostring() k | 290 | lua_pushvalue(L_, kKey); // L_: mt error() "Unknown key: " tostring() k |
346 | lua_call(L_, 1, 1); // L_: mt error() "Unknown key: " "k" | 291 | lua_call(L_, 1, 1); // L_: mt error() "Unknown key: " "k" |
347 | lua_concat(L_, 2); // L_: mt error() "Unknown key: <k>" | 292 | lua_concat(L_, 2); // L_: mt error() "Unknown key: <k>" |
348 | lua_call(L_, 1, 0); // error( "Unknown key: " .. key) -> doesn't return // L_: mt | 293 | lua_call(L_, 1, 0); // error( "Unknown key: " .. key) -> doesn't return // L_: mt |
349 | raise_luaL_error(L_, "%s[%s]: should not get here!", _lane->getDebugName().data(), luaG_typename(L_, kKey).data()); | 294 | raise_luaL_error(L_, "%s[%s]: should not get here!", _lane->getDebugName().data(), luaW_typename(L_, kKey).data()); |
350 | } | 295 | } |
351 | } | 296 | } |
352 | 297 | ||
@@ -437,7 +382,7 @@ int Lane::LuaErrorHandler(lua_State* L_) | |||
437 | // table of { "sourcefile.lua:<line>", ... } | 382 | // table of { "sourcefile.lua:<line>", ... } |
438 | // | 383 | // |
439 | lua_newtable(L_); // L_: some_error {} | 384 | lua_newtable(L_); // L_: some_error {} |
440 | StackIndex const kIdxTraceTbl{ luaG_absindex(L_, kIdxTop) }; | 385 | StackIndex const kIdxTraceTbl{ luaW_absindex(L_, kIdxTop) }; |
441 | 386 | ||
442 | // Best to start from level 1, but in some cases it might be a C function | 387 | // Best to start from level 1, but in some cases it might be a C function |
443 | // and we don't get '.currentline' for that. It's okay - just keep level | 388 | // and we don't get '.currentline' for that. It's okay - just keep level |
@@ -448,25 +393,25 @@ int Lane::LuaErrorHandler(lua_State* L_) | |||
448 | lua_getinfo(L_, _extended ? "Sln" : "Sl", &_ar); | 393 | lua_getinfo(L_, _extended ? "Sln" : "Sl", &_ar); |
449 | if (_extended) { | 394 | if (_extended) { |
450 | lua_newtable(L_); // L_: some_error {} {} | 395 | lua_newtable(L_); // L_: some_error {} {} |
451 | StackIndex const kIdxFrameTbl{ luaG_absindex(L_, kIdxTop) }; | 396 | StackIndex const kIdxFrameTbl{ luaW_absindex(L_, kIdxTop) }; |
452 | lua_pushstring(L_, _ar.source); // L_: some_error {} {} source | 397 | lua_pushstring(L_, _ar.source); // L_: some_error {} {} source |
453 | luaG_setfield(L_, kIdxFrameTbl, std::string_view{ "source" }); // L_: some_error {} {} | 398 | luaW_setfield(L_, kIdxFrameTbl, std::string_view{ "source" }); // L_: some_error {} {} |
454 | 399 | ||
455 | lua_pushinteger(L_, _ar.currentline); // L_: some_error {} {} currentline | 400 | lua_pushinteger(L_, _ar.currentline); // L_: some_error {} {} currentline |
456 | luaG_setfield(L_, kIdxFrameTbl, std::string_view{ "currentline" }); // L_: some_error {} {} | 401 | luaW_setfield(L_, kIdxFrameTbl, std::string_view{ "currentline" }); // L_: some_error {} {} |
457 | 402 | ||
458 | lua_pushstring(L_, _ar.name ? _ar.name : "<?>"); // L_: some_error {} {} name | 403 | lua_pushstring(L_, _ar.name ? _ar.name : "<?>"); // L_: some_error {} {} name |
459 | luaG_setfield(L_, kIdxFrameTbl, std::string_view{ "name" }); // L_: some_error {} {} | 404 | luaW_setfield(L_, kIdxFrameTbl, std::string_view{ "name" }); // L_: some_error {} {} |
460 | 405 | ||
461 | lua_pushstring(L_, _ar.namewhat); // L_: some_error {} {} namewhat | 406 | lua_pushstring(L_, _ar.namewhat); // L_: some_error {} {} namewhat |
462 | luaG_setfield(L_, kIdxFrameTbl, std::string_view{ "namewhat" }); // L_: some_error {} {} | 407 | luaW_setfield(L_, kIdxFrameTbl, std::string_view{ "namewhat" }); // L_: some_error {} {} |
463 | 408 | ||
464 | lua_pushstring(L_, _ar.what); // L_: some_error {} {} what | 409 | lua_pushstring(L_, _ar.what); // L_: some_error {} {} what |
465 | luaG_setfield(L_, kIdxFrameTbl, std::string_view{ "what" }); // L_: some_error {} {} | 410 | luaW_setfield(L_, kIdxFrameTbl, std::string_view{ "what" }); // L_: some_error {} {} |
466 | } else if (_ar.currentline > 0) { | 411 | } else if (_ar.currentline > 0) { |
467 | luaG_pushstring(L_, "%s:%d", _ar.short_src, _ar.currentline); // L_: some_error {} "blah:blah" | 412 | luaW_pushstring(L_, "%s:%d", _ar.short_src, _ar.currentline); // L_: some_error {} "blah:blah" |
468 | } else { | 413 | } else { |
469 | luaG_pushstring(L_, "%s:?", _ar.short_src); // L_: some_error {} "blah" | 414 | luaW_pushstring(L_, "%s:?", _ar.short_src); // L_: some_error {} "blah" |
470 | } | 415 | } |
471 | lua_rawseti(L_, kIdxTraceTbl, static_cast<lua_Integer>(_n)); // L_: some_error {} | 416 | lua_rawseti(L_, kIdxTraceTbl, static_cast<lua_Integer>(_n)); // L_: some_error {} |
472 | } | 417 | } |
@@ -489,6 +434,7 @@ static int PushStackTrace(lua_State* const L_, Lane::ErrorTraceLevel const error | |||
489 | StackIndex const _top{ lua_gettop(L_) }; | 434 | StackIndex const _top{ lua_gettop(L_) }; |
490 | switch (rc_) { | 435 | switch (rc_) { |
491 | case LuaError::OK: // no error, body return values are on the stack | 436 | case LuaError::OK: // no error, body return values are on the stack |
437 | case LuaError::YIELD: | ||
492 | break; | 438 | break; |
493 | 439 | ||
494 | case LuaError::ERRRUN: // cancellation or a runtime error | 440 | case LuaError::ERRRUN: // cancellation or a runtime error |
@@ -502,7 +448,7 @@ static int PushStackTrace(lua_State* const L_, Lane::ErrorTraceLevel const error | |||
502 | 448 | ||
503 | // For cancellation the error message is kCancelError, and a stack trace isn't placed | 449 | // For cancellation the error message is kCancelError, and a stack trace isn't placed |
504 | // For other errors, the message can be whatever was thrown, and we should have a stack trace table | 450 | // For other errors, the message can be whatever was thrown, and we should have a stack trace table |
505 | LUA_ASSERT(L_, luaG_type(L_, StackIndex{ 1 + stk_base_ }) == (kCancelError.equals(L_, stk_base_) ? LuaType::NIL : LuaType::TABLE)); | 451 | LUA_ASSERT(L_, luaW_type(L_, StackIndex{ 1 + stk_base_ }) == (kCancelError.equals(L_, stk_base_) ? LuaType::NIL : LuaType::TABLE)); |
506 | // Just leaving the stack trace table on the stack is enough to get it through to the master. | 452 | // Just leaving the stack trace table on the stack is enough to get it through to the master. |
507 | } else { | 453 | } else { |
508 | // any kind of error can be thrown with error(), or through a lane/linda cancellation | 454 | // any kind of error can be thrown with error(), or through a lane/linda cancellation |
@@ -514,7 +460,7 @@ static int PushStackTrace(lua_State* const L_, Lane::ErrorTraceLevel const error | |||
514 | case LuaError::ERRERR: // error while running the error handler (if any, for example an out-of-memory condition) | 460 | case LuaError::ERRERR: // error while running the error handler (if any, for example an out-of-memory condition) |
515 | default: | 461 | default: |
516 | // the Lua core provides a string error message in those situations | 462 | // the Lua core provides a string error message in those situations |
517 | LUA_ASSERT(L_, (lua_gettop(L_) == stk_base_) && (luaG_type(L_, stk_base_) == LuaType::STRING)); | 463 | LUA_ASSERT(L_, (lua_gettop(L_) == stk_base_) && (luaW_type(L_, stk_base_) == LuaType::STRING)); |
518 | break; | 464 | break; |
519 | } | 465 | } |
520 | return lua_gettop(L_) - _top; // either 0 or 1 | 466 | return lua_gettop(L_) - _top; // either 0 or 1 |
@@ -563,7 +509,7 @@ static LuaError run_finalizers(Lane* const lane_, Lane::ErrorTraceLevel const er | |||
563 | LUA_ASSERT(_L, lua_isfunction(_L, -1)); | 509 | LUA_ASSERT(_L, lua_isfunction(_L, -1)); |
564 | if (lua_rc_ != LuaError::OK) { // we have <error>, [trace] on the thread stack | 510 | if (lua_rc_ != LuaError::OK) { // we have <error>, [trace] on the thread stack |
565 | LUA_ASSERT(_L, lane_->nresults == 1 || lane_->nresults == 2); | 511 | LUA_ASSERT(_L, lane_->nresults == 1 || lane_->nresults == 2); |
566 | //std::string_view const _err_msg{ luaG_tostring(_L, 1) }; | 512 | //std::string_view const _err_msg{ luaW_tostring(_L, 1) }; |
567 | if (lane_->isCoroutine()) { | 513 | if (lane_->isCoroutine()) { |
568 | // transfer them on the main state | 514 | // transfer them on the main state |
569 | lua_pushvalue(lane_->L, 1); | 515 | lua_pushvalue(lane_->L, 1); |
@@ -615,7 +561,7 @@ static LuaError run_finalizers(Lane* const lane_, Lane::ErrorTraceLevel const er | |||
615 | 561 | ||
616 | if (lane_->isCoroutine()) { | 562 | if (lane_->isCoroutine()) { |
617 | // only the coroutine thread should remain on the master state when we are done | 563 | // only the coroutine thread should remain on the master state when we are done |
618 | LUA_ASSERT(_L, lua_gettop(_L) == 1 && luaG_type(_L, StackIndex{ 1 }) == LuaType::THREAD); | 564 | LUA_ASSERT(_L, lua_gettop(_L) == 1 && luaW_type(_L, StackIndex{ 1 }) == LuaType::THREAD); |
619 | } | 565 | } |
620 | 566 | ||
621 | return _rc; | 567 | return _rc; |
@@ -711,7 +657,7 @@ static void lane_main(Lane* const lane_) | |||
711 | LuaError _rc{ LuaError::ERRRUN }; | 657 | LuaError _rc{ LuaError::ERRRUN }; |
712 | if (lane_->status.load(std::memory_order_acquire) == Lane::Pending) { // nothing wrong happened during preparation, we can work | 658 | if (lane_->status.load(std::memory_order_acquire) == Lane::Pending) { // nothing wrong happened during preparation, we can work |
713 | // At this point, the lane function and arguments are on the stack, possibly preceded by the error handler | 659 | // At this point, the lane function and arguments are on the stack, possibly preceded by the error handler |
714 | int const _errorHandlerCount{ lane_->errorHandlerCount() }; | 660 | int const _errorHandlerCount{ lane_->errorHandlerCount() }; // no error handler for coroutines, ever. |
715 | int _nargs{ lua_gettop(_L) - 1 - _errorHandlerCount }; | 661 | int _nargs{ lua_gettop(_L) - 1 - _errorHandlerCount }; |
716 | { | 662 | { |
717 | std::unique_lock _guard{ lane_->doneMutex }; | 663 | std::unique_lock _guard{ lane_->doneMutex }; |
@@ -724,27 +670,60 @@ static void lane_main(Lane* const lane_) | |||
724 | lane_->nresults = lua_gettop(_L) - _errorHandlerCount; | 670 | lane_->nresults = lua_gettop(_L) - _errorHandlerCount; |
725 | } else { | 671 | } else { |
726 | // S and L are different: we run as a coroutine in Lua thread L created in state S | 672 | // S and L are different: we run as a coroutine in Lua thread L created in state S |
673 | bool _shouldClose{ false }; | ||
727 | do { | 674 | do { |
728 | // starting with Lua 5.4, lua_resume can leave more stuff on the stack below the actual yielded values. | 675 | // starting with Lua 5.4, lua_resume can leave more stuff on the stack below the actual yielded values. |
729 | // that's why we have lane_->nresults | 676 | // that's why we have lane_->nresults |
730 | _rc = luaG_resume(_L, nullptr, _nargs, &lane_->nresults); // L: eh? ... retvals|err... | 677 | _rc = luaW_resume(_L, nullptr, _nargs, &lane_->nresults); // L: ... retvals|err... |
731 | if (_rc == LuaError::YIELD) { | 678 | if (_rc == LuaError::YIELD) { |
732 | // change our status to suspended, and wait until someone wants us to resume | ||
733 | std::unique_lock _guard{ lane_->doneMutex }; | ||
734 | lane_->status.store(Lane::Suspended, std::memory_order_release); // Running -> Suspended | ||
735 | lane_->doneCondVar.notify_one(); | ||
736 | // wait until the user wants us to resume | ||
737 | // TODO: do I update waiting_on or not, so that the lane can be woken by cancellation requests here? | ||
738 | // lane_->waiting_on = &lane_->doneCondVar; | ||
739 | lane_->doneCondVar.wait(_guard, [lane_]() { return lane_->status.load(std::memory_order_acquire) == Lane::Resuming; }); | ||
740 | // here lane_->doneMutex is locked again | ||
741 | // lane_->waiting_on = nullptr; | ||
742 | lane_->status.store(Lane::Running, std::memory_order_release); // Resuming -> Running | ||
743 | // on the stack we find the values pushed by lane:resume() | 679 | // on the stack we find the values pushed by lane:resume() |
744 | _nargs = lua_gettop(_L); | 680 | _nargs = lua_gettop(_L); |
681 | if (std::unique_lock _guard{ lane_->doneMutex }; true) { | ||
682 | // change our status to suspended, and wait until someone wants us to resume | ||
683 | lane_->status.store(Lane::Suspended, std::memory_order_release); // Running -> Suspended | ||
684 | lane_->doneCondVar.notify_one(); | ||
685 | // wait until the user wants us to resume | ||
686 | // update waiting_on, so that the lane can be woken by cancellation requests here | ||
687 | lane_->waiting_on = &lane_->doneCondVar; | ||
688 | lane_->doneCondVar.wait(_guard, | ||
689 | [lane_,&_shouldClose]() | ||
690 | { | ||
691 | auto const _status{ lane_->status.load(std::memory_order_acquire) }; | ||
692 | // wait interrupted because of a cancellation or join request means we have to abort the resume loop | ||
693 | _shouldClose = (_status == Lane::Closing); | ||
694 | return _shouldClose || (_status == Lane::Resuming) || (lane_->cancelRequest.load(std::memory_order_relaxed) != CancelRequest::None); | ||
695 | } | ||
696 | ); | ||
697 | // here lane_->doneMutex is locked again | ||
698 | lane_->waiting_on = nullptr; | ||
699 | lane_->status.store(Lane::Running, std::memory_order_release); // Resuming -> Running | ||
700 | } | ||
701 | } else { | ||
702 | _shouldClose = true; | ||
745 | } | 703 | } |
746 | } while (_rc == LuaError::YIELD); | 704 | } while (!_shouldClose); |
747 | if (_rc != LuaError::OK) { // : err... | 705 | if (_rc == LuaError::YIELD) { |
706 | #if LUA_VERSION_NUM >= 504 | ||
707 | lua_State* const _S{ lane_->S }; | ||
708 | STACK_CHECK_START_REL(_S, 0); | ||
709 | // lua_closethread cleans the stack, meaning we lose the yielded values! -> store | ||
710 | lua_xmove(_L, _S, lane_->nresults); | ||
711 | // lane is cancelled before completion (for example at Lanes shutdown), close everything | ||
712 | _rc = static_cast<LuaError>(lua_closethread(_L, nullptr)); // L: ... retvals|err <close_err> | ||
713 | // then restore the yielded values | ||
714 | if (_rc == LuaError::OK) { | ||
715 | lua_xmove(_S, _L, lane_->nresults); | ||
716 | } else { | ||
717 | lua_pop(_S, lane_->nresults); | ||
718 | } | ||
719 | STACK_CHECK(_S, 0); | ||
720 | |||
721 | #else // LUA_VERSION_NUM | ||
722 | // Lua prior to 5.4 do not have lua_closethread. | ||
723 | _rc = LuaError::OK; | ||
724 | #endif // LUA_VERSION_NUM | ||
725 | } | ||
726 | if (_rc != LuaError::OK) { // an error occurred // L: err... | ||
748 | // for some reason, in my tests with Lua 5.4, when the coroutine raises an error, I have 3 copies of it on the stack | 727 | // for some reason, in my tests with Lua 5.4, when the coroutine raises an error, I have 3 copies of it on the stack |
749 | // or false + the error message when running Lua 5.1 | 728 | // or false + the error message when running Lua 5.1 |
750 | // since the rest of our code wants only the error message, let us keep only the latter. | 729 | // since the rest of our code wants only the error message, let us keep only the latter. |
@@ -769,7 +748,7 @@ static void lane_main(Lane* const lane_) | |||
769 | // in case of error and if it exists, fetch stack trace from registry and push it | 748 | // in case of error and if it exists, fetch stack trace from registry and push it |
770 | lane_->nresults += PushStackTrace(_L, lane_->errorTraceLevel, _rc, StackIndex{ 1 }); // L: retvals|error [trace] | 749 | lane_->nresults += PushStackTrace(_L, lane_->errorTraceLevel, _rc, StackIndex{ 1 }); // L: retvals|error [trace] |
771 | 750 | ||
772 | DEBUGSPEW_CODE(DebugSpew(lane_->U) << "Lane " << _L << " body: " << GetErrcodeName(_rc) << " (" << (kCancelError.equals(_L, StackIndex{ 1 }) ? "cancelled" : luaG_typename(_L, StackIndex{ 1 })) << ")" << std::endl); | 751 | DEBUGSPEW_CODE(DebugSpew(lane_->U) << "Lane " << _L << " body: " << GetErrcodeName(_rc) << " (" << (kCancelError.equals(_L, StackIndex{ 1 }) ? "cancelled" : luaW_typename(_L, StackIndex{ 1 })) << ")" << std::endl); |
773 | // Call finalizers, if the script has set them up. | 752 | // Call finalizers, if the script has set them up. |
774 | // If the lane is not a coroutine, there is only a regular state, so everything is the same whether we use S or L. | 753 | // If the lane is not a coroutine, there is only a regular state, so everything is the same whether we use S or L. |
775 | // If the lane is a coroutine, this has to be done from the master state (S), not the thread (L), because we can't lua_pcall in a thread state | 754 | // If the lane is a coroutine, this has to be done from the master state (S), not the thread (L), because we can't lua_pcall in a thread state |
@@ -783,10 +762,12 @@ static void lane_main(Lane* const lane_) | |||
783 | if (lane_->selfdestructRemove()) { // check and remove (under lock!) | 762 | if (lane_->selfdestructRemove()) { // check and remove (under lock!) |
784 | // We're a free-running thread and no-one is there to clean us up. | 763 | // We're a free-running thread and no-one is there to clean us up. |
785 | lane_->closeState(); | 764 | lane_->closeState(); |
786 | lane_->U->selfdestructMutex.lock(); | 765 | |
787 | // done with lua_close(), terminal shutdown sequence may proceed | 766 | // let's try not to crash if the lane didn't terminate gracefully and the Universe met its end |
788 | lane_->U->selfdestructingCount.fetch_sub(1, std::memory_order_release); | 767 | if (!lane_->flaggedAfterUniverseGC.load(std::memory_order_relaxed)) { |
789 | lane_->U->selfdestructMutex.unlock(); | 768 | // done with lua_close(), terminal shutdown sequence may proceed |
769 | lane_->U->selfdestructingCount.fetch_sub(1, std::memory_order_release); | ||
770 | } | ||
790 | 771 | ||
791 | // we destroy ourselves, therefore our thread member too, from inside the thread body | 772 | // we destroy ourselves, therefore our thread member too, from inside the thread body |
792 | // detach so that we don't try to join, as this doesn't seem a good idea | 773 | // detach so that we don't try to join, as this doesn't seem a good idea |
@@ -807,6 +788,8 @@ static void lane_main(Lane* const lane_) | |||
807 | // ################################################################################################# | 788 | // ################################################################################################# |
808 | 789 | ||
809 | #if LUA_VERSION_NUM >= 504 | 790 | #if LUA_VERSION_NUM >= 504 |
791 | |||
792 | // __close(lane_ud, <err>) | ||
810 | static LUAG_FUNC(lane_close) | 793 | static LUAG_FUNC(lane_close) |
811 | { | 794 | { |
812 | [[maybe_unused]] Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) }; // L_: lane err|nil | 795 | [[maybe_unused]] Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) }; // L_: lane err|nil |
@@ -814,12 +797,13 @@ static LUAG_FUNC(lane_close) | |||
814 | lua_settop(L_, 1); // L_: lane | 797 | lua_settop(L_, 1); // L_: lane |
815 | 798 | ||
816 | // no error if the lane body doesn't return a non-nil first value | 799 | // no error if the lane body doesn't return a non-nil first value |
817 | luaG_pushstring(L_, "close"); // L_: lane "close" | 800 | luaW_pushstring(L_, "close"); // L_: lane "close" |
818 | lua_pushcclosure(L_, LG_lane_join, 1); // L_: lane join() | 801 | lua_pushcclosure(L_, LG_lane_join, 1); // L_: lane join() |
819 | lua_insert(L_, 1); // L_: join() lane | 802 | lua_insert(L_, 1); // L_: join() lane |
820 | lua_call(L_, 1, LUA_MULTRET); // L_: join() results | 803 | lua_call(L_, 1, LUA_MULTRET); // L_: join() results |
821 | return lua_gettop(L_); | 804 | return lua_gettop(L_); |
822 | } | 805 | } |
806 | |||
823 | #endif // LUA_VERSION_NUM >= 504 | 807 | #endif // LUA_VERSION_NUM >= 504 |
824 | 808 | ||
825 | // ################################################################################################# | 809 | // ################################################################################################# |
@@ -843,9 +827,9 @@ static LUAG_FUNC(lane_gc) | |||
843 | // if there a gc callback? | 827 | // if there a gc callback? |
844 | lua_getiuservalue(L_, StackIndex{ 1 }, UserValueIndex{ 1 }); // L_: ud uservalue | 828 | lua_getiuservalue(L_, StackIndex{ 1 }, UserValueIndex{ 1 }); // L_: ud uservalue |
845 | kLaneGC.pushKey(L_); // L_: ud uservalue __gc | 829 | kLaneGC.pushKey(L_); // L_: ud uservalue __gc |
846 | if (luaG_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // L_: ud uservalue gc_cb|nil | 830 | if (luaW_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // L_: ud uservalue gc_cb|nil |
847 | lua_remove(L_, -2); // L_: ud gc_cb|nil | 831 | lua_remove(L_, -2); // L_: ud gc_cb|nil |
848 | luaG_pushstring(L_, _lane->getDebugName()); // L_: ud gc_cb name | 832 | luaW_pushstring(L_, _lane->getDebugName()); // L_: ud gc_cb name |
849 | _have_gc_cb = true; | 833 | _have_gc_cb = true; |
850 | } else { | 834 | } else { |
851 | lua_pop(L_, 2); // L_: ud | 835 | lua_pop(L_, 2); // L_: ud |
@@ -856,7 +840,7 @@ static LUAG_FUNC(lane_gc) | |||
856 | // still running: will have to be cleaned up later | 840 | // still running: will have to be cleaned up later |
857 | _lane->selfdestructAdd(); | 841 | _lane->selfdestructAdd(); |
858 | if (_have_gc_cb) { | 842 | if (_have_gc_cb) { |
859 | luaG_pushstring(L_, "selfdestruct"); // L_: ud gc_cb name status | 843 | luaW_pushstring(L_, "selfdestruct"); // L_: ud gc_cb name status |
860 | lua_call(L_, 2, 0); // L_: ud | 844 | lua_call(L_, 2, 0); // L_: ud |
861 | } | 845 | } |
862 | return 0; | 846 | return 0; |
@@ -871,7 +855,7 @@ static LUAG_FUNC(lane_gc) | |||
871 | 855 | ||
872 | // do this after lane cleanup in case the callback triggers an error | 856 | // do this after lane cleanup in case the callback triggers an error |
873 | if (_have_gc_cb) { | 857 | if (_have_gc_cb) { |
874 | luaG_pushstring(L_, "closed"); // L_: ud gc_cb name status | 858 | luaW_pushstring(L_, "closed"); // L_: ud gc_cb name status |
875 | lua_call(L_, 2, 0); // L_: ud | 859 | lua_call(L_, 2, 0); // L_: ud |
876 | } | 860 | } |
877 | return 0; | 861 | return 0; |
@@ -916,7 +900,7 @@ void Lane::applyDebugName() const | |||
916 | { | 900 | { |
917 | if constexpr (HAVE_DECODA_SUPPORT()) { | 901 | if constexpr (HAVE_DECODA_SUPPORT()) { |
918 | // to see VM name in Decoda debugger Virtual Machine window | 902 | // to see VM name in Decoda debugger Virtual Machine window |
919 | luaG_pushstring(L, debugName); // L: ... "name" | 903 | luaW_pushstring(L, debugName); // L: ... "name" |
920 | lua_setglobal(L, "decoda_name"); // L: ... | 904 | lua_setglobal(L, "decoda_name"); // L: ... |
921 | } | 905 | } |
922 | // and finally set the OS thread name | 906 | // and finally set the OS thread name |
@@ -941,7 +925,8 @@ CancelResult Lane::cancel(CancelOp const op_, std::chrono::time_point<std::chron | |||
941 | 925 | ||
942 | // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here | 926 | // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here |
943 | // We can read status without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) | 927 | // We can read status without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) |
944 | if (status.load(std::memory_order_acquire) >= Lane::Done) { | 928 | auto const _status{ status.load(std::memory_order_acquire) }; |
929 | if (_status == Lane::Done || _status == Lane::Error || _status == Lane::Cancelled) { | ||
945 | // say "ok" by default, including when lane is already done | 930 | // say "ok" by default, including when lane is already done |
946 | return CancelResult::Cancelled; | 931 | return CancelResult::Cancelled; |
947 | } | 932 | } |
@@ -968,14 +953,15 @@ CancelResult Lane::internalCancel(CancelRequest const rq_, std::chrono::time_poi | |||
968 | // lane_->thread.get_stop_source().request_stop(); | 953 | // lane_->thread.get_stop_source().request_stop(); |
969 | } | 954 | } |
970 | if (wakeLane_ == WakeLane::Yes) { // wake the thread so that execution returns from any pending linda operation if desired | 955 | if (wakeLane_ == WakeLane::Yes) { // wake the thread so that execution returns from any pending linda operation if desired |
971 | if (status.load(std::memory_order_acquire) == Lane::Waiting) { // waiting_on is updated under control of status acquire/release semantics | 956 | auto const _status{ status.load(std::memory_order_acquire) }; |
957 | if (_status == Lane::Waiting || _status == Lane::Suspended) { // waiting_on is updated under control of status acquire/release semantics | ||
972 | if (std::condition_variable* const _waiting_on{ waiting_on }) { | 958 | if (std::condition_variable* const _waiting_on{ waiting_on }) { |
973 | _waiting_on->notify_all(); | 959 | _waiting_on->notify_all(); |
974 | } | 960 | } |
975 | } | 961 | } |
976 | } | 962 | } |
977 | // wait until the lane stops working with its state (either Suspended or Done+) | 963 | // wait until the lane stops working with its state (either Suspended or Done+) |
978 | CancelResult const result{ waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout }; | 964 | CancelResult const result{ waitForCompletion(until_, false) ? CancelResult::Cancelled : CancelResult::Timeout }; |
979 | return result; | 965 | return result; |
980 | } | 966 | } |
981 | 967 | ||
@@ -1027,7 +1013,7 @@ void Lane::PushMetatable(lua_State* const L_) | |||
1027 | { | 1013 | { |
1028 | STACK_CHECK_START_REL(L_, 0); | 1014 | STACK_CHECK_START_REL(L_, 0); |
1029 | if (luaL_newmetatable(L_, kLaneMetatableName.data())) { // L_: mt | 1015 | if (luaL_newmetatable(L_, kLaneMetatableName.data())) { // L_: mt |
1030 | luaG_registerlibfuncs(L_, local::sLaneFunctions); | 1016 | luaW_registerlibfuncs(L_, local::sLaneFunctions); |
1031 | // cache error() and tostring() | 1017 | // cache error() and tostring() |
1032 | kCachedError.pushKey(L_); // L_: mt kCachedError | 1018 | kCachedError.pushKey(L_); // L_: mt kCachedError |
1033 | lua_getglobal(L_, "error"); // L_: mt kCachedError error() | 1019 | lua_getglobal(L_, "error"); // L_: mt kCachedError error() |
@@ -1036,7 +1022,7 @@ void Lane::PushMetatable(lua_State* const L_) | |||
1036 | lua_getglobal(L_, "tostring"); // L_: mt kCachedTostring tostring() | 1022 | lua_getglobal(L_, "tostring"); // L_: mt kCachedTostring tostring() |
1037 | lua_rawset(L_, -3); // L_: mt | 1023 | lua_rawset(L_, -3); // L_: mt |
1038 | // hide the actual metatable from getmetatable() | 1024 | // hide the actual metatable from getmetatable() |
1039 | luaG_pushstring(L_, kLaneMetatableName); // L_: mt "Lane" | 1025 | luaW_pushstring(L_, kLaneMetatableName); // L_: mt "Lane" |
1040 | lua_setfield(L_, -2, "__metatable"); // L_: mt | 1026 | lua_setfield(L_, -2, "__metatable"); // L_: mt |
1041 | } | 1027 | } |
1042 | STACK_CHECK(L_, 1); | 1028 | STACK_CHECK(L_, 1); |
@@ -1049,7 +1035,7 @@ void Lane::pushStatusString(lua_State* const L_) const | |||
1049 | std::string_view const _str{ threadStatusString() }; | 1035 | std::string_view const _str{ threadStatusString() }; |
1050 | LUA_ASSERT(L_, !_str.empty()); | 1036 | LUA_ASSERT(L_, !_str.empty()); |
1051 | 1037 | ||
1052 | luaG_pushstring(L_, _str); | 1038 | luaW_pushstring(L_, _str); |
1053 | } | 1039 | } |
1054 | 1040 | ||
1055 | // ################################################################################################# | 1041 | // ################################################################################################# |
@@ -1108,12 +1094,88 @@ void Lane::pushIndexedResult(lua_State* const L_, int const key_) const | |||
1108 | // ################################################################################################# | 1094 | // ################################################################################################# |
1109 | 1095 | ||
1110 | [[nodiscard]] | 1096 | [[nodiscard]] |
1097 | int Lane::pushStoredResults(lua_State* const L_) const | ||
1098 | { | ||
1099 | STACK_CHECK_START_ABS(L_, 1); // should only have the lane UD on the stack | ||
1100 | static constexpr StackIndex kIdxSelf{ 1 }; | ||
1101 | static constexpr UserValueIndex kUvResults{ 1 }; | ||
1102 | LUA_ASSERT(L_, ToLane(L_, kIdxSelf) == this); // L_: lane | ||
1103 | lua_getiuservalue(L_, kIdxSelf, kUvResults); // L_: lane {uv} | ||
1104 | lua_rawgeti(L_, kIdxTop, 0); // L_: lane {uv} stored | ||
1105 | int const _stored{ static_cast<int>(lua_tointeger(L_, kIdxTop)) }; | ||
1106 | lua_pop(L_, 1); // L_: lane {uv} | ||
1107 | |||
1108 | int _ret{}; | ||
1109 | STACK_GROW(L_, std::max(3, _stored + 1)); | ||
1110 | switch (status.load(std::memory_order_acquire)) { | ||
1111 | case Lane::Suspended: | ||
1112 | raise_luaL_error(L_, "INTERNAL ERROR: SHOULD NEVER BE SUSPENDED HERE"); | ||
1113 | break; | ||
1114 | |||
1115 | case Lane::Done: // got regular return values | ||
1116 | if (_stored > 0) { | ||
1117 | for (int _i = 2; _i <= _stored; ++_i) { | ||
1118 | lua_rawgeti(L_, 2, _i); // L_: lane {uv} results2...N | ||
1119 | } | ||
1120 | lua_rawgeti(L_, 2, 1); // L_: lane {uv} results2...N result1 | ||
1121 | lua_replace(L_, 2); // L_: lane results | ||
1122 | } else { | ||
1123 | lua_pop(L_, 1); // L_: lane | ||
1124 | } | ||
1125 | // we precede the lane body returned values with boolean true | ||
1126 | lua_pushboolean(L_, 1); // L_: lane results true | ||
1127 | lua_replace(L_, 1); // L_: true results | ||
1128 | _ret = _stored + 1; | ||
1129 | STACK_CHECK(L_, _ret); | ||
1130 | break; | ||
1131 | |||
1132 | case Lane::Error: | ||
1133 | { | ||
1134 | LUA_ASSERT(L_, _stored == 2 || _stored == 3); // contains nil error [trace] | ||
1135 | lua_rawgeti(L_, 2, 2); // L_: lane {uv} <error> | ||
1136 | lua_rawgeti(L_, 2, 3); // L_: lane {uv} <error> <trace>|nil | ||
1137 | if (lua_isnil(L_, -1)) { | ||
1138 | lua_replace(L_, 2); // L_: lane nil <error> | ||
1139 | } else { | ||
1140 | lua_rawgeti(L_, 2, 1); // L_: lane {uv} <error> <trace> nil | ||
1141 | lua_replace(L_, 2); // L_: lane nil <error> <trace> | ||
1142 | } | ||
1143 | _ret = _stored; // 2 or 3 | ||
1144 | STACK_CHECK(L_, _ret + 1); // stack still contains the lane UD below | ||
1145 | } | ||
1146 | break; | ||
1147 | |||
1148 | case Lane::Cancelled: | ||
1149 | { | ||
1150 | LUA_ASSERT(L_, _stored == 2); | ||
1151 | lua_rawgeti(L_, 2, 2); // L_: lane {uv} cancel_error | ||
1152 | lua_rawgeti(L_, 2, 1); // L_: lane {uv} cancel_error nil | ||
1153 | lua_replace(L_, -3); // L_: lane nil cancel_error | ||
1154 | LUA_ASSERT(L_, lua_isnil(L_, -2) && kCancelError.equals(L_, kIdxTop)); | ||
1155 | _ret = 2; | ||
1156 | STACK_CHECK(L_, _ret + 1); // stack still contains the lane UD below | ||
1157 | } | ||
1158 | break; | ||
1159 | |||
1160 | default: | ||
1161 | DEBUGSPEW_CODE(DebugSpew(nullptr) << "Unknown Lane status: " << static_cast<int>(_lane->status.load(std::memory_order_relaxed)) << std::endl); | ||
1162 | LUA_ASSERT(L_, false); | ||
1163 | _ret = 0; | ||
1164 | STACK_CHECK(L_, _ret); | ||
1165 | } | ||
1166 | LUA_ASSERT(L_, lua_gettop(L_) >= _ret); | ||
1167 | return _ret; | ||
1168 | } | ||
1169 | |||
1170 | // ################################################################################################# | ||
1171 | |||
1172 | [[nodiscard]] | ||
1111 | std::string_view Lane::pushErrorTraceLevel(lua_State* L_) const | 1173 | std::string_view Lane::pushErrorTraceLevel(lua_State* L_) const |
1112 | { | 1174 | { |
1113 | std::string_view const _str{ errorTraceLevelString() }; | 1175 | std::string_view const _str{ errorTraceLevelString() }; |
1114 | LUA_ASSERT(L_, !_str.empty()); | 1176 | LUA_ASSERT(L_, !_str.empty()); |
1115 | 1177 | ||
1116 | return luaG_pushstring(L_, _str); | 1178 | return luaW_pushstring(L_, _str); |
1117 | } | 1179 | } |
1118 | 1180 | ||
1119 | // ################################################################################################# | 1181 | // ################################################################################################# |
@@ -1124,7 +1186,7 @@ void Lane::resetResultsStorage(lua_State* const L_, StackIndex const self_idx_) | |||
1124 | { | 1186 | { |
1125 | STACK_GROW(L_, 4); | 1187 | STACK_GROW(L_, 4); |
1126 | STACK_CHECK_START_REL(L_, 0); | 1188 | STACK_CHECK_START_REL(L_, 0); |
1127 | StackIndex const _self_idx{ luaG_absindex(L_, self_idx_) }; | 1189 | StackIndex const _self_idx{ luaW_absindex(L_, self_idx_) }; |
1128 | LUA_ASSERT(L_, ToLane(L_, _self_idx) == this); // L_: ... self ... | 1190 | LUA_ASSERT(L_, ToLane(L_, _self_idx) == this); // L_: ... self ... |
1129 | // create the new table | 1191 | // create the new table |
1130 | lua_newtable(L_); // L_: ... self ... {} | 1192 | lua_newtable(L_); // L_: ... self ... {} |
@@ -1158,7 +1220,7 @@ void Lane::securizeDebugName(lua_State* const L_) | |||
1158 | lua_newtable(L_); // L_: lane ... {uv} {} | 1220 | lua_newtable(L_); // L_: lane ... {uv} {} |
1159 | { | 1221 | { |
1160 | std::lock_guard<std::mutex> _guard{ debugNameMutex }; | 1222 | std::lock_guard<std::mutex> _guard{ debugNameMutex }; |
1161 | debugName = luaG_pushstring(L_, debugName); // L_: lane ... {uv} {} name | 1223 | debugName = luaW_pushstring(L_, debugName); // L_: lane ... {uv} {} name |
1162 | } | 1224 | } |
1163 | lua_rawset(L_, -3); // L_: lane ... {uv} | 1225 | lua_rawset(L_, -3); // L_: lane ... {uv} |
1164 | lua_pop(L_, 1); // L_: lane | 1226 | lua_pop(L_, 1); // L_: lane |
@@ -1167,11 +1229,11 @@ void Lane::securizeDebugName(lua_State* const L_) | |||
1167 | 1229 | ||
1168 | // ################################################################################################# | 1230 | // ################################################################################################# |
1169 | 1231 | ||
1170 | void Lane::startThread(int const priority_) | 1232 | void Lane::startThread(lua_State* const L_, int const priority_, NativePrioFlag native_) |
1171 | { | 1233 | { |
1172 | thread = std::thread([this]() { lane_main(this); }); | 1234 | thread = std::thread([this]() { lane_main(this); }); |
1173 | if (priority_ != kThreadPrioDefault) { | 1235 | if (priority_ != kThreadPrioDefault) { |
1174 | THREAD_SET_PRIORITY(thread, priority_, U->sudo); | 1236 | THREAD_SET_PRIORITY(L_, thread, priority_, native_, U->sudo); |
1175 | } | 1237 | } |
1176 | } | 1238 | } |
1177 | 1239 | ||
@@ -1181,13 +1243,13 @@ void Lane::storeDebugName(std::string_view const& name_) | |||
1181 | { | 1243 | { |
1182 | STACK_CHECK_START_REL(L, 0); | 1244 | STACK_CHECK_START_REL(L, 0); |
1183 | // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global... | 1245 | // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global... |
1184 | kLaneNameRegKey.setValue(L, [name = name_](lua_State* L_) { luaG_pushstring(L_, name); }); | 1246 | kLaneNameRegKey.setValue(L, [name = name_](lua_State* L_) { luaW_pushstring(L_, name); }); |
1185 | STACK_CHECK(L, 0); | 1247 | STACK_CHECK(L, 0); |
1186 | kLaneNameRegKey.pushValue(L); // L: ... "name" ... | 1248 | kLaneNameRegKey.pushValue(L); // L: ... "name" ... |
1187 | // keep a direct view on the stored string | 1249 | // keep a direct view on the stored string |
1188 | { | 1250 | { |
1189 | std::lock_guard<std::mutex> _guard{ debugNameMutex }; | 1251 | std::lock_guard<std::mutex> _guard{ debugNameMutex }; |
1190 | debugName = luaG_tostring(L, kIdxTop); | 1252 | debugName = luaW_tostring(L, kIdxTop); |
1191 | } | 1253 | } |
1192 | lua_pop(L, 1); | 1254 | lua_pop(L, 1); |
1193 | STACK_CHECK(L, 0); | 1255 | STACK_CHECK(L, 0); |
@@ -1207,20 +1269,21 @@ int Lane::storeResults(lua_State* const L_) | |||
1207 | lua_getiuservalue(L_, kIdxSelf, UserValueIndex{ 1 }); // L_: lane ... {uv} | 1269 | lua_getiuservalue(L_, kIdxSelf, UserValueIndex{ 1 }); // L_: lane ... {uv} |
1208 | StackIndex const _tidx{ lua_gettop(L_) }; | 1270 | StackIndex const _tidx{ lua_gettop(L_) }; |
1209 | 1271 | ||
1210 | int _stored{}; | 1272 | // if the results were already stored from a previous indexing, just say how many values we have in store |
1211 | if (nresults == 0) { | 1273 | if (!L) { |
1212 | lua_rawgeti(L_, -1, 0); // L_: lane ... {uv} nresults | 1274 | lua_rawgeti(L_, -1, 0); // L_: lane ... {uv} nresults |
1213 | _stored = static_cast<int>(lua_tointeger(L_, -1)); | 1275 | auto const _stored{ static_cast<int>(lua_tointeger(L_, -1)) }; |
1214 | lua_pop(L_, 2); | 1276 | lua_pop(L_, 2); |
1215 | STACK_CHECK(L_, 0); | 1277 | STACK_CHECK(L_, 0); |
1216 | return _stored; | 1278 | return _stored; |
1217 | } | 1279 | } |
1218 | 1280 | ||
1281 | int _stored{}; | ||
1219 | switch (status.load(std::memory_order_acquire)) { | 1282 | switch (status.load(std::memory_order_acquire)) { |
1220 | default: | 1283 | default: |
1221 | // this is an internal error, we probably never get here | 1284 | // this is an internal error, we probably never get here |
1222 | lua_settop(L_, 0); // L_: | 1285 | lua_settop(L_, 0); // L_: |
1223 | luaG_pushstring(L_, "Unexpected status: "); // L_: "Unexpected status: " | 1286 | luaW_pushstring(L_, "Unexpected status: "); // L_: "Unexpected status: " |
1224 | pushStatusString(L_); // L_: "Unexpected status: " "<status>" | 1287 | pushStatusString(L_); // L_: "Unexpected status: " "<status>" |
1225 | lua_concat(L_, 2); // L_: "Unexpected status: <status>" | 1288 | lua_concat(L_, 2); // L_: "Unexpected status: <status>" |
1226 | raise_lua_error(L_); | 1289 | raise_lua_error(L_); |
@@ -1295,12 +1358,13 @@ int Lane::storeResults(lua_State* const L_) | |||
1295 | //--- | 1358 | //--- |
1296 | // str= thread_status( lane ) | 1359 | // str= thread_status( lane ) |
1297 | // | 1360 | // |
1298 | // "pending" -> | ("running" <-> "waiting") <-> "suspended" <-> "resuming" | -> "done"/"error"/"cancelled" | 1361 | // "pending" -> | ("running" <-> "waiting") <-> "suspended" <-> "resuming/closing" | -> "done"/"error"/"cancelled" |
1299 | 1362 | ||
1300 | // "pending" not started yet | 1363 | // "pending" not started yet |
1301 | // "running" started, doing its work.. | 1364 | // "running" started, doing its work.. |
1302 | // "suspended" returned from a lua_resume | 1365 | // "suspended" returned from a lua_resume |
1303 | // "resuming" told by its parent state to resume | 1366 | // "resuming" told by its parent state to resume |
1367 | // "closing" not observable from the outside: happens only inside a join()/indexation call to unblock a suspended coroutine Lane so that it can join properly | ||
1304 | // "waiting" blocked in a send()/receive() | 1368 | // "waiting" blocked in a send()/receive() |
1305 | // "done" finished, results are there | 1369 | // "done" finished, results are there |
1306 | // "error" finished at an error, error value is there | 1370 | // "error" finished at an error, error value is there |
@@ -1311,7 +1375,7 @@ std::string_view Lane::threadStatusString() const | |||
1311 | { | 1375 | { |
1312 | static constexpr std::string_view kStrs[] = { | 1376 | static constexpr std::string_view kStrs[] = { |
1313 | "pending", | 1377 | "pending", |
1314 | "running", "suspended", "resuming", | 1378 | "running", "suspended", "resuming", "closing", |
1315 | "waiting", | 1379 | "waiting", |
1316 | "done", "error", "cancelled" | 1380 | "done", "error", "cancelled" |
1317 | }; | 1381 | }; |
@@ -1319,12 +1383,13 @@ std::string_view Lane::threadStatusString() const | |||
1319 | static_assert(1 == static_cast<std::underlying_type_t<Lane::Status>>(Running)); | 1383 | static_assert(1 == static_cast<std::underlying_type_t<Lane::Status>>(Running)); |
1320 | static_assert(2 == static_cast<std::underlying_type_t<Lane::Status>>(Suspended)); | 1384 | static_assert(2 == static_cast<std::underlying_type_t<Lane::Status>>(Suspended)); |
1321 | static_assert(3 == static_cast<std::underlying_type_t<Lane::Status>>(Resuming)); | 1385 | static_assert(3 == static_cast<std::underlying_type_t<Lane::Status>>(Resuming)); |
1322 | static_assert(4 == static_cast<std::underlying_type_t<Lane::Status>>(Waiting)); | 1386 | static_assert(4 == static_cast<std::underlying_type_t<Lane::Status>>(Closing)); |
1323 | static_assert(5 == static_cast<std::underlying_type_t<Lane::Status>>(Done)); | 1387 | static_assert(5 == static_cast<std::underlying_type_t<Lane::Status>>(Waiting)); |
1324 | static_assert(6 == static_cast<std::underlying_type_t<Lane::Status>>(Error)); | 1388 | static_assert(6 == static_cast<std::underlying_type_t<Lane::Status>>(Done)); |
1325 | static_assert(7 == static_cast<std::underlying_type_t<Lane::Status>>(Cancelled)); | 1389 | static_assert(7 == static_cast<std::underlying_type_t<Lane::Status>>(Error)); |
1390 | static_assert(8 == static_cast<std::underlying_type_t<Lane::Status>>(Cancelled)); | ||
1326 | auto const _status{ static_cast<std::underlying_type_t<Lane::Status>>(status.load(std::memory_order_acquire)) }; | 1391 | auto const _status{ static_cast<std::underlying_type_t<Lane::Status>>(status.load(std::memory_order_acquire)) }; |
1327 | if (_status < 0 || _status > 7) { // should never happen, but better safe than sorry | 1392 | if (_status < 0 || _status > 8) { // should never happen, but better safe than sorry |
1328 | return ""; | 1393 | return ""; |
1329 | } | 1394 | } |
1330 | return kStrs[_status]; | 1395 | return kStrs[_status]; |
@@ -1332,17 +1397,52 @@ std::string_view Lane::threadStatusString() const | |||
1332 | 1397 | ||
1333 | // ################################################################################################# | 1398 | // ################################################################################################# |
1334 | 1399 | ||
1335 | bool Lane::waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_) | 1400 | bool Lane::waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_, bool const _acceptSuspended) |
1336 | { | 1401 | { |
1337 | std::unique_lock _guard{ doneMutex }; | 1402 | std::unique_lock _guard{ doneMutex }; |
1338 | // std::stop_token token{ thread.get_stop_token() }; | 1403 | // std::stop_token token{ thread.get_stop_token() }; |
1339 | // return doneCondVar.wait_until(lock, token, secs_, [this](){ return status >= Lane::Done; }); | 1404 | // return doneCondVar.wait_until(lock, token, secs_, [this](){ return status >= Lane::Done; }); |
1340 | 1405 | ||
1341 | // wait until the lane stops working with its state (either Suspended or Done+) | 1406 | // wait until the lane exits lane_main (which is the only place where status can become one of the 3 tested values) |
1342 | return doneCondVar.wait_until(_guard, until_, [this]() | 1407 | return doneCondVar.wait_until(_guard, until_, [this, suspended = _acceptSuspended ? Lane::Suspended : Lane::Done]() { |
1408 | auto const _status{ status.load(std::memory_order_acquire) }; | ||
1409 | return _status == Lane::Done || _status == Lane::Error || _status == Lane::Cancelled || _status == suspended; | ||
1410 | }); | ||
1411 | } | ||
1412 | |||
1413 | // ################################################################################################# | ||
1414 | |||
1415 | [[nodiscard]] | ||
1416 | bool Lane::waitForJoin(lua_State* const L_, std::chrono::time_point<std::chrono::steady_clock> until_) | ||
1417 | { | ||
1418 | // wait until suspended or done | ||
1419 | { | ||
1420 | bool const _done{ !thread.joinable() || waitForCompletion(until_, true) }; | ||
1421 | |||
1422 | if (!_done) { | ||
1423 | lua_pushnil(L_); // L_: lane nil | ||
1424 | luaW_pushstring(L_, "timeout"); // L_: lane nil "timeout" | ||
1425 | return false; | ||
1426 | } | ||
1427 | } | ||
1428 | |||
1429 | // if lane is suspended, force the yield loop to break, and the termination of the thread | ||
1430 | if (status.load(std::memory_order_acquire) == Lane::Suspended) { | ||
1431 | LUA_ASSERT(L_, waiting_on == &doneCondVar); | ||
1432 | status.store(Lane::Closing, std::memory_order_release); | ||
1433 | doneCondVar.notify_all(); | ||
1434 | // wait until done | ||
1343 | { | 1435 | { |
1344 | auto const _status{ status.load(std::memory_order_acquire) }; | 1436 | bool const _done{ !thread.joinable() || waitForCompletion(until_, true) }; |
1345 | return _status == Lane::Suspended || _status >= Lane::Done; | 1437 | |
1438 | if (!_done) { | ||
1439 | lua_pushnil(L_); // L_: lane nil | ||
1440 | luaW_pushstring(L_, "timeout"); // L_: lane nil "timeout" | ||
1441 | return false; | ||
1442 | } | ||
1346 | } | 1443 | } |
1347 | ); | 1444 | LUA_ASSERT(L_, status.load(std::memory_order_acquire) != Lane::Closing); |
1445 | } | ||
1446 | LUA_ASSERT(L_, status.load(std::memory_order_acquire) != Lane::Suspended); | ||
1447 | return true; | ||
1348 | } | 1448 | } |