aboutsummaryrefslogtreecommitdiff
path: root/src/lanes.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lanes.cpp')
-rw-r--r--src/lanes.cpp894
1 files changed, 2 insertions, 892 deletions
diff --git a/src/lanes.cpp b/src/lanes.cpp
index f8efa42..a775895 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -84,11 +84,10 @@ THE SOFTWARE.
84#include "deep.h" 84#include "deep.h"
85#include "intercopycontext.h" 85#include "intercopycontext.h"
86#include "keeper.h" 86#include "keeper.h"
87#include "lanes_private.h" 87#include "lane.h"
88#include "state.h" 88#include "state.h"
89#include "threading.h" 89#include "threading.h"
90#include "tools.h" 90#include "tools.h"
91#include "universe.h"
92 91
93#if !(defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC)) 92#if !(defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC))
94#include <sys/time.h> 93#include <sys/time.h>
@@ -103,279 +102,9 @@ THE SOFTWARE.
103#include <atomic> 102#include <atomic>
104 103
105// ################################################################################################# 104// #################################################################################################
106
107Lane::Lane(Universe* U_, lua_State* L_)
108: U{ U_ }
109, L{ L_ }
110{
111#if HAVE_LANE_TRACKING()
112 U->tracker.tracking_add(this);
113#endif // HAVE_LANE_TRACKING()
114}
115
116// #################################################################################################
117
118bool Lane::waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_)
119{
120 std::unique_lock _guard{ doneMutex };
121 // std::stop_token token{ thread.get_stop_token() };
122 // return doneCondVar.wait_until(lock, token, secs_, [this](){ return status >= Lane::Done; });
123 return doneCondVar.wait_until(_guard, until_, [this]() { return status >= Lane::Done; });
124}
125
126// #################################################################################################
127
128static void lane_main(Lane* lane_);
129void Lane::startThread(int priority_)
130{
131 thread = std::jthread([this]() { lane_main(this); });
132 if (priority_ != kThreadPrioDefault) {
133 JTHREAD_SET_PRIORITY(thread, priority_, U->sudo);
134 }
135}
136
137// #################################################################################################
138
139/* Do you want full call stacks, or just the line where the error happened?
140 *
141 * TBD: The full stack feature does not seem to work (try 'make error').
142 */
143#define ERROR_FULL_STACK 1 // must be either 0 or 1 as we do some index arithmetics with it!
144
145// intern the debug name in the caller lua state so that the pointer remains valid after the lane's state is closed
146void Lane::securizeDebugName(lua_State* L_)
147{
148 STACK_CHECK_START_REL(L_, 0);
149 STACK_GROW(L_, 3);
150 // a Lane's uservalue should be a table
151 lua_getiuservalue(L_, 1, 1); // L_: lane ... {uv}
152 LUA_ASSERT(L_, lua_istable(L_, -1));
153 // we don't care about the actual key, so long as it's unique and can't collide with anything.
154 lua_newtable(L_); // L_: lane ... {uv} {}
155 // Lua 5.1 can't do 'lane_->debugName = lua_pushstring(L_, lane_->debugName);'
156 lua_pushstring(L_, debugName); // L_: lane ... {uv} {} name
157 debugName = lua_tostring(L_, -1);
158 lua_rawset(L_, -3); // L_: lane ... {uv}
159 lua_pop(L_, 1); // L_: lane
160 STACK_CHECK(L_, 0);
161}
162
163// #################################################################################################
164
165#if ERROR_FULL_STACK
166[[nodiscard]] static int lane_error(lua_State* L_);
167// xxh64 of string "kStackTraceRegKey" generated at https://www.pelock.com/products/hash-calculator
168static constexpr RegistryUniqueKey kStackTraceRegKey{ 0x3F327747CACAA904ull };
169#endif // ERROR_FULL_STACK
170
171/*
172 * registry[FINALIZER_REG_KEY] is either nil (no finalizers) or a table
173 * of functions that Lanes will call after the executing 'pcall' has ended.
174 *
175 * We're NOT using the GC system for finalizer mainly because providing the
176 * error (and maybe stack trace) parameters to the finalizer functions would
177 * anyways complicate that approach.
178 */
179// xxh64 of string "kFinalizerRegKey" generated at https://www.pelock.com/products/hash-calculator
180static constexpr RegistryUniqueKey kFinalizerRegKey{ 0xFE936BFAA718FEEAull };
181
182// #################################################################################################
183
184Lane::~Lane()
185{
186 // Clean up after a (finished) thread
187 //
188#if HAVE_LANE_TRACKING()
189 std::ignore = U->tracker.tracking_remove(this);
190#endif // HAVE_LANE_TRACKING()
191}
192
193// #################################################################################################
194// ########################################## Finalizer ############################################
195// #################################################################################################
196
197// void= finalizer( finalizer_func )
198//
199// finalizer_func( [err, stack_tbl] )
200//
201// Add a function that will be called when exiting the lane, either via
202// normal return or an error.
203//
204LUAG_FUNC(set_finalizer)
205{
206 luaL_argcheck(L_, lua_isfunction(L_, 1), 1, "finalizer should be a function");
207 luaL_argcheck(L_, lua_gettop(L_) == 1, 1, "too many arguments");
208 STACK_GROW(L_, 3);
209 // Get the current finalizer table (if any), create one if it doesn't exist
210 std::ignore = kFinalizerRegKey.getSubTable(L_, 1, 0); // L_: finalizer {finalisers}
211 // must cast to int, not lua_Integer, because LuaJIT signature of lua_rawseti is not the same as PUC-Lua.
212 int const _idx{ static_cast<int>(lua_rawlen(L_, -1) + 1) };
213 lua_pushvalue(L_, 1); // L_: finalizer {finalisers} finalizer
214 lua_rawseti(L_, -2, _idx); // L_: finalizer {finalisers}
215 // no need to adjust the stack, Lua does this for us
216 return 0;
217}
218
219// #################################################################################################
220
221static void push_stack_trace(lua_State* L_, int rc_, int stk_base_)
222{
223 // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry
224 switch (rc_) {
225 case LUA_OK: // no error, body return values are on the stack
226 break;
227
228 case LUA_ERRRUN: // cancellation or a runtime error
229#if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler
230 {
231 STACK_CHECK_START_REL(L_, 0);
232 // fetch the call stack table from the registry where the handler stored it
233 STACK_GROW(L_, 1);
234 // yields nil if no stack was generated (in case of cancellation for example)
235 kStackTraceRegKey.pushValue(L_); // L_: err trace|nil
236 STACK_CHECK(L_, 1);
237
238 // For cancellation the error message is kCancelError, and a stack trace isn't placed
239 // For other errors, the message can be whatever was thrown, and we should have a stack trace table
240 LUA_ASSERT(L_, lua_type(L_, 1 + stk_base_) == (kCancelError.equals(L_, stk_base_) ? LUA_TNIL : LUA_TTABLE));
241 // Just leaving the stack trace table on the stack is enough to get it through to the master.
242 break;
243 }
244#else // !ERROR_FULL_STACK
245 [[fallthrough]]; // fall through if not ERROR_FULL_STACK
246#endif // !ERROR_FULL_STACK
247
248 case LUA_ERRMEM: // memory allocation error (handler not called)
249 case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition)
250 default:
251 // we should have a single value which is either a string (the error message) or kCancelError
252 LUA_ASSERT(L_, (lua_gettop(L_) == stk_base_) && ((lua_type(L_, stk_base_) == LUA_TSTRING) || kCancelError.equals(L_, stk_base_)));
253 break;
254 }
255}
256
257// #################################################################################################
258//---
259// Run finalizers - if any - with the given parameters
260//
261// If 'rc' is nonzero, error message and stack index (the latter only when ERROR_FULL_STACK == 1) are available as:
262// [-1]: stack trace (table)
263// [-2]: error message (any type)
264//
265// Returns:
266// 0 if finalizers were run without error (or there were none)
267// LUA_ERRxxx return code if any of the finalizers failed
268//
269// TBD: should we add stack trace on failing finalizer, wouldn't be hard..
270//
271
272[[nodiscard]] static int run_finalizers(lua_State* L_, int lua_rc_)
273{
274 kFinalizerRegKey.pushValue(L_); // L_: ... finalizers?
275 if (lua_isnil(L_, -1)) {
276 lua_pop(L_, 1);
277 return 0; // no finalizers
278 }
279
280 STACK_GROW(L_, 5);
281
282 int const _finalizers_index{ lua_gettop(L_) };
283 int const _err_handler_index{ ERROR_FULL_STACK ? (lua_pushcfunction(L_, lane_error), lua_gettop(L_)) : 0 };
284
285 int rc{ LUA_OK };
286 for (int n = static_cast<int>(lua_rawlen(L_, _finalizers_index)); n > 0; --n) {
287 int args = 0;
288 lua_pushinteger(L_, n); // L_: ... finalizers lane_error n
289 lua_rawget(L_, _finalizers_index); // L_: ... finalizers lane_error finalizer
290 LUA_ASSERT(L_, lua_isfunction(L_, -1));
291 if (lua_rc_ != LUA_OK) { // we have an error message and an optional stack trace at the bottom of the stack
292 LUA_ASSERT(L_, _finalizers_index == 2 || _finalizers_index == 3);
293 // char const* err_msg = lua_tostring(L_, 1);
294 lua_pushvalue(L_, 1); // L_: ... finalizers lane_error finalizer err_msg
295 // note we don't always have a stack trace for example when kCancelError, or when we got an error that doesn't call our handler, such as LUA_ERRMEM
296 if (_finalizers_index == 3) {
297 lua_pushvalue(L_, 2); // L_: ... finalizers lane_error finalizer err_msg stack_trace
298 }
299 args = _finalizers_index - 1;
300 }
301
302 // if no error from the main body, finalizer doesn't receive any argument, else it gets the error message and optional stack trace
303 rc = lua_pcall(L_, args, 0, _err_handler_index); // L_: ... finalizers lane_error err_msg2?
304 if (rc != LUA_OK) {
305 push_stack_trace(L_, rc, lua_gettop(L_)); // L_: ... finalizers lane_error err_msg2? trace
306 // If one finalizer fails, don't run the others. Return this
307 // as the 'real' error, replacing what we could have had (or not)
308 // from the actual code.
309 break;
310 }
311 // no error, proceed to next finalizer // L_: ... finalizers lane_error
312 }
313
314 if (rc != LUA_OK) {
315 // ERROR_FULL_STACK accounts for the presence of lane_error on the stack
316 int const nb_err_slots{ lua_gettop(L_) - _finalizers_index - ERROR_FULL_STACK };
317 // a finalizer generated an error, this is what we leave of the stack
318 for (int n = nb_err_slots; n > 0; --n) {
319 lua_replace(L_, n);
320 }
321 // leave on the stack only the error and optional stack trace produced by the error in the finalizer
322 lua_settop(L_, nb_err_slots); // L_: ... lane_error trace
323 } else { // no error from the finalizers, make sure only the original return values from the lane body remain on the stack
324 lua_settop(L_, _finalizers_index - 1);
325 }
326
327 return rc;
328}
329
330// #################################################################################################
331// ########################################### Threads ############################################# 105// ########################################### Threads #############################################
332// ################################################################################################# 106// #################################################################################################
333 107
334/*
335 * Add the lane to selfdestruct chain; the ones still running at the end of the
336 * whole process will be cancelled.
337 */
338static void selfdestruct_add(Lane* lane_)
339{
340 std::lock_guard<std::mutex> _guard{ lane_->U->selfdestructMutex };
341 assert(lane_->selfdestruct_next == nullptr);
342
343 lane_->selfdestruct_next = lane_->U->selfdestructFirst;
344 lane_->U->selfdestructFirst = lane_;
345}
346
347// #################################################################################################
348
349// A free-running lane has ended; remove it from selfdestruct chain
350[[nodiscard]] static bool selfdestruct_remove(Lane* lane_)
351{
352 bool _found{ false };
353 std::lock_guard<std::mutex> _guard{ lane_->U->selfdestructMutex };
354 // Make sure (within the MUTEX) that we actually are in the chain
355 // still (at process exit they will remove us from chain and then
356 // cancel/kill).
357 //
358 if (lane_->selfdestruct_next != nullptr) {
359 Lane* volatile* _ref = static_cast<Lane* volatile*>(&lane_->U->selfdestructFirst);
360
361 while (*_ref != SELFDESTRUCT_END) {
362 if (*_ref == lane_) {
363 *_ref = lane_->selfdestruct_next;
364 lane_->selfdestruct_next = nullptr;
365 // the terminal shutdown should wait until the lane is done with its lua_close()
366 lane_->U->selfdestructingCount.fetch_add(1, std::memory_order_release);
367 _found = true;
368 break;
369 }
370 _ref = static_cast<Lane* volatile*>(&((*_ref)->selfdestruct_next));
371 }
372 assert(_found);
373 }
374 return _found;
375}
376
377// #################################################################################################
378
379//--- 108//---
380// = _single( [cores_uint=1] ) 109// = _single( [cores_uint=1] )
381// 110//
@@ -404,159 +133,6 @@ LUAG_FUNC(set_singlethreaded)
404 133
405// ################################################################################################# 134// #################################################################################################
406 135
407/*
408 * str= lane_error( error_val|str )
409 *
410 * Called if there's an error in some lane; add call stack to error message
411 * just like 'lua.c' normally does.
412 *
413 * ".. will be called with the error message and its return value will be the
414 * message returned on the stack by lua_pcall."
415 *
416 * Note: Rather than modifying the error message itself, it would be better
417 * to provide the call stack (as string) completely separated. This would
418 * work great with non-string error values as well (current system does not).
419 * (This is NOT possible with the Lua 5.1 'lua_pcall()'; we could of course
420 * implement a Lanes-specific 'pcall' of our own that does this). TBD!!! :)
421 * --AKa 22-Jan-2009
422 */
423#if ERROR_FULL_STACK
424
425// xxh64 of string "kExtendedStackTraceRegKey" generated at https://www.pelock.com/products/hash-calculator
426static constexpr RegistryUniqueKey kExtendedStackTraceRegKey{ 0x38147AD48FB426E2ull }; // used as registry key
427
428LUAG_FUNC(set_error_reporting)
429{
430 luaL_checktype(L_, 1, LUA_TSTRING);
431 char const* _mode{ lua_tostring(L_, 1) };
432 lua_pushliteral(L_, "extended");
433 bool const _extended{ strcmp(_mode, "extended") == 0 };
434 bool const _basic{ strcmp(_mode, "basic") == 0 };
435 if (!_extended && !_basic) {
436 raise_luaL_error(L_, "unsupported error reporting model %s", _mode);
437 }
438
439 kExtendedStackTraceRegKey.setValue(L_, [extended = _extended](lua_State* L_) { lua_pushboolean(L_, extended ? 1 : 0); });
440 return 0;
441}
442
443// #################################################################################################
444
445[[nodiscard]] static int lane_error(lua_State* L_)
446{
447 // error message (any type)
448 STACK_CHECK_START_ABS(L_, 1); // L_: some_error
449
450 // Don't do stack survey for cancelled lanes.
451 //
452 if (kCancelError.equals(L_, 1)) {
453 return 1; // just pass on
454 }
455
456 STACK_GROW(L_, 3);
457 bool const _extended{ kExtendedStackTraceRegKey.readBoolValue(L_) };
458 STACK_CHECK(L_, 1);
459
460 // Place stack trace at 'registry[kStackTraceRegKey]' for the 'lua_pcall()'
461 // caller to fetch. This bypasses the Lua 5.1 limitation of only one
462 // return value from error handler to 'lua_pcall()' caller.
463
464 // It's adequate to push stack trace as a table. This gives the receiver
465 // of the stack best means to format it to their liking. Also, it allows
466 // us to add more stack info later, if needed.
467 //
468 // table of { "sourcefile.lua:<line>", ... }
469 //
470 lua_newtable(L_); // L_: some_error {}
471
472 // Best to start from level 1, but in some cases it might be a C function
473 // and we don't get '.currentline' for that. It's okay - just keep level
474 // and table index growing separate. --AKa 22-Jan-2009
475 //
476 lua_Debug _ar;
477 for (int _n = 1; lua_getstack(L_, _n, &_ar); ++_n) {
478 lua_getinfo(L_, _extended ? "Sln" : "Sl", &_ar);
479 if (_extended) {
480 lua_newtable(L_); // L_: some_error {} {}
481
482 lua_pushstring(L_, _ar.source); // L_: some_error {} {} source
483 lua_setfield(L_, -2, "source"); // L_: some_error {} {}
484
485 lua_pushinteger(L_, _ar.currentline); // L_: some_error {} {} currentline
486 lua_setfield(L_, -2, "currentline"); // L_: some_error {} {}
487
488 lua_pushstring(L_, _ar.name); // L_: some_error {} {} name
489 lua_setfield(L_, -2, "name"); // L_: some_error {} {}
490
491 lua_pushstring(L_, _ar.namewhat); // L_: some_error {} {} namewhat
492 lua_setfield(L_, -2, "namewhat"); // L_: some_error {} {}
493
494 lua_pushstring(L_, _ar.what); // L_: some_error {} {} what
495 lua_setfield(L_, -2, "what"); // L_: some_error {} {}
496 } else if (_ar.currentline > 0) {
497 lua_pushfstring(L_, "%s:%d", _ar.short_src, _ar.currentline); // L_: some_error {} "blah:blah"
498 } else {
499 lua_pushfstring(L_, "%s:?", _ar.short_src); // L_: some_error {} "blah"
500 }
501 lua_rawseti(L_, -2, static_cast<lua_Integer>(_n)); // L_: some_error {}
502 }
503
504 // store the stack trace table in the registry
505 kStackTraceRegKey.setValue(L_, [](lua_State* L_) { lua_insert(L_, -2); }); // L_: some_error
506
507 STACK_CHECK(L_, 1);
508 return 1; // the untouched error value
509}
510#endif // ERROR_FULL_STACK
511
512// #################################################################################################
513
514void Lane::changeDebugName(int nameIdx_)
515{
516 // xxh64 of string "debugName" generated at https://www.pelock.com/products/hash-calculator
517 static constexpr RegistryUniqueKey kRegKey{ 0xA194E2645C57F6DDull };
518 nameIdx_ = lua_absindex(L, nameIdx_);
519 luaL_checktype(L, nameIdx_, LUA_TSTRING); // L: ... "name" ...
520 STACK_CHECK_START_REL(L, 0);
521 // 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...
522 kRegKey.setValue(L, [nameIdx = nameIdx_](lua_State* L_) { lua_pushvalue(L_, nameIdx); }); // L: ... "name" ...
523 // keep a direct pointer on the string
524 debugName = lua_tostring(L, nameIdx_);
525 // to see VM name in Decoda debugger Virtual Machine window
526 lua_pushvalue(L, nameIdx_); // L: ... "name" ... "name"
527 lua_setglobal(L, "decoda_name"); // L: ... "name" ...
528 // and finally set the OS thread name
529 THREAD_SETNAME(debugName);
530 STACK_CHECK(L, 0);
531}
532
533// #################################################################################################
534
535// upvalue #1 is the lane userdata
536LUAG_FUNC(set_debug_threadname)
537{
538 // C s_lane structure is a light userdata upvalue
539 Lane* const _lane{ lua_tolightuserdata<Lane>(L_, lua_upvalueindex(1)) };
540 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
541 lua_settop(L_, 1);
542 STACK_CHECK_START_REL(L_, 0);
543 _lane->changeDebugName(-1);
544 STACK_CHECK(L_, 0);
545 return 0;
546}
547
548// #################################################################################################
549
550LUAG_FUNC(get_debug_threadname)
551{
552 Lane* const _lane{ ToLane(L_, 1) };
553 luaL_argcheck(L_, lua_gettop(L_) == 1, 2, "too many arguments");
554 lua_pushstring(L_, _lane->debugName);
555 return 1;
556}
557
558// #################################################################################################
559
560LUAG_FUNC(set_thread_priority) 136LUAG_FUNC(set_thread_priority)
561{ 137{
562 lua_Integer const _prio{ luaL_checkinteger(L_, 1) }; 138 lua_Integer const _prio{ luaL_checkinteger(L_, 1) };
@@ -584,127 +160,6 @@ LUAG_FUNC(set_thread_affinity)
584 160
585// ################################################################################################# 161// #################################################################################################
586 162
587#if USE_DEBUG_SPEW()
588// can't use direct LUA_x errcode indexing because the sequence is not the same between Lua 5.1 and 5.2 :-(
589// LUA_ERRERR doesn't have the same value
590struct errcode_name
591{
592 int code;
593 char const* name;
594};
595
596static struct errcode_name s_errcodes[] = {
597 { LUA_OK, "LUA_OK" },
598 { LUA_YIELD, "LUA_YIELD" },
599 { LUA_ERRRUN, "LUA_ERRRUN" },
600 { LUA_ERRSYNTAX, "LUA_ERRSYNTAX" },
601 { LUA_ERRMEM, "LUA_ERRMEM" },
602 { LUA_ERRGCMM, "LUA_ERRGCMM" },
603 { LUA_ERRERR, "LUA_ERRERR" },
604};
605static char const* get_errcode_name(int _code)
606{
607 for (errcode_name const& _entry : s_errcodes) {
608 if (_entry.code == _code) {
609 return _entry.name;
610 }
611 }
612 return "<nullptr>";
613}
614#endif // USE_DEBUG_SPEW()
615
616// #################################################################################################
617
618static void lane_main(Lane* lane_)
619{
620 lua_State* const _L{ lane_->L };
621 // wait until the launching thread has finished preparing L
622 lane_->ready.wait();
623 int _rc{ LUA_ERRRUN };
624 if (lane_->status == Lane::Pending) { // nothing wrong happened during preparation, we can work
625 // At this point, the lane function and arguments are on the stack
626 int const nargs{ lua_gettop(_L) - 1 };
627 DEBUGSPEW_CODE(Universe* U = universe_get(_L));
628 lane_->status = Lane::Running; // Pending -> Running
629
630 // Tie "set_finalizer()" to the state
631 lua_pushcfunction(_L, LG_set_finalizer);
632 populate_func_lookup_table(_L, -1, "set_finalizer");
633 lua_setglobal(_L, "set_finalizer");
634
635 // Tie "set_debug_threadname()" to the state
636 // But don't register it in the lookup database because of the Lane pointer upvalue
637 lua_pushlightuserdata(_L, lane_);
638 lua_pushcclosure(_L, LG_set_debug_threadname, 1);
639 lua_setglobal(_L, "set_debug_threadname");
640
641 // Tie "cancel_test()" to the state
642 lua_pushcfunction(_L, LG_cancel_test);
643 populate_func_lookup_table(_L, -1, "cancel_test");
644 lua_setglobal(_L, "cancel_test");
645
646 // this could be done in lane_new before the lane body function is pushed on the stack to avoid unnecessary stack slot shifting around
647#if ERROR_FULL_STACK
648 // Tie "set_error_reporting()" to the state
649 lua_pushcfunction(_L, LG_set_error_reporting);
650 populate_func_lookup_table(_L, -1, "set_error_reporting");
651 lua_setglobal(_L, "set_error_reporting");
652
653 STACK_GROW(_L, 1);
654 lua_pushcfunction(_L, lane_error); // L: func args handler
655 lua_insert(_L, 1); // L: handler func args
656#endif // L: ERROR_FULL_STACK
657
658 _rc = lua_pcall(_L, nargs, LUA_MULTRET, ERROR_FULL_STACK); // L: retvals|err
659
660#if ERROR_FULL_STACK
661 lua_remove(_L, 1); // L: retvals|error
662#endif // ERROR_FULL_STACK
663
664 // in case of error and if it exists, fetch stack trace from registry and push it
665 push_stack_trace(_L, _rc, 1); // L: retvals|error [trace]
666
667 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END(U), _L, get_errcode_name(_rc), kCancelError.equals(_L, 1) ? "cancelled" : lua_typename(_L, lua_type(_L, 1))));
668 // Call finalizers, if the script has set them up.
669 //
670 int _rc2{ run_finalizers(_L, _rc) };
671 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END(U), _L, get_errcode_name(_rc2)));
672 if (_rc2 != LUA_OK) { // Error within a finalizer!
673 // the finalizer generated an error, and left its own error message [and stack trace] on the stack
674 _rc = _rc2; // we're overruling the earlier script error or normal return
675 }
676 lane_->waiting_on = nullptr; // just in case
677 if (selfdestruct_remove(lane_)) { // check and remove (under lock!)
678 // We're a free-running thread and no-one's there to clean us up.
679 lua_close(lane_->L);
680 lane_->L = nullptr; // just in case
681 lane_->U->selfdestructMutex.lock();
682 // done with lua_close(), terminal shutdown sequence may proceed
683 lane_->U->selfdestructingCount.fetch_sub(1, std::memory_order_release);
684 lane_->U->selfdestructMutex.unlock();
685
686 // we destroy our jthread member from inside the thread body, so we have to detach so that we don't try to join, as this doesn't seem a good idea
687 lane_->thread.detach();
688 delete lane_;
689 lane_ = nullptr;
690 }
691 }
692 if (lane_) {
693 // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them
694
695 Lane::Status const _st = (_rc == LUA_OK) ? Lane::Done : kCancelError.equals(_L, 1) ? Lane::Cancelled : Lane::Error;
696
697 {
698 // 'doneMutex' protects the -> Done|Error|Cancelled state change
699 std::lock_guard lock{ lane_->doneMutex };
700 lane_->status = _st;
701 lane_->doneCondVar.notify_one(); // wake up master (while 'lane_->doneMutex' is on)
702 }
703 }
704}
705
706// #################################################################################################
707
708// --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required 163// --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required
709// with lanes.require, that will call the regular 'require', then populate the lookup database in the source lane 164// with lanes.require, that will call the regular 'require', then populate the lookup database in the source lane
710// module = lanes.require( "modname") 165// module = lanes.require( "modname")
@@ -750,9 +205,6 @@ LUAG_FUNC(register)
750 205
751// ################################################################################################# 206// #################################################################################################
752 207
753// xxh64 of string "kLaneGC" generated at https://www.pelock.com/products/hash-calculator
754static constexpr UniqueKey kLaneGC{ 0x5D6122141727F960ull };
755
756//--- 208//---
757// lane_ud = lane_new( function 209// lane_ud = lane_new( function
758// , [libs_str] 210// , [libs_str]
@@ -1039,328 +491,6 @@ LUAG_FUNC(lane_new)
1039 return 1; 491 return 1;
1040} 492}
1041 493
1042// #################################################################################################
1043
1044// = thread_gc( lane_ud )
1045//
1046// Cleanup for a thread userdata. If the thread is still executing, leave it
1047// alive as a free-running thread (will clean up itself).
1048//
1049// * Why NOT cancel/kill a loose thread:
1050//
1051// At least timer system uses a free-running thread, they should be handy
1052// and the issue of canceling/killing threads at gc is not very nice, either
1053// (would easily cause waits at gc cycle, which we don't want).
1054//
1055[[nodiscard]] static int lane_gc(lua_State* L_)
1056{
1057 bool _have_gc_cb{ false };
1058 Lane* const _lane{ ToLane(L_, 1) }; // L_: ud
1059
1060 // if there a gc callback?
1061 lua_getiuservalue(L_, 1, 1); // L_: ud uservalue
1062 kLaneGC.pushKey(L_); // L_: ud uservalue __gc
1063 lua_rawget(L_, -2); // L_: ud uservalue gc_cb|nil
1064 if (!lua_isnil(L_, -1)) {
1065 lua_remove(L_, -2); // L_: ud gc_cb|nil
1066 lua_pushstring(L_, _lane->debugName); // L_: ud gc_cb name
1067 _have_gc_cb = true;
1068 } else {
1069 lua_pop(L_, 2); // L_: ud
1070 }
1071
1072 // We can read 'lane->status' without locks, but not wait for it
1073 if (_lane->status < Lane::Done) {
1074 // still running: will have to be cleaned up later
1075 selfdestruct_add(_lane);
1076 assert(_lane->selfdestruct_next);
1077 if (_have_gc_cb) {
1078 lua_pushliteral(L_, "selfdestruct"); // L_: ud gc_cb name status
1079 lua_call(L_, 2, 0); // L_: ud
1080 }
1081 return 0;
1082 } else if (_lane->L) {
1083 // no longer accessing the Lua VM: we can close right now
1084 lua_close(_lane->L);
1085 _lane->L = nullptr;
1086 // just in case, but s will be freed soon so...
1087 _lane->debugName = "<gc>";
1088 }
1089
1090 // Clean up after a (finished) thread
1091 delete _lane;
1092
1093 // do this after lane cleanup in case the callback triggers an error
1094 if (_have_gc_cb) {
1095 lua_pushliteral(L_, "closed"); // L_: ud gc_cb name status
1096 lua_call(L_, 2, 0); // L_: ud
1097 }
1098 return 0;
1099}
1100
1101// #################################################################################################
1102
1103//---
1104// str= thread_status( lane )
1105//
1106// Returns: "pending" not started yet
1107// -> "running" started, doing its work..
1108// <-> "waiting" blocked in a receive()
1109// -> "done" finished, results are there
1110// / "error" finished at an error, error value is there
1111// / "cancelled" execution cancelled by M (state gone)
1112//
1113[[nodiscard]] static char const* thread_status_string(Lane::Status status_)
1114{
1115 char const* const _str{
1116 (status_ == Lane::Pending) ? "pending" :
1117 (status_ == Lane::Running) ? "running" : // like in 'co.status()'
1118 (status_ == Lane::Waiting) ? "waiting" :
1119 (status_ == Lane::Done) ? "done" :
1120 (status_ == Lane::Error) ? "error" :
1121 (status_ == Lane::Cancelled) ? "cancelled" :
1122 nullptr
1123 };
1124 return _str;
1125}
1126
1127// #################################################################################################
1128
1129void Lane::pushThreadStatus(lua_State* L_)
1130{
1131 char const* const _str{ thread_status_string(status) };
1132 LUA_ASSERT(L_, _str);
1133
1134 lua_pushstring(L_, _str);
1135}
1136
1137// #################################################################################################
1138
1139//---
1140// [...] | [nil, err_any, stack_tbl]= thread_join( lane_ud [, wait_secs=-1] )
1141//
1142// timeout: returns nil
1143// done: returns return values (0..N)
1144// error: returns nil + error value [+ stack table]
1145// cancelled: returns nil
1146//
1147LUAG_FUNC(thread_join)
1148{
1149 Lane* const _lane{ ToLane(L_, 1) };
1150 lua_State* const _L2{ _lane->L };
1151
1152 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
1153 if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion
1154 lua_Duration const duration{ lua_tonumber(L_, 2) };
1155 if (duration.count() >= 0.0) {
1156 _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration);
1157 } else {
1158 raise_luaL_argerror(L_, 2, "duration cannot be < 0");
1159 }
1160
1161 } else if (!lua_isnoneornil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key
1162 raise_luaL_argerror(L_, 2, "incorrect duration type");
1163 }
1164
1165 bool const done{ !_lane->thread.joinable() || _lane->waitForCompletion(_until) };
1166 lua_settop(L_, 1); // L_: lane
1167 if (!done || !_L2) {
1168 lua_pushnil(L_); // L_: lane nil
1169 lua_pushliteral(L_, "timeout"); // L_: lane nil "timeout"
1170 return 2;
1171 }
1172
1173 STACK_CHECK_START_REL(L_, 0); // L_: lane
1174 // Thread is Done/Error/Cancelled; all ours now
1175
1176 int _ret{ 0 };
1177 // debugName is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed
1178 // so store it in the userdata uservalue at a key that can't possibly collide
1179 _lane->securizeDebugName(L_);
1180 switch (_lane->status) {
1181 case Lane::Done:
1182 {
1183 int const _n{ lua_gettop(_L2) }; // whole L2 stack
1184 if (
1185 (_n > 0) &&
1186 (InterCopyContext{ _lane->U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} }.inter_move(_n) != InterCopyResult::Success)
1187 ) { // L_: lane results L2:
1188 raise_luaL_error(L_, "tried to copy unsupported types");
1189 }
1190 _ret = _n;
1191 }
1192 break;
1193
1194 case Lane::Error:
1195 {
1196 int const _n{ lua_gettop(_L2) }; // L_: lane L2: "err" [trace]
1197 STACK_GROW(L_, 3);
1198 lua_pushnil(L_); // L_: lane nil
1199 // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ...
1200 InterCopyContext _c{ _lane->U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} };
1201 if (_c.inter_move(_n) != InterCopyResult::Success) { // L_: lane nil "err" [trace] L2:
1202 raise_luaL_error(L_, "tried to copy unsupported types: %s", lua_tostring(L_, -_n));
1203 }
1204 _ret = 1 + _n;
1205 }
1206 break;
1207
1208 case Lane::Cancelled:
1209 _ret = 0;
1210 break;
1211
1212 default:
1213 DEBUGSPEW_CODE(fprintf(stderr, "Status: %d\n", _lane->status));
1214 LUA_ASSERT(L_, false);
1215 _ret = 0;
1216 }
1217 lua_close(_L2);
1218 _lane->L = nullptr;
1219 STACK_CHECK(L_, _ret);
1220 return _ret;
1221}
1222
1223// #################################################################################################
1224
1225// lane:__index(key,usr) -> value
1226//
1227// If key is found in the environment, return it
1228// If key is numeric, wait until the thread returns and populate the environment with the return values
1229// If the return values signal an error, propagate it
1230// If key is "status" return the thread status
1231// Else raise an error
1232LUAG_FUNC(thread_index)
1233{
1234 static constexpr int kSelf{ 1 };
1235 static constexpr int kKey{ 2 };
1236 Lane* const _lane{ ToLane(L_, kSelf) };
1237 LUA_ASSERT(L_, lua_gettop(L_) == 2);
1238
1239 STACK_GROW(L_, 8); // up to 8 positions are needed in case of error propagation
1240
1241 // If key is numeric, wait until the thread returns and populate the environment with the return values
1242 if (lua_type(L_, kKey) == LUA_TNUMBER) {
1243 static constexpr int kUsr{ 3 };
1244 // first, check that we don't already have an environment that holds the requested value
1245 {
1246 // If key is found in the uservalue, return it
1247 lua_getiuservalue(L_, kSelf, 1);
1248 lua_pushvalue(L_, kKey);
1249 lua_rawget(L_, kUsr);
1250 if (!lua_isnil(L_, -1)) {
1251 return 1;
1252 }
1253 lua_pop(L_, 1);
1254 }
1255 {
1256 // check if we already fetched the values from the thread or not
1257 lua_pushinteger(L_, 0);
1258 lua_rawget(L_, kUsr);
1259 bool const _fetched{ !lua_isnil(L_, -1) };
1260 lua_pop(L_, 1); // back to our 2 args + uservalue on the stack
1261 if (!_fetched) {
1262 lua_pushinteger(L_, 0);
1263 lua_pushboolean(L_, 1);
1264 lua_rawset(L_, kUsr);
1265 // wait until thread has completed
1266 lua_pushcfunction(L_, LG_thread_join);
1267 lua_pushvalue(L_, kSelf);
1268 lua_call(L_, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+
1269 switch (_lane->status) {
1270 default:
1271 // this is an internal error, we probably never get here
1272 lua_settop(L_, 0);
1273 lua_pushliteral(L_, "Unexpected status: ");
1274 lua_pushstring(L_, thread_status_string(_lane->status));
1275 lua_concat(L_, 2);
1276 raise_lua_error(L_);
1277 [[fallthrough]]; // fall through if we are killed, as we got nil, "killed" on the stack
1278
1279 case Lane::Done: // got regular return values
1280 {
1281 int const _nvalues{ lua_gettop(L_) - 3 };
1282 for (int _i = _nvalues; _i > 0; --_i) {
1283 // pop the last element of the stack, to store it in the uservalue at its proper index
1284 lua_rawseti(L_, kUsr, _i);
1285 }
1286 }
1287 break;
1288
1289 case Lane::Error: // got 3 values: nil, errstring, callstack table
1290 // me[-2] could carry the stack table, but even
1291 // me[-1] is rather unnecessary (and undocumented);
1292 // use ':join()' instead. --AKa 22-Jan-2009
1293 LUA_ASSERT(L_, lua_isnil(L_, 4) && !lua_isnil(L_, 5) && lua_istable(L_, 6));
1294 // store errstring at key -1
1295 lua_pushnumber(L_, -1);
1296 lua_pushvalue(L_, 5);
1297 lua_rawset(L_, kUsr);
1298 break;
1299
1300 case Lane::Cancelled:
1301 // do nothing
1302 break;
1303 }
1304 }
1305 lua_settop(L_, 3); // L_: self KEY ENV
1306 int const _key{ static_cast<int>(lua_tointeger(L_, kKey)) };
1307 if (_key != -1) {
1308 lua_pushnumber(L_, -1); // L_: self KEY ENV -1
1309 lua_rawget(L_, kUsr); // L_: self KEY ENV "error"|nil
1310 if (!lua_isnil(L_, -1)) { // L_: an error was stored
1311 // Note: Lua 5.1 interpreter is not prepared to show
1312 // non-string errors, so we use 'tostring()' here
1313 // to get meaningful output. --AKa 22-Jan-2009
1314 //
1315 // Also, the stack dump we get is no good; it only
1316 // lists our internal Lanes functions. There seems
1317 // to be no way to switch it off, though.
1318 //
1319 // Level 3 should show the line where 'h[x]' was read
1320 // but this only seems to work for string messages
1321 // (Lua 5.1.4). No idea, why. --AKa 22-Jan-2009
1322 lua_getmetatable(L_, kSelf); // L_: self KEY ENV "error" mt
1323 lua_getfield(L_, -1, "cached_error"); // L_: self KEY ENV "error" mt error()
1324 lua_getfield(L_, -2, "cached_tostring"); // L_: self KEY ENV "error" mt error() tostring()
1325 lua_pushvalue(L_, 4); // L_: self KEY ENV "error" mt error() tostring() "error"
1326 lua_call(L_, 1, 1); // tostring(errstring) -- just in case // L_: self KEY ENV "error" mt error() "error"
1327 lua_pushinteger(L_, 3); // L_: self KEY ENV "error" mt error() "error" 3
1328 lua_call(L_, 2, 0); // error(tostring(errstring), 3) -> doesn't return // L_: self KEY ENV "error" mt
1329 } else {
1330 lua_pop(L_, 1); // L_: self KEY ENV
1331 }
1332 }
1333 lua_rawgeti(L_, kUsr, _key);
1334 }
1335 return 1;
1336 }
1337 if (lua_type(L_, kKey) == LUA_TSTRING) {
1338 char const* const _keystr{ lua_tostring(L_, kKey) };
1339 lua_settop(L_, 2); // keep only our original arguments on the stack
1340 if (strcmp(_keystr, "status") == 0) {
1341 _lane->pushThreadStatus(L_); // push the string representing the status
1342 return 1;
1343 }
1344 // return self.metatable[key]
1345 lua_getmetatable(L_, kSelf); // L_: self KEY mt
1346 lua_replace(L_, -3); // L_: mt KEY
1347 lua_rawget(L_, -2); // L_: mt value
1348 // only "cancel" and "join" are registered as functions, any other string will raise an error
1349 if (!lua_iscfunction(L_, -1)) {
1350 raise_luaL_error(L_, "can't index a lane with '%s'", _keystr);
1351 }
1352 return 1;
1353 }
1354 // unknown key
1355 lua_getmetatable(L_, kSelf); // L_: mt
1356 lua_getfield(L_, -1, "cached_error"); // L_: mt error
1357 lua_pushliteral(L_, "Unknown key: "); // L_: mt error "Unknown key: "
1358 lua_pushvalue(L_, kKey); // L_: mt error "Unknown key: " k
1359 lua_concat(L_, 2); // L_: mt error "Unknown key: <k>"
1360 lua_call(L_, 1, 0); // error( "Unknown key: " .. key) -> doesn't return // L_: mt
1361 return 0;
1362}
1363
1364// ################################################################################################ 494// ################################################################################################
1365 495
1366#if HAVE_LANE_TRACKING() 496#if HAVE_LANE_TRACKING()
@@ -1649,27 +779,7 @@ LUAG_FUNC(configure)
1649 779
1650 // prepare the metatable for threads 780 // prepare the metatable for threads
1651 // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname } 781 // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname }
1652 // 782 Lane::PushMetatable(L_);
1653 if (luaL_newmetatable(L_, kLaneMetatableName)) { // L_: settings M mt
1654 lua_pushcfunction(L_, lane_gc); // L_: settings M mt lane_gc
1655 lua_setfield(L_, -2, "__gc"); // L_: settings M mt
1656 lua_pushcfunction(L_, LG_thread_index); // L_: settings M mt LG_thread_index
1657 lua_setfield(L_, -2, "__index"); // L_: settings M mt
1658 lua_getglobal(L_, "error"); // L_: settings M mt error
1659 LUA_ASSERT(L_, lua_isfunction(L_, -1));
1660 lua_setfield(L_, -2, "cached_error"); // L_: settings M mt
1661 lua_getglobal(L_, "tostring"); // L_: settings M mt tostring
1662 LUA_ASSERT(L_, lua_isfunction(L_, -1));
1663 lua_setfield(L_, -2, "cached_tostring"); // L_: settings M mt
1664 lua_pushcfunction(L_, LG_thread_join); // L_: settings M mt LG_thread_join
1665 lua_setfield(L_, -2, "join"); // L_: settings M mt
1666 lua_pushcfunction(L_, LG_get_debug_threadname); // L_: settings M mt LG_get_debug_threadname
1667 lua_setfield(L_, -2, "get_debug_threadname"); // L_: settings M mt
1668 lua_pushcfunction(L_, LG_thread_cancel); // L_: settings M mt LG_thread_cancel
1669 lua_setfield(L_, -2, "cancel"); // L_: settings M mt
1670 lua_pushliteral(L_, kLaneMetatableName); // L_: settings M mt "Lane"
1671 lua_setfield(L_, -2, "__metatable"); // L_: settings M mt
1672 }
1673 783
1674 lua_pushcclosure(L_, LG_lane_new, 1); // L_: settings M lane_new 784 lua_pushcclosure(L_, LG_lane_new, 1); // L_: settings M lane_new
1675 lua_setfield(L_, -2, "lane_new"); // L_: settings M 785 lua_setfield(L_, -2, "lane_new"); // L_: settings M