aboutsummaryrefslogtreecommitdiff
path: root/src/lane.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lane.cpp')
-rw-r--r--src/lane.cpp424
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)
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;
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>)
810static LUAG_FUNC(lane_close) 793static 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]]
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]]
1111std::string_view Lane::pushErrorTraceLevel(lua_State* L_) const 1173std::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
1170void Lane::startThread(int const priority_) 1232void 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
1335bool 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)
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]]
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
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}