aboutsummaryrefslogtreecommitdiff
path: root/src/lane.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lane.cpp')
-rw-r--r--src/lane.cpp430
1 files changed, 268 insertions, 162 deletions
diff --git a/src/lane.cpp b/src/lane.cpp
index 7a5c257..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)
85static LUAG_FUNC(lane_threadname) 85static 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;
703 }
704 } while (!_shouldClose);
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);
745 } 718 }
746 } while (_rc == LuaError::YIELD); 719 STACK_CHECK(_S, 0);
747 if (_rc != LuaError::OK) { // : err... 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.
@@ -756,6 +735,12 @@ static void lane_main(Lane* const lane_)
756 } 735 }
757 } 736 }
758 737
738 if (lane_->flaggedAfterUniverseGC.load(std::memory_order_relaxed)) {
739 // let's try not to crash if the lane didn't terminate gracefully and the Universe met its end
740 // there will be leaks, but what else can we do?
741 return;
742 }
743
759 if (_errorHandlerCount) { 744 if (_errorHandlerCount) {
760 lua_remove(_L, 1); // L: retvals|error 745 lua_remove(_L, 1); // L: retvals|error
761 } 746 }
@@ -763,7 +748,7 @@ static void lane_main(Lane* const lane_)
763 // 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
764 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]
765 750
766 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);
767 // Call finalizers, if the script has set them up. 752 // Call finalizers, if the script has set them up.
768 // 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.
769 // 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
@@ -777,10 +762,12 @@ static void lane_main(Lane* const lane_)
777 if (lane_->selfdestructRemove()) { // check and remove (under lock!) 762 if (lane_->selfdestructRemove()) { // check and remove (under lock!)
778 // 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.
779 lane_->closeState(); 764 lane_->closeState();
780 lane_->U->selfdestructMutex.lock(); 765
781 // 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
782 lane_->U->selfdestructingCount.fetch_sub(1, std::memory_order_release); 767 if (!lane_->flaggedAfterUniverseGC.load(std::memory_order_relaxed)) {
783 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 }
784 771
785 // 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
786 // 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
@@ -801,6 +788,8 @@ static void lane_main(Lane* const lane_)
801// ################################################################################################# 788// #################################################################################################
802 789
803#if LUA_VERSION_NUM >= 504 790#if LUA_VERSION_NUM >= 504
791
792// __close(lane_ud, <err>)
804static LUAG_FUNC(lane_close) 793static LUAG_FUNC(lane_close)
805{ 794{
806 [[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
@@ -808,12 +797,13 @@ static LUAG_FUNC(lane_close)
808 lua_settop(L_, 1); // L_: lane 797 lua_settop(L_, 1); // L_: lane
809 798
810 // 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
811 luaG_pushstring(L_, "close"); // L_: lane "close" 800 luaW_pushstring(L_, "close"); // L_: lane "close"
812 lua_pushcclosure(L_, LG_lane_join, 1); // L_: lane join() 801 lua_pushcclosure(L_, LG_lane_join, 1); // L_: lane join()
813 lua_insert(L_, 1); // L_: join() lane 802 lua_insert(L_, 1); // L_: join() lane
814 lua_call(L_, 1, LUA_MULTRET); // L_: join() results 803 lua_call(L_, 1, LUA_MULTRET); // L_: join() results
815 return lua_gettop(L_); 804 return lua_gettop(L_);
816} 805}
806
817#endif // LUA_VERSION_NUM >= 504 807#endif // LUA_VERSION_NUM >= 504
818 808
819// ################################################################################################# 809// #################################################################################################
@@ -837,9 +827,9 @@ static LUAG_FUNC(lane_gc)
837 // if there a gc callback? 827 // if there a gc callback?
838 lua_getiuservalue(L_, StackIndex{ 1 }, UserValueIndex{ 1 }); // L_: ud uservalue 828 lua_getiuservalue(L_, StackIndex{ 1 }, UserValueIndex{ 1 }); // L_: ud uservalue
839 kLaneGC.pushKey(L_); // L_: ud uservalue __gc 829 kLaneGC.pushKey(L_); // L_: ud uservalue __gc
840 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
841 lua_remove(L_, -2); // L_: ud gc_cb|nil 831 lua_remove(L_, -2); // L_: ud gc_cb|nil
842 luaG_pushstring(L_, _lane->getDebugName()); // L_: ud gc_cb name 832 luaW_pushstring(L_, _lane->getDebugName()); // L_: ud gc_cb name
843 _have_gc_cb = true; 833 _have_gc_cb = true;
844 } else { 834 } else {
845 lua_pop(L_, 2); // L_: ud 835 lua_pop(L_, 2); // L_: ud
@@ -850,7 +840,7 @@ static LUAG_FUNC(lane_gc)
850 // still running: will have to be cleaned up later 840 // still running: will have to be cleaned up later
851 _lane->selfdestructAdd(); 841 _lane->selfdestructAdd();
852 if (_have_gc_cb) { 842 if (_have_gc_cb) {
853 luaG_pushstring(L_, "selfdestruct"); // L_: ud gc_cb name status 843 luaW_pushstring(L_, "selfdestruct"); // L_: ud gc_cb name status
854 lua_call(L_, 2, 0); // L_: ud 844 lua_call(L_, 2, 0); // L_: ud
855 } 845 }
856 return 0; 846 return 0;
@@ -865,7 +855,7 @@ static LUAG_FUNC(lane_gc)
865 855
866 // 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
867 if (_have_gc_cb) { 857 if (_have_gc_cb) {
868 luaG_pushstring(L_, "closed"); // L_: ud gc_cb name status 858 luaW_pushstring(L_, "closed"); // L_: ud gc_cb name status
869 lua_call(L_, 2, 0); // L_: ud 859 lua_call(L_, 2, 0); // L_: ud
870 } 860 }
871 return 0; 861 return 0;
@@ -910,7 +900,7 @@ void Lane::applyDebugName() const
910{ 900{
911 if constexpr (HAVE_DECODA_SUPPORT()) { 901 if constexpr (HAVE_DECODA_SUPPORT()) {
912 // to see VM name in Decoda debugger Virtual Machine window 902 // to see VM name in Decoda debugger Virtual Machine window
913 luaG_pushstring(L, debugName); // L: ... "name" 903 luaW_pushstring(L, debugName); // L: ... "name"
914 lua_setglobal(L, "decoda_name"); // L: ... 904 lua_setglobal(L, "decoda_name"); // L: ...
915 } 905 }
916 // and finally set the OS thread name 906 // and finally set the OS thread name
@@ -935,7 +925,8 @@ CancelResult Lane::cancel(CancelOp const op_, std::chrono::time_point<std::chron
935 925
936 // 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
937 // 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)
938 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) {
939 // say "ok" by default, including when lane is already done 930 // say "ok" by default, including when lane is already done
940 return CancelResult::Cancelled; 931 return CancelResult::Cancelled;
941 } 932 }
@@ -962,14 +953,15 @@ CancelResult Lane::internalCancel(CancelRequest const rq_, std::chrono::time_poi
962 // lane_->thread.get_stop_source().request_stop(); 953 // lane_->thread.get_stop_source().request_stop();
963 } 954 }
964 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
965 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
966 if (std::condition_variable* const _waiting_on{ waiting_on }) { 958 if (std::condition_variable* const _waiting_on{ waiting_on }) {
967 _waiting_on->notify_all(); 959 _waiting_on->notify_all();
968 } 960 }
969 } 961 }
970 } 962 }
971 // 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+)
972 CancelResult const result{ waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout }; 964 CancelResult const result{ waitForCompletion(until_, false) ? CancelResult::Cancelled : CancelResult::Timeout };
973 return result; 965 return result;
974} 966}
975 967
@@ -1021,7 +1013,7 @@ void Lane::PushMetatable(lua_State* const L_)
1021{ 1013{
1022 STACK_CHECK_START_REL(L_, 0); 1014 STACK_CHECK_START_REL(L_, 0);
1023 if (luaL_newmetatable(L_, kLaneMetatableName.data())) { // L_: mt 1015 if (luaL_newmetatable(L_, kLaneMetatableName.data())) { // L_: mt
1024 luaG_registerlibfuncs(L_, local::sLaneFunctions); 1016 luaW_registerlibfuncs(L_, local::sLaneFunctions);
1025 // cache error() and tostring() 1017 // cache error() and tostring()
1026 kCachedError.pushKey(L_); // L_: mt kCachedError 1018 kCachedError.pushKey(L_); // L_: mt kCachedError
1027 lua_getglobal(L_, "error"); // L_: mt kCachedError error() 1019 lua_getglobal(L_, "error"); // L_: mt kCachedError error()
@@ -1030,7 +1022,7 @@ void Lane::PushMetatable(lua_State* const L_)
1030 lua_getglobal(L_, "tostring"); // L_: mt kCachedTostring tostring() 1022 lua_getglobal(L_, "tostring"); // L_: mt kCachedTostring tostring()
1031 lua_rawset(L_, -3); // L_: mt 1023 lua_rawset(L_, -3); // L_: mt
1032 // hide the actual metatable from getmetatable() 1024 // hide the actual metatable from getmetatable()
1033 luaG_pushstring(L_, kLaneMetatableName); // L_: mt "Lane" 1025 luaW_pushstring(L_, kLaneMetatableName); // L_: mt "Lane"
1034 lua_setfield(L_, -2, "__metatable"); // L_: mt 1026 lua_setfield(L_, -2, "__metatable"); // L_: mt
1035 } 1027 }
1036 STACK_CHECK(L_, 1); 1028 STACK_CHECK(L_, 1);
@@ -1043,7 +1035,7 @@ void Lane::pushStatusString(lua_State* const L_) const
1043 std::string_view const _str{ threadStatusString() }; 1035 std::string_view const _str{ threadStatusString() };
1044 LUA_ASSERT(L_, !_str.empty()); 1036 LUA_ASSERT(L_, !_str.empty());
1045 1037
1046 luaG_pushstring(L_, _str); 1038 luaW_pushstring(L_, _str);
1047} 1039}
1048 1040
1049// ################################################################################################# 1041// #################################################################################################
@@ -1102,12 +1094,88 @@ void Lane::pushIndexedResult(lua_State* const L_, int const key_) const
1102// ################################################################################################# 1094// #################################################################################################
1103 1095
1104[[nodiscard]] 1096[[nodiscard]]
1097int 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]]
1105std::string_view Lane::pushErrorTraceLevel(lua_State* L_) const 1173std::string_view Lane::pushErrorTraceLevel(lua_State* L_) const
1106{ 1174{
1107 std::string_view const _str{ errorTraceLevelString() }; 1175 std::string_view const _str{ errorTraceLevelString() };
1108 LUA_ASSERT(L_, !_str.empty()); 1176 LUA_ASSERT(L_, !_str.empty());
1109 1177
1110 return luaG_pushstring(L_, _str); 1178 return luaW_pushstring(L_, _str);
1111} 1179}
1112 1180
1113// ################################################################################################# 1181// #################################################################################################
@@ -1118,7 +1186,7 @@ void Lane::resetResultsStorage(lua_State* const L_, StackIndex const self_idx_)
1118{ 1186{
1119 STACK_GROW(L_, 4); 1187 STACK_GROW(L_, 4);
1120 STACK_CHECK_START_REL(L_, 0); 1188 STACK_CHECK_START_REL(L_, 0);
1121 StackIndex const _self_idx{ luaG_absindex(L_, self_idx_) }; 1189 StackIndex const _self_idx{ luaW_absindex(L_, self_idx_) };
1122 LUA_ASSERT(L_, ToLane(L_, _self_idx) == this); // L_: ... self ... 1190 LUA_ASSERT(L_, ToLane(L_, _self_idx) == this); // L_: ... self ...
1123 // create the new table 1191 // create the new table
1124 lua_newtable(L_); // L_: ... self ... {} 1192 lua_newtable(L_); // L_: ... self ... {}
@@ -1152,7 +1220,7 @@ void Lane::securizeDebugName(lua_State* const L_)
1152 lua_newtable(L_); // L_: lane ... {uv} {} 1220 lua_newtable(L_); // L_: lane ... {uv} {}
1153 { 1221 {
1154 std::lock_guard<std::mutex> _guard{ debugNameMutex }; 1222 std::lock_guard<std::mutex> _guard{ debugNameMutex };
1155 debugName = luaG_pushstring(L_, debugName); // L_: lane ... {uv} {} name 1223 debugName = luaW_pushstring(L_, debugName); // L_: lane ... {uv} {} name
1156 } 1224 }
1157 lua_rawset(L_, -3); // L_: lane ... {uv} 1225 lua_rawset(L_, -3); // L_: lane ... {uv}
1158 lua_pop(L_, 1); // L_: lane 1226 lua_pop(L_, 1); // L_: lane
@@ -1161,11 +1229,11 @@ void Lane::securizeDebugName(lua_State* const L_)
1161 1229
1162// ################################################################################################# 1230// #################################################################################################
1163 1231
1164void Lane::startThread(int const priority_) 1232void Lane::startThread(lua_State* const L_, int const priority_, NativePrioFlag native_)
1165{ 1233{
1166 thread = std::thread([this]() { lane_main(this); }); 1234 thread = std::thread([this]() { lane_main(this); });
1167 if (priority_ != kThreadPrioDefault) { 1235 if (priority_ != kThreadPrioDefault) {
1168 THREAD_SET_PRIORITY(thread, priority_, U->sudo); 1236 THREAD_SET_PRIORITY(L_, thread, priority_, native_, U->sudo);
1169 } 1237 }
1170} 1238}
1171 1239
@@ -1175,13 +1243,13 @@ void Lane::storeDebugName(std::string_view const& name_)
1175{ 1243{
1176 STACK_CHECK_START_REL(L, 0); 1244 STACK_CHECK_START_REL(L, 0);
1177 // 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...
1178 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); });
1179 STACK_CHECK(L, 0); 1247 STACK_CHECK(L, 0);
1180 kLaneNameRegKey.pushValue(L); // L: ... "name" ... 1248 kLaneNameRegKey.pushValue(L); // L: ... "name" ...
1181 // keep a direct view on the stored string 1249 // keep a direct view on the stored string
1182 { 1250 {
1183 std::lock_guard<std::mutex> _guard{ debugNameMutex }; 1251 std::lock_guard<std::mutex> _guard{ debugNameMutex };
1184 debugName = luaG_tostring(L, kIdxTop); 1252 debugName = luaW_tostring(L, kIdxTop);
1185 } 1253 }
1186 lua_pop(L, 1); 1254 lua_pop(L, 1);
1187 STACK_CHECK(L, 0); 1255 STACK_CHECK(L, 0);
@@ -1201,20 +1269,21 @@ int Lane::storeResults(lua_State* const L_)
1201 lua_getiuservalue(L_, kIdxSelf, UserValueIndex{ 1 }); // L_: lane ... {uv} 1269 lua_getiuservalue(L_, kIdxSelf, UserValueIndex{ 1 }); // L_: lane ... {uv}
1202 StackIndex const _tidx{ lua_gettop(L_) }; 1270 StackIndex const _tidx{ lua_gettop(L_) };
1203 1271
1204 int _stored{}; 1272 // if the results were already stored from a previous indexing, just say how many values we have in store
1205 if (nresults == 0) { 1273 if (!L) {
1206 lua_rawgeti(L_, -1, 0); // L_: lane ... {uv} nresults 1274 lua_rawgeti(L_, -1, 0); // L_: lane ... {uv} nresults
1207 _stored = static_cast<int>(lua_tointeger(L_, -1)); 1275 auto const _stored{ static_cast<int>(lua_tointeger(L_, -1)) };
1208 lua_pop(L_, 2); 1276 lua_pop(L_, 2);
1209 STACK_CHECK(L_, 0); 1277 STACK_CHECK(L_, 0);
1210 return _stored; 1278 return _stored;
1211 } 1279 }
1212 1280
1281 int _stored{};
1213 switch (status.load(std::memory_order_acquire)) { 1282 switch (status.load(std::memory_order_acquire)) {
1214 default: 1283 default:
1215 // this is an internal error, we probably never get here 1284 // this is an internal error, we probably never get here
1216 lua_settop(L_, 0); // L_: 1285 lua_settop(L_, 0); // L_:
1217 luaG_pushstring(L_, "Unexpected status: "); // L_: "Unexpected status: " 1286 luaW_pushstring(L_, "Unexpected status: "); // L_: "Unexpected status: "
1218 pushStatusString(L_); // L_: "Unexpected status: " "<status>" 1287 pushStatusString(L_); // L_: "Unexpected status: " "<status>"
1219 lua_concat(L_, 2); // L_: "Unexpected status: <status>" 1288 lua_concat(L_, 2); // L_: "Unexpected status: <status>"
1220 raise_lua_error(L_); 1289 raise_lua_error(L_);
@@ -1289,12 +1358,13 @@ int Lane::storeResults(lua_State* const L_)
1289//--- 1358//---
1290// str= thread_status( lane ) 1359// str= thread_status( lane )
1291// 1360//
1292// "pending" -> | ("running" <-> "waiting") <-> "suspended" <-> "resuming" | -> "done"/"error"/"cancelled" 1361// "pending" -> | ("running" <-> "waiting") <-> "suspended" <-> "resuming/closing" | -> "done"/"error"/"cancelled"
1293 1362
1294// "pending" not started yet 1363// "pending" not started yet
1295// "running" started, doing its work.. 1364// "running" started, doing its work..
1296// "suspended" returned from a lua_resume 1365// "suspended" returned from a lua_resume
1297// "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
1298// "waiting" blocked in a send()/receive() 1368// "waiting" blocked in a send()/receive()
1299// "done" finished, results are there 1369// "done" finished, results are there
1300// "error" finished at an error, error value is there 1370// "error" finished at an error, error value is there
@@ -1305,7 +1375,7 @@ std::string_view Lane::threadStatusString() const
1305{ 1375{
1306 static constexpr std::string_view kStrs[] = { 1376 static constexpr std::string_view kStrs[] = {
1307 "pending", 1377 "pending",
1308 "running", "suspended", "resuming", 1378 "running", "suspended", "resuming", "closing",
1309 "waiting", 1379 "waiting",
1310 "done", "error", "cancelled" 1380 "done", "error", "cancelled"
1311 }; 1381 };
@@ -1313,12 +1383,13 @@ std::string_view Lane::threadStatusString() const
1313 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));
1314 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));
1315 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));
1316 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));
1317 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));
1318 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));
1319 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));
1320 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)) };
1321 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
1322 return ""; 1393 return "";
1323 } 1394 }
1324 return kStrs[_status]; 1395 return kStrs[_status];
@@ -1326,17 +1397,52 @@ std::string_view Lane::threadStatusString() const
1326 1397
1327// ################################################################################################# 1398// #################################################################################################
1328 1399
1329bool Lane::waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_) 1400bool Lane::waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_, bool const _acceptSuspended)
1330{ 1401{
1331 std::unique_lock _guard{ doneMutex }; 1402 std::unique_lock _guard{ doneMutex };
1332 // std::stop_token token{ thread.get_stop_token() }; 1403 // std::stop_token token{ thread.get_stop_token() };
1333 // 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; });
1334 1405
1335 // 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)
1336 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]]
1416bool 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
1337 { 1435 {
1338 auto const _status{ status.load(std::memory_order_acquire) }; 1436 bool const _done{ !thread.joinable() || waitForCompletion(until_, true) };
1339 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 }
1340 } 1443 }
1341 ); 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;
1342} 1448}