aboutsummaryrefslogtreecommitdiff
path: root/src/lanes.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lanes.cpp')
-rw-r--r--src/lanes.cpp2054
1 files changed, 2054 insertions, 0 deletions
diff --git a/src/lanes.cpp b/src/lanes.cpp
new file mode 100644
index 0000000..1f795cc
--- /dev/null
+++ b/src/lanes.cpp
@@ -0,0 +1,2054 @@
1/*
2 * LANES.CPP Copyright (c) 2007-08, Asko Kauppi
3 * Copyright (C) 2009-24, Benoit Germain
4 *
5 * Multithreading in Lua.
6 *
7 * History:
8 * See CHANGES
9 *
10 * Platforms (tested internally):
11 * OS X (10.5.7 PowerPC/Intel)
12 * Linux x86 (Ubuntu 8.04)
13 * Win32 (Windows XP Home SP2, Visual C++ 2005/2008 Express)
14 *
15 * Platforms (tested externally):
16 * Win32 (MSYS) by Ross Berteig.
17 *
18 * Platforms (testers appreciated):
19 * Win64 - should work???
20 * Linux x64 - should work
21 * FreeBSD - should work
22 * QNX - porting shouldn't be hard
23 * Sun Solaris - porting shouldn't be hard
24 *
25 * References:
26 * "Porting multithreaded applications from Win32 to Mac OS X":
27 * <http://developer.apple.com/macosx/multithreadedprogramming.html>
28 *
29 * Pthreads:
30 * <http://vergil.chemistry.gatech.edu/resources/programming/threads.html>
31 *
32 * MSDN: <http://msdn2.microsoft.com/en-us/library/ms686679.aspx>
33 *
34 * <http://ridiculousfish.com/blog/archives/2007/02/17/barrier>
35 *
36 * Defines:
37 * -DLINUX_SCHED_RR: all threads are lifted to SCHED_RR category, to
38 * allow negative priorities [-3,-1] be used. Even without this,
39 * using priorities will require 'sudo' privileges on Linux.
40 *
41 * -DUSE_PTHREAD_TIMEDJOIN: use 'pthread_timedjoin_np()' for waiting
42 * for threads with a timeout. This changes the thread cleanup
43 * mechanism slightly (cleans up at the join, not once the thread
44 * has finished). May or may not be a good idea to use it.
45 * Available only in selected operating systems (Linux).
46 *
47 * Bugs:
48 *
49 * To-do:
50 *
51 * Make waiting threads cancellable.
52 * ...
53 */
54
55/*
56===============================================================================
57
58Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com>
59 2011-24 Benoit Germain <bnt.germain@gmail.com>
60
61Permission is hereby granted, free of charge, to any person obtaining a copy
62of this software and associated documentation files (the "Software"), to deal
63in the Software without restriction, including without limitation the rights
64to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
65copies of the Software, and to permit persons to whom the Software is
66furnished to do so, subject to the following conditions:
67
68The above copyright notice and this permission notice shall be included in
69all copies or substantial portions of the Software.
70
71THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
72IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
73FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
74AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
75LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
76OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
77THE SOFTWARE.
78
79===============================================================================
80*/
81
82#include "lanes.h"
83
84#include "compat.h"
85#include "keeper.h"
86#include "lanes_private.h"
87#include "state.h"
88#include "threading.h"
89#include "tools.h"
90#include "universe.h"
91
92#if !(defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC))
93# include <sys/time.h>
94#endif
95
96/* geteuid() */
97#ifdef PLATFORM_LINUX
98# include <unistd.h>
99# include <sys/types.h>
100#endif
101
102#include <atomic>
103
104// forwarding (will do things better later)
105static void tracking_add(Lane* lane_);
106
107Lane::Lane(Universe* U_, lua_State* L_)
108: U{ U_ }
109, L{ L_ }
110{
111#if HAVE_LANE_TRACKING()
112 if (U->tracking_first)
113 {
114 tracking_add(this);
115 }
116#endif // HAVE_LANE_TRACKING()
117}
118
119bool Lane::waitForCompletion(lua_Duration duration_)
120{
121 std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
122 if (duration_.count() >= 0.0)
123 {
124 until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration_);
125 }
126
127 std::unique_lock lock{ m_done_mutex };
128 //std::stop_token token{ m_thread.get_stop_token() };
129 //return m_done_signal.wait_until(lock, token, secs_, [this](){ return m_status >= Lane::Done; });
130 return m_done_signal.wait_until(lock, until, [this](){ return m_status >= Lane::Done; });
131}
132
133static void lane_main(Lane* lane);
134void Lane::startThread(int priority_)
135{
136 m_thread = std::jthread([this]() { lane_main(this); });
137 if (priority_ != THREAD_PRIO_DEFAULT)
138 {
139 JTHREAD_SET_PRIORITY(m_thread, priority_, U->m_sudo);
140 }
141}
142
143/* Do you want full call stacks, or just the line where the error happened?
144*
145* TBD: The full stack feature does not seem to work (try 'make error').
146*/
147#define ERROR_FULL_STACK 1 // must be either 0 or 1 as we do some index arithmetics with it!
148
149// intern the debug name in the specified lua state so that the pointer remains valid when the lane's state is closed
150static void securize_debug_threadname(lua_State* L, Lane* lane_)
151{
152 STACK_CHECK_START_REL(L, 0);
153 STACK_GROW(L, 3);
154 lua_getiuservalue(L, 1, 1);
155 lua_newtable(L);
156 // Lua 5.1 can't do 'lane_->debug_name = lua_pushstring(L, lane_->debug_name);'
157 lua_pushstring(L, lane_->debug_name);
158 lane_->debug_name = lua_tostring(L, -1);
159 lua_rawset(L, -3);
160 lua_pop(L, 1);
161 STACK_CHECK(L, 0);
162}
163
164#if ERROR_FULL_STACK
165[[nodiscard]] static int lane_error(lua_State* L);
166// crc64/we of string "STACKTRACE_REGKEY" generated at http://www.nitrxgen.net/hashgen/
167static constexpr UniqueKey STACKTRACE_REGKEY{ 0x534af7d3226a429full };
168#endif // ERROR_FULL_STACK
169
170/*
171* registry[FINALIZER_REG_KEY] is either nil (no finalizers) or a table
172* of functions that Lanes will call after the executing 'pcall' has ended.
173*
174* We're NOT using the GC system for finalizer mainly because providing the
175* error (and maybe stack trace) parameters to the finalizer functions would
176* anyways complicate that approach.
177*/
178// crc64/we of string "FINALIZER_REGKEY" generated at http://www.nitrxgen.net/hashgen/
179static constexpr UniqueKey FINALIZER_REGKEY{ 0x188fccb8bf348e09ull };
180
181// #################################################################################################
182
183/*
184* Push a table stored in registry onto Lua stack.
185*
186* If there is no existing table, create one if 'create' is true.
187*
188* Returns: true if a table was pushed
189* false if no table found, not created, and nothing pushed
190*/
191[[nodiscard]] static bool push_registry_table(lua_State* L, UniqueKey key, bool create)
192{
193 STACK_GROW(L, 3);
194 STACK_CHECK_START_REL(L, 0);
195
196 key.pushValue(L); // ?
197 if (lua_isnil(L, -1)) // nil?
198 {
199 lua_pop(L, 1); //
200 STACK_CHECK(L, 0);
201
202 if (!create)
203 {
204 return false;
205 }
206
207 lua_newtable(L); // t
208 key.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); });
209 }
210 STACK_CHECK(L, 1);
211 return true; // table pushed
212}
213
214// #################################################################################################
215
216#if HAVE_LANE_TRACKING()
217
218// The chain is ended by '(Lane*)(-1)', not nullptr:
219// 'tracking_first -> ... -> ... -> (-1)'
220#define TRACKING_END ((Lane *)(-1))
221
222/*
223 * Add the lane to tracking chain; the ones still running at the end of the
224 * whole process will be cancelled.
225 */
226static void tracking_add(Lane* lane_)
227{
228 std::lock_guard<std::mutex> guard{ lane_->U->tracking_cs };
229 assert(lane_->tracking_next == nullptr);
230
231 lane_->tracking_next = lane_->U->tracking_first;
232 lane_->U->tracking_first = lane_;
233}
234
235// #################################################################################################
236
237/*
238 * A free-running lane has ended; remove it from tracking chain
239 */
240[[nodiscard]] static bool tracking_remove(Lane* lane_)
241{
242 bool found{ false };
243 std::lock_guard<std::mutex> guard{ lane_->U->tracking_cs };
244 // Make sure (within the MUTEX) that we actually are in the chain
245 // still (at process exit they will remove us from chain and then
246 // cancel/kill).
247 //
248 if (lane_->tracking_next != nullptr)
249 {
250 Lane** ref = (Lane**) &lane_->U->tracking_first;
251
252 while( *ref != TRACKING_END)
253 {
254 if (*ref == lane_)
255 {
256 *ref = lane_->tracking_next;
257 lane_->tracking_next = nullptr;
258 found = true;
259 break;
260 }
261 ref = (Lane**) &((*ref)->tracking_next);
262 }
263 assert( found);
264 }
265 return found;
266}
267
268#endif // HAVE_LANE_TRACKING()
269
270// #################################################################################################
271
272Lane::~Lane()
273{
274 // Clean up after a (finished) thread
275 //
276#if HAVE_LANE_TRACKING()
277 if (U->tracking_first != nullptr)
278 {
279 // Lane was cleaned up, no need to handle at process termination
280 std::ignore = tracking_remove(this);
281 }
282#endif // HAVE_LANE_TRACKING()
283}
284
285/*
286 * ###############################################################################################
287 * ########################################## Finalizer ##########################################
288 * ###############################################################################################
289 */
290
291//---
292// void= finalizer( finalizer_func )
293//
294// finalizer_func( [err, stack_tbl] )
295//
296// Add a function that will be called when exiting the lane, either via
297// normal return or an error.
298//
299LUAG_FUNC( set_finalizer)
300{
301 luaL_argcheck(L, lua_isfunction(L, 1), 1, "finalizer should be a function");
302 luaL_argcheck(L, lua_gettop( L) == 1, 1, "too many arguments");
303 // Get the current finalizer table (if any), create one if it doesn't exist
304 std::ignore = push_registry_table(L, FINALIZER_REGKEY, true); // finalizer {finalisers}
305 STACK_GROW(L, 2);
306 lua_pushinteger(L, lua_rawlen(L, -1) + 1); // finalizer {finalisers} idx
307 lua_pushvalue(L, 1); // finalizer {finalisers} idx finalizer
308 lua_rawset(L, -3); // finalizer {finalisers}
309 lua_pop(L, 2); //
310 return 0;
311}
312
313
314//---
315// Run finalizers - if any - with the given parameters
316//
317// If 'rc' is nonzero, error message and stack index (the latter only when ERROR_FULL_STACK == 1) are available as:
318// [-1]: stack trace (table)
319// [-2]: error message (any type)
320//
321// Returns:
322// 0 if finalizers were run without error (or there were none)
323// LUA_ERRxxx return code if any of the finalizers failed
324//
325// TBD: should we add stack trace on failing finalizer, wouldn't be hard..
326//
327static void push_stack_trace( lua_State* L, int rc_, int stk_base_);
328
329[[nodiscard]] static int run_finalizers(lua_State* L, int lua_rc)
330{
331 int finalizers_index;
332 int n;
333 int err_handler_index = 0;
334 int rc = LUA_OK; // ...
335 if (!push_registry_table(L, FINALIZER_REGKEY, false)) // ... finalizers?
336 {
337 return 0; // no finalizers
338 }
339
340 STACK_GROW(L, 5);
341
342 finalizers_index = lua_gettop( L);
343
344#if ERROR_FULL_STACK
345 lua_pushcfunction(L, lane_error); // ... finalizers lane_error
346 err_handler_index = lua_gettop( L);
347#endif // ERROR_FULL_STACK
348
349 for( n = (int) lua_rawlen(L, finalizers_index); n > 0; -- n)
350 {
351 int args = 0;
352 lua_pushinteger(L, n); // ... finalizers lane_error n
353 lua_rawget(L, finalizers_index); // ... finalizers lane_error finalizer
354 ASSERT_L( lua_isfunction(L, -1));
355 if (lua_rc != LUA_OK) // we have an error message and an optional stack trace at the bottom of the stack
356 {
357 ASSERT_L( finalizers_index == 2 || finalizers_index == 3);
358 //char const* err_msg = lua_tostring(L, 1);
359 lua_pushvalue(L, 1); // ... finalizers lane_error finalizer err_msg
360 // note we don't always have a stack trace for example when CANCEL_ERROR, or when we got an error that doesn't call our handler, such as LUA_ERRMEM
361 if (finalizers_index == 3)
362 {
363 lua_pushvalue(L, 2); // ... finalizers lane_error finalizer err_msg stack_trace
364 }
365 args = finalizers_index - 1;
366 }
367
368 // if no error from the main body, finalizer doesn't receive any argument, else it gets the error message and optional stack trace
369 rc = lua_pcall(L, args, 0, err_handler_index); // ... finalizers lane_error err_msg2?
370 if (rc != LUA_OK)
371 {
372 push_stack_trace(L, rc, lua_gettop( L));
373 // If one finalizer fails, don't run the others. Return this
374 // as the 'real' error, replacing what we could have had (or not)
375 // from the actual code.
376 break;
377 }
378 // no error, proceed to next finalizer // ... finalizers lane_error
379 }
380
381 if (rc != LUA_OK)
382 {
383 // ERROR_FULL_STACK accounts for the presence of lane_error on the stack
384 int nb_err_slots = lua_gettop( L) - finalizers_index - ERROR_FULL_STACK;
385 // a finalizer generated an error, this is what we leave of the stack
386 for( n = nb_err_slots; n > 0; -- n)
387 {
388 lua_replace(L, n);
389 }
390 // leave on the stack only the error and optional stack trace produced by the error in the finalizer
391 lua_settop(L, nb_err_slots);
392 }
393 else // no error from the finalizers, make sure only the original return values from the lane body remain on the stack
394 {
395 lua_settop(L, finalizers_index - 1);
396 }
397
398 return rc;
399}
400
401/*
402 * ###############################################################################################
403 * ########################################### Threads ###########################################
404 * ###############################################################################################
405 */
406
407//
408// Protects modifying the selfdestruct chain
409
410#define SELFDESTRUCT_END ((Lane*)(-1))
411//
412// The chain is ended by '(Lane*)(-1)', not nullptr:
413// 'selfdestruct_first -> ... -> ... -> (-1)'
414
415/*
416 * Add the lane to selfdestruct chain; the ones still running at the end of the
417 * whole process will be cancelled.
418 */
419static void selfdestruct_add(Lane* lane_)
420{
421 std::lock_guard<std::mutex> guard{ lane_->U->selfdestruct_cs };
422 assert(lane_->selfdestruct_next == nullptr);
423
424 lane_->selfdestruct_next = lane_->U->selfdestruct_first;
425 lane_->U->selfdestruct_first = lane_;
426}
427
428// ###############################################################################################
429
430/*
431 * A free-running lane has ended; remove it from selfdestruct chain
432 */
433[[nodiscard]] static bool selfdestruct_remove(Lane* lane_)
434{
435 bool found{ false };
436 std::lock_guard<std::mutex> guard{ lane_->U->selfdestruct_cs };
437 // Make sure (within the MUTEX) that we actually are in the chain
438 // still (at process exit they will remove us from chain and then
439 // cancel/kill).
440 //
441 if (lane_->selfdestruct_next != nullptr)
442 {
443 Lane** ref = (Lane**) &lane_->U->selfdestruct_first;
444
445 while (*ref != SELFDESTRUCT_END)
446 {
447 if (*ref == lane_)
448 {
449 *ref = lane_->selfdestruct_next;
450 lane_->selfdestruct_next = nullptr;
451 // the terminal shutdown should wait until the lane is done with its lua_close()
452 lane_->U->selfdestructing_count.fetch_add(1, std::memory_order_release);
453 found = true;
454 break;
455 }
456 ref = (Lane**) &((*ref)->selfdestruct_next);
457 }
458 assert(found);
459 }
460 return found;
461}
462
463// ###############################################################################################
464
465/*
466* Process end; cancel any still free-running threads
467*/
468[[nodiscard]] static int universe_gc(lua_State* L)
469{
470 Universe* const U{ lua_tofulluserdata<Universe>(L, 1) };
471 lua_Duration const shutdown_timeout{ lua_tonumber(L, lua_upvalueindex(1)) };
472 [[maybe_unused]] char const* const op_string{ lua_tostring(L, lua_upvalueindex(2)) };
473 CancelOp const op{ which_cancel_op(op_string) };
474
475 if (U->selfdestruct_first != SELFDESTRUCT_END)
476 {
477
478 // Signal _all_ still running threads to exit (including the timer thread)
479 //
480 {
481 std::lock_guard<std::mutex> guard{ U->selfdestruct_cs };
482 Lane* lane{ U->selfdestruct_first };
483 lua_Duration timeout{ 1us };
484 while (lane != SELFDESTRUCT_END)
485 {
486 // attempt the requested cancel with a small timeout.
487 // if waiting on a linda, they will raise a cancel_error.
488 // if a cancellation hook is desired, it will be installed to try to raise an error
489 if (lane->m_thread.joinable())
490 {
491 std::ignore = thread_cancel(lane, op, 1, timeout, true);
492 }
493 lane = lane->selfdestruct_next;
494 }
495 }
496
497 // When noticing their cancel, the lanes will remove themselves from
498 // the selfdestruct chain.
499 {
500 std::chrono::time_point<std::chrono::steady_clock> t_until{ std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(shutdown_timeout) };
501
502 while (U->selfdestruct_first != SELFDESTRUCT_END)
503 {
504 // give threads time to act on their cancel
505 std::this_thread::yield();
506 // count the number of cancelled thread that didn't have the time to act yet
507 int n{ 0 };
508 {
509 std::lock_guard<std::mutex> guard{ U->selfdestruct_cs };
510 Lane* lane{ U->selfdestruct_first };
511 while (lane != SELFDESTRUCT_END)
512 {
513 if (lane->cancel_request != CancelRequest::None)
514 ++n;
515 lane = lane->selfdestruct_next;
516 }
517 }
518 // if timeout elapsed, or we know all threads have acted, stop waiting
519 std::chrono::time_point<std::chrono::steady_clock> t_now = std::chrono::steady_clock::now();
520 if (n == 0 || (t_now >= t_until))
521 {
522 DEBUGSPEW_CODE(fprintf(stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout.count()));
523 break;
524 }
525 }
526 }
527
528 // If some lanes are currently cleaning after themselves, wait until they are done.
529 // They are no longer listed in the selfdestruct chain, but they still have to lua_close().
530 while (U->selfdestructing_count.load(std::memory_order_acquire) > 0)
531 {
532 std::this_thread::yield();
533 }
534 }
535
536 // If after all this, we still have some free-running lanes, it's an external user error, they should have stopped appropriately
537 {
538 std::lock_guard<std::mutex> guard{ U->selfdestruct_cs };
539 Lane* lane{ U->selfdestruct_first };
540 if (lane != SELFDESTRUCT_END)
541 {
542 // this causes a leak because we don't call U's destructor (which could be bad if the still running lanes are accessing it)
543 luaL_error(L, "Zombie thread %s refuses to die!", lane->debug_name); // doesn't return
544 }
545 }
546
547 // necessary so that calling free_deep_prelude doesn't crash because linda_id expects a linda lightuserdata at absolute slot 1
548 lua_settop(L, 0);
549 // no need to mutex-protect this as all threads in the universe are gone at that point
550 if (U->timer_deep != nullptr) // test ins case some early internal error prevented Lanes from creating the deep timer
551 {
552 [[maybe_unused]] int const prev_ref_count{ U->timer_deep->m_refcount.fetch_sub(1, std::memory_order_relaxed) };
553 ASSERT_L(prev_ref_count == 1); // this should be the last reference
554 free_deep_prelude(L, U->timer_deep);
555 U->timer_deep = nullptr;
556 }
557
558 close_keepers(U);
559
560 // remove the protected allocator, if any
561 U->protected_allocator.removeFrom(L);
562
563 U->Universe::~Universe();
564
565 // universe is no longer available (nor necessary)
566 // we need to do this in case some deep userdata objects were created before Lanes was initialized,
567 // as potentially they will be garbage collected after Lanes at application shutdown
568 universe_store(L, nullptr);
569 return 0;
570}
571
572// ###############################################################################################
573
574//---
575// = _single( [cores_uint=1] )
576//
577// Limits the process to use only 'cores' CPU cores. To be used for performance
578// testing on multicore devices. DEBUGGING ONLY!
579//
580LUAG_FUNC( set_singlethreaded)
581{
582 lua_Integer cores = luaL_optinteger(L, 1, 1);
583 (void) cores; // prevent "unused" warning
584
585#ifdef PLATFORM_OSX
586#ifdef _UTILBINDTHREADTOCPU
587 if (cores > 1)
588 {
589 return luaL_error(L, "Limiting to N>1 cores not possible");
590 }
591 // requires 'chudInitialize()'
592 utilBindThreadToCPU(0); // # of CPU to run on (we cannot limit to 2..N CPUs?)
593 return 0;
594#else
595 return luaL_error(L, "Not available: compile with _UTILBINDTHREADTOCPU");
596#endif
597#else
598 return luaL_error(L, "not implemented");
599#endif
600}
601
602// ###############################################################################################
603
604/*
605* str= lane_error( error_val|str )
606*
607* Called if there's an error in some lane; add call stack to error message
608* just like 'lua.c' normally does.
609*
610* ".. will be called with the error message and its return value will be the
611* message returned on the stack by lua_pcall."
612*
613* Note: Rather than modifying the error message itself, it would be better
614* to provide the call stack (as string) completely separated. This would
615* work great with non-string error values as well (current system does not).
616* (This is NOT possible with the Lua 5.1 'lua_pcall()'; we could of course
617* implement a Lanes-specific 'pcall' of our own that does this). TBD!!! :)
618* --AKa 22-Jan-2009
619*/
620#if ERROR_FULL_STACK
621
622// crc64/we of string "EXTENDED_STACKTRACE_REGKEY" generated at http://www.nitrxgen.net/hashgen/
623static constexpr UniqueKey EXTENDED_STACKTRACE_REGKEY{ 0x2357c69a7c92c936ull }; // used as registry key
624
625LUAG_FUNC( set_error_reporting)
626{
627 luaL_checktype(L, 1, LUA_TSTRING);
628 char const* mode{ lua_tostring(L, 1) };
629 lua_pushliteral(L, "extended");
630 bool const extended{ strcmp(mode, "extended") == 0 };
631 bool const basic{ strcmp(mode, "basic") == 0 };
632 if (!extended && !basic)
633 {
634 return luaL_error(L, "unsupported error reporting model %s", mode);
635 }
636
637 EXTENDED_STACKTRACE_REGKEY.setValue(L, [extended](lua_State* L) { lua_pushboolean(L, extended ? 1 : 0); });
638 return 0;
639}
640
641[[nodiscard]] static int lane_error(lua_State* L)
642{
643 // error message (any type)
644 STACK_CHECK_START_ABS(L, 1); // some_error
645
646 // Don't do stack survey for cancelled lanes.
647 //
648 if (CANCEL_ERROR.equals(L, 1))
649 {
650 return 1; // just pass on
651 }
652
653 STACK_GROW(L, 3);
654 bool const extended{ EXTENDED_STACKTRACE_REGKEY.readBoolValue(L) };
655 STACK_CHECK(L, 1);
656
657 // Place stack trace at 'registry[STACKTRACE_REGKEY]' for the 'lua_pcall()'
658 // caller to fetch. This bypasses the Lua 5.1 limitation of only one
659 // return value from error handler to 'lua_pcall()' caller.
660
661 // It's adequate to push stack trace as a table. This gives the receiver
662 // of the stack best means to format it to their liking. Also, it allows
663 // us to add more stack info later, if needed.
664 //
665 // table of { "sourcefile.lua:<line>", ... }
666 //
667 lua_newtable(L); // some_error {}
668
669 // Best to start from level 1, but in some cases it might be a C function
670 // and we don't get '.currentline' for that. It's okay - just keep level
671 // and table index growing separate. --AKa 22-Jan-2009
672 //
673 lua_Debug ar;
674 for (int n = 1; lua_getstack(L, n, &ar); ++n)
675 {
676 lua_getinfo(L, extended ? "Sln" : "Sl", &ar);
677 if (extended)
678 {
679 lua_newtable(L); // some_error {} {}
680
681 lua_pushstring(L, ar.source); // some_error {} {} source
682 lua_setfield(L, -2, "source"); // some_error {} {}
683
684 lua_pushinteger(L, ar.currentline); // some_error {} {} currentline
685 lua_setfield(L, -2, "currentline"); // some_error {} {}
686
687 lua_pushstring(L, ar.name); // some_error {} {} name
688 lua_setfield(L, -2, "name"); // some_error {} {}
689
690 lua_pushstring(L, ar.namewhat); // some_error {} {} namewhat
691 lua_setfield(L, -2, "namewhat"); // some_error {} {}
692
693 lua_pushstring(L, ar.what); // some_error {} {} what
694 lua_setfield(L, -2, "what"); // some_error {} {}
695 }
696 else if (ar.currentline > 0)
697 {
698 lua_pushfstring(L, "%s:%d", ar.short_src, ar.currentline); // some_error {} "blah:blah"
699 }
700 else
701 {
702 lua_pushfstring(L, "%s:?", ar.short_src); // some_error {} "blah"
703 }
704 lua_rawseti(L, -2, (lua_Integer) n); // some_error {}
705 }
706
707 // store the stack trace table in the registry
708 STACKTRACE_REGKEY.setValue(L, [](lua_State* L) { lua_insert(L, -2); }); // some_error
709
710 STACK_CHECK(L, 1);
711 return 1; // the untouched error value
712}
713#endif // ERROR_FULL_STACK
714
715static void push_stack_trace( lua_State* L, int rc_, int stk_base_)
716{
717 // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry
718 switch( rc_)
719 {
720 case LUA_OK: // no error, body return values are on the stack
721 break;
722
723 case LUA_ERRRUN: // cancellation or a runtime error
724#if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler
725 {
726 STACK_CHECK_START_REL(L, 0);
727 // fetch the call stack table from the registry where the handler stored it
728 STACK_GROW(L, 1);
729 // yields nil if no stack was generated (in case of cancellation for example)
730 STACKTRACE_REGKEY.pushValue(L); // err trace|nil
731 STACK_CHECK(L, 1);
732
733 // For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed
734 // For other errors, the message can be whatever was thrown, and we should have a stack trace table
735 ASSERT_L(lua_type(L, 1 + stk_base_) == (CANCEL_ERROR.equals(L, stk_base_) ? LUA_TNIL : LUA_TTABLE));
736 // Just leaving the stack trace table on the stack is enough to get it through to the master.
737 break;
738 }
739#endif // fall through if not ERROR_FULL_STACK
740
741 case LUA_ERRMEM: // memory allocation error (handler not called)
742 case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition)
743 default:
744 // we should have a single value which is either a string (the error message) or CANCEL_ERROR
745 ASSERT_L((lua_gettop(L) == stk_base_) && ((lua_type(L, stk_base_) == LUA_TSTRING) || CANCEL_ERROR.equals(L, stk_base_)));
746 break;
747 }
748}
749
750// #################################################################################################
751
752LUAG_FUNC(set_debug_threadname)
753{
754 // fnv164 of string "debug_threadname" generated at https://www.pelock.com/products/hash-calculator
755 constexpr UniqueKey hidden_regkey{ 0x79C0669AAAE04440ull };
756 // C s_lane structure is a light userdata upvalue
757 Lane* const lane{ lua_tolightuserdata<Lane>(L, lua_upvalueindex(1)) };
758 luaL_checktype(L, -1, LUA_TSTRING); // "name"
759 lua_settop(L, 1);
760 STACK_CHECK_START_ABS(L, 1);
761 // 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...
762 hidden_regkey.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); });
763 STACK_CHECK(L, 1);
764 lane->debug_name = lua_tostring(L, -1);
765 // keep a direct pointer on the string
766 THREAD_SETNAME(lane->debug_name);
767 // to see VM name in Decoda debugger Virtual Machine window
768 lua_setglobal(L, "decoda_name"); //
769 STACK_CHECK(L, 0);
770 return 0;
771}
772
773// #################################################################################################
774
775LUAG_FUNC(get_debug_threadname)
776{
777 Lane* const lane{ lua_toLane(L, 1) };
778 luaL_argcheck(L, lua_gettop(L) == 1, 2, "too many arguments");
779 lua_pushstring(L, lane->debug_name);
780 return 1;
781}
782
783// #################################################################################################
784
785LUAG_FUNC(set_thread_priority)
786{
787 lua_Integer const prio{ luaL_checkinteger(L, 1) };
788 // public Lanes API accepts a generic range -3/+3
789 // that will be remapped into the platform-specific scheduler priority scheme
790 // On some platforms, -3 is equivalent to -2 and +3 to +2
791 if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX)
792 {
793 return luaL_error(L, "priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio);
794 }
795 THREAD_SET_PRIORITY(static_cast<int>(prio), universe_get(L)->m_sudo);
796 return 0;
797}
798
799// #################################################################################################
800
801LUAG_FUNC(set_thread_affinity)
802{
803 lua_Integer const affinity{ luaL_checkinteger(L, 1) };
804 if (affinity <= 0)
805 {
806 return luaL_error(L, "invalid affinity (%d)", affinity);
807 }
808 THREAD_SET_AFFINITY( static_cast<unsigned int>(affinity));
809 return 0;
810}
811
812#if USE_DEBUG_SPEW()
813// can't use direct LUA_x errcode indexing because the sequence is not the same between Lua 5.1 and 5.2 :-(
814// LUA_ERRERR doesn't have the same value
815struct errcode_name
816{
817 int code;
818 char const* name;
819};
820
821static struct errcode_name s_errcodes[] =
822{
823 { LUA_OK, "LUA_OK"},
824 { LUA_YIELD, "LUA_YIELD"},
825 { LUA_ERRRUN, "LUA_ERRRUN"},
826 { LUA_ERRSYNTAX, "LUA_ERRSYNTAX"},
827 { LUA_ERRMEM, "LUA_ERRMEM"},
828 { LUA_ERRGCMM, "LUA_ERRGCMM"},
829 { LUA_ERRERR, "LUA_ERRERR"},
830};
831static char const* get_errcode_name( int _code)
832{
833 int i;
834 for( i = 0; i < 7; ++ i)
835 {
836 if (s_errcodes[i].code == _code)
837 {
838 return s_errcodes[i].name;
839 }
840 }
841 return "<nullptr>";
842}
843#endif // USE_DEBUG_SPEW()
844
845static void lane_main(Lane* lane)
846{
847 lua_State* const L{ lane->L };
848 // wait until the launching thread has finished preparing L
849 lane->m_ready.wait();
850 int rc{ LUA_ERRRUN };
851 if (lane->m_status == Lane::Pending) // nothing wrong happened during preparation, we can work
852 {
853 // At this point, the lane function and arguments are on the stack
854 int const nargs{ lua_gettop(L) - 1 };
855 DEBUGSPEW_CODE(Universe* U = universe_get(L));
856 lane->m_status = Lane::Running; // Pending -> Running
857
858 // Tie "set_finalizer()" to the state
859 lua_pushcfunction(L, LG_set_finalizer);
860 populate_func_lookup_table(L, -1, "set_finalizer");
861 lua_setglobal(L, "set_finalizer");
862
863 // Tie "set_debug_threadname()" to the state
864 // But don't register it in the lookup database because of the Lane pointer upvalue
865 lua_pushlightuserdata(L, lane);
866 lua_pushcclosure(L, LG_set_debug_threadname, 1);
867 lua_setglobal(L, "set_debug_threadname");
868
869 // Tie "cancel_test()" to the state
870 lua_pushcfunction(L, LG_cancel_test);
871 populate_func_lookup_table(L, -1, "cancel_test");
872 lua_setglobal(L, "cancel_test");
873
874 // this could be done in lane_new before the lane body function is pushed on the stack to avoid unnecessary stack slot shifting around
875#if ERROR_FULL_STACK
876 // Tie "set_error_reporting()" to the state
877 lua_pushcfunction(L, LG_set_error_reporting);
878 populate_func_lookup_table(L, -1, "set_error_reporting");
879 lua_setglobal(L, "set_error_reporting");
880
881 STACK_GROW(L, 1);
882 lua_pushcfunction(L, lane_error); // func args handler
883 lua_insert(L, 1); // handler func args
884#endif // ERROR_FULL_STACK
885
886 rc = lua_pcall(L, nargs, LUA_MULTRET, ERROR_FULL_STACK); // retvals|err
887
888#if ERROR_FULL_STACK
889 lua_remove(L, 1); // retvals|error
890#endif // ERROR_FULL_STACK
891
892 // in case of error and if it exists, fetch stack trace from registry and push it
893 push_stack_trace(L, rc, 1); // retvals|error [trace]
894
895 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name(rc), CANCEL_ERROR.equals(L, 1) ? "cancelled" : lua_typename(L, lua_type(L, 1))));
896 // STACK_DUMP(L);
897 // Call finalizers, if the script has set them up.
898 //
899 int rc2{ run_finalizers(L, rc) };
900 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name(rc2)));
901 if (rc2 != LUA_OK) // Error within a finalizer!
902 {
903 // the finalizer generated an error, and left its own error message [and stack trace] on the stack
904 rc = rc2; // we're overruling the earlier script error or normal return
905 }
906 lane->m_waiting_on = nullptr; // just in case
907 if (selfdestruct_remove(lane)) // check and remove (under lock!)
908 {
909 // We're a free-running thread and no-one's there to clean us up.
910 lua_close(lane->L);
911 lane->L = nullptr; // just in case
912 lane->U->selfdestruct_cs.lock();
913 // done with lua_close(), terminal shutdown sequence may proceed
914 lane->U->selfdestructing_count.fetch_sub(1, std::memory_order_release);
915 lane->U->selfdestruct_cs.unlock();
916
917 // 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
918 lane->m_thread.detach();
919 delete lane;
920 lane = nullptr;
921 }
922 }
923 if (lane)
924 {
925 // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them
926
927 Lane::Status st = (rc == LUA_OK) ? Lane::Done : CANCEL_ERROR.equals(L, 1) ? Lane::Cancelled : Lane::Error;
928
929 {
930 // 'm_done_mutex' protects the -> Done|Error|Cancelled state change
931 std::lock_guard lock{ lane->m_done_mutex };
932 lane->m_status = st;
933 lane->m_done_signal.notify_one();// wake up master (while 'lane->m_done_mutex' is on)
934 }
935 }
936}
937
938// #################################################################################################
939
940// --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required
941// with lanes.require, that will call the regular 'require', then populate the lookup database in the source lane
942// module = lanes.require( "modname")
943// upvalue[1]: _G.require
944LUAG_FUNC(require)
945{
946 char const* name = lua_tostring(L, 1);
947 int const nargs = lua_gettop(L);
948 DEBUGSPEW_CODE(Universe* U = universe_get(L));
949 STACK_CHECK_START_REL(L, 0);
950 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END, name));
951 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
952 lua_pushvalue(L, lua_upvalueindex(1)); // "name" require
953 lua_insert(L, 1); // require "name"
954 lua_call(L, nargs, 1); // module
955 populate_func_lookup_table(L, -1, name);
956 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END, name));
957 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
958 STACK_CHECK(L, 0);
959 return 1;
960}
961
962// #################################################################################################
963
964// --- If a client wants to transfer stuff of a previously required module from the current state to another Lane, the module must be registered
965// to populate the lookup database in the source lane (and in the destination too, of course)
966// lanes.register( "modname", module)
967LUAG_FUNC(register)
968{
969 char const* name = luaL_checkstring(L, 1);
970 LuaType const mod_type{ lua_type_as_enum(L, 2) };
971 // ignore extra parameters, just in case
972 lua_settop(L, 2);
973 luaL_argcheck(L, (mod_type == LuaType::TABLE) || (mod_type == LuaType::FUNCTION), 2, "unexpected module type");
974 DEBUGSPEW_CODE(Universe* U = universe_get(L));
975 STACK_CHECK_START_REL(L, 0); // "name" mod_table
976 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END, name));
977 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
978 populate_func_lookup_table(L, -1, name);
979 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s END\n" INDENT_END, name));
980 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
981 STACK_CHECK(L, 0);
982 return 0;
983}
984
985// #################################################################################################
986
987// crc64/we of string "GCCB_KEY" generated at http://www.nitrxgen.net/hashgen/
988static constexpr UniqueKey GCCB_KEY{ 0xcfb1f046ef074e88ull };
989
990//---
991// lane_ud = lane_new( function
992// , [libs_str]
993// , [priority_int=0]
994// , [globals_tbl]
995// , [package_tbl]
996// , [required_tbl]
997// , [gc_cb_func]
998// [, ... args ...])
999//
1000// Upvalues: metatable to use for 'lane_ud'
1001//
1002LUAG_FUNC(lane_new)
1003{
1004 char const* const libs_str{ lua_tostring(L, 2) };
1005 bool const have_priority{ !lua_isnoneornil(L, 3) };
1006 int const priority{ have_priority ? (int) lua_tointeger(L, 3) : THREAD_PRIO_DEFAULT };
1007 int const globals_idx{ lua_isnoneornil(L, 4) ? 0 : 4 };
1008 int const package_idx{ lua_isnoneornil(L, 5) ? 0 : 5 };
1009 int const required_idx{ lua_isnoneornil(L, 6) ? 0 : 6 };
1010 int const gc_cb_idx{ lua_isnoneornil(L, 7) ? 0 : 7 };
1011
1012 static constexpr int FIXED_ARGS{ 7 };
1013 int const nargs{ lua_gettop(L) - FIXED_ARGS };
1014 Universe* const U{ universe_get(L) };
1015 ASSERT_L( nargs >= 0);
1016
1017 // public Lanes API accepts a generic range -3/+3
1018 // that will be remapped into the platform-specific scheduler priority scheme
1019 // On some platforms, -3 is equivalent to -2 and +3 to +2
1020 if (have_priority && (priority < THREAD_PRIO_MIN || priority > THREAD_PRIO_MAX))
1021 {
1022 return luaL_error(L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, priority);
1023 }
1024
1025 /* --- Create and prepare the sub state --- */
1026 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END));
1027 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
1028
1029 // populate with selected libraries at the same time
1030 lua_State* const L2{ luaG_newstate(U, Source{ L }, libs_str) }; // L // L2
1031
1032 // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread)
1033 Lane* const lane{ new (U) Lane{ U, L2 } };
1034 if (lane == nullptr)
1035 {
1036 return luaL_error(L, "could not create lane: out of memory");
1037 }
1038
1039 class OnExit
1040 {
1041 private:
1042
1043 lua_State* const m_L;
1044 Lane* m_lane{ nullptr };
1045 int const m_gc_cb_idx;
1046 DEBUGSPEW_CODE(Universe* const U); // for DEBUGSPEW only (hence the absence of m_ prefix)
1047
1048 public:
1049
1050 OnExit(lua_State* L_, Lane* lane_, int gc_cb_idx_ DEBUGSPEW_COMMA_PARAM(Universe* U_))
1051 : m_L{ L_ }
1052 , m_lane{ lane_ }
1053 , m_gc_cb_idx{ gc_cb_idx_ }
1054 DEBUGSPEW_COMMA_PARAM(U{ U_ })
1055 {}
1056
1057 ~OnExit()
1058 {
1059 if (m_lane)
1060 {
1061 // we still need a full userdata so that garbage collection can do its thing
1062 prepareUserData();
1063 // leave a single cancel_error on the stack for the caller
1064 lua_settop(m_lane->L, 0);
1065 CANCEL_ERROR.pushKey(m_lane->L);
1066 {
1067 std::lock_guard lock{ m_lane->m_done_mutex };
1068 m_lane->m_status = Lane::Cancelled;
1069 m_lane->m_done_signal.notify_one(); // wake up master (while 'lane->m_done_mutex' is on)
1070 }
1071 // unblock the thread so that it can terminate gracefully
1072 m_lane->m_ready.count_down();
1073 }
1074 }
1075
1076 private:
1077
1078 void prepareUserData()
1079 {
1080 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: preparing lane userdata\n" INDENT_END));
1081 STACK_CHECK_START_REL(m_L, 0);
1082 // a Lane full userdata needs a single uservalue
1083 Lane** const ud{ lua_newuserdatauv<Lane*>(m_L, 1) }; // ... lane
1084 *ud = m_lane; // don't forget to store the pointer in the userdata!
1085
1086 // Set metatable for the userdata
1087 //
1088 lua_pushvalue(m_L, lua_upvalueindex(1)); // ... lane mt
1089 lua_setmetatable(m_L, -2); // ... lane
1090 STACK_CHECK(m_L, 1);
1091
1092 // Create uservalue for the userdata
1093 // (this is where lane body return values will be stored when the handle is indexed by a numeric key)
1094 lua_newtable(m_L); // ... lane uv
1095
1096 // Store the gc_cb callback in the uservalue
1097 if (m_gc_cb_idx > 0)
1098 {
1099 GCCB_KEY.pushKey(m_L); // ... lane uv k
1100 lua_pushvalue(m_L, m_gc_cb_idx); // ... lane uv k gc_cb
1101 lua_rawset(m_L, -3); // ... lane uv
1102 }
1103
1104 lua_setiuservalue(m_L, -2, 1); // ... lane
1105 STACK_CHECK(m_L, 1);
1106 }
1107
1108 public:
1109
1110 void success()
1111 {
1112 prepareUserData();
1113 m_lane->m_ready.count_down();
1114 m_lane = nullptr;
1115 }
1116 } onExit{ L, lane, gc_cb_idx DEBUGSPEW_COMMA_PARAM(U) };
1117 // launch the thread early, it will sync with a std::latch to parallelize OS thread warmup and L2 preparation
1118 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END));
1119 lane->startThread(priority);
1120
1121 STACK_GROW( L2, nargs + 3); //
1122 STACK_CHECK_START_REL(L2, 0);
1123
1124 STACK_GROW(L, 3); // func libs priority globals package required gc_cb [... args ...]
1125 STACK_CHECK_START_REL(L, 0);
1126
1127 // give a default "Lua" name to the thread to see VM name in Decoda debugger
1128 lua_pushfstring( L2, "Lane #%p", L2); // "..."
1129 lua_setglobal( L2, "decoda_name"); //
1130 ASSERT_L( lua_gettop( L2) == 0);
1131
1132 // package
1133 if (package_idx != 0)
1134 {
1135 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END));
1136 // when copying with mode LookupMode::LaneBody, should raise an error in case of problem, not leave it one the stack
1137 [[maybe_unused]] InterCopyResult const ret{ luaG_inter_copy_package(U, Source{ L }, Dest{ L2 }, package_idx, LookupMode::LaneBody) };
1138 ASSERT_L(ret == InterCopyResult::Success); // either all went well, or we should not even get here
1139 }
1140
1141 // modules to require in the target lane *before* the function is transfered!
1142 if (required_idx != 0)
1143 {
1144 int nbRequired = 1;
1145 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END));
1146 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
1147 // should not happen, was checked in lanes.lua before calling lane_new()
1148 if (lua_type(L, required_idx) != LUA_TTABLE)
1149 {
1150 luaL_error(L, "expected required module list as a table, got %s", luaL_typename(L, required_idx)); // doesn't return
1151 }
1152
1153 lua_pushnil(L); // func libs priority globals package required gc_cb [... args ...] nil
1154 while (lua_next(L, required_idx) != 0) // func libs priority globals package required gc_cb [... args ...] n "modname"
1155 {
1156 if (lua_type(L, -1) != LUA_TSTRING || lua_type(L, -2) != LUA_TNUMBER || lua_tonumber(L, -2) != nbRequired)
1157 {
1158 luaL_error(L, "required module list should be a list of strings"); // doesn't return
1159 }
1160 else
1161 {
1162 // require the module in the target state, and populate the lookup table there too
1163 size_t len;
1164 char const* name = lua_tolstring(L, -1, &len);
1165 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END, name));
1166
1167 // require the module in the target lane
1168 lua_getglobal( L2, "require"); // require()?
1169 if (lua_isnil( L2, -1))
1170 {
1171 lua_pop( L2, 1); //
1172 luaL_error(L, "cannot pre-require modules without loading 'package' library first"); // doesn't return
1173 }
1174 else
1175 {
1176 lua_pushlstring( L2, name, len); // require() name
1177 if (lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode
1178 {
1179 // propagate error to main state if any
1180 std::ignore = luaG_inter_move(U
1181 , Source{ L2 }, Dest{ L }
1182 , 1, LookupMode::LaneBody
1183 ); // func libs priority globals package required gc_cb [... args ...] n "modname" error
1184 raise_lua_error(L);
1185 }
1186 // after requiring the module, register the functions it exported in our name<->function database
1187 populate_func_lookup_table( L2, -1, name);
1188 lua_pop( L2, 1); //
1189 }
1190 }
1191 lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] n
1192 ++ nbRequired;
1193 } // func libs priority globals package required gc_cb [... args ...]
1194 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1195 }
1196 STACK_CHECK(L, 0);
1197 STACK_CHECK(L2, 0); //
1198
1199 // Appending the specified globals to the global environment
1200 // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed...
1201 //
1202 if (globals_idx != 0)
1203 {
1204 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer globals\n" INDENT_END));
1205 if (!lua_istable(L, globals_idx))
1206 {
1207 luaL_error(L, "Expected table, got %s", luaL_typename(L, globals_idx)); // doesn't return
1208 }
1209
1210 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
1211 lua_pushnil(L); // func libs priority globals package required gc_cb [... args ...] nil
1212 // Lua 5.2 wants us to push the globals table on the stack
1213 lua_pushglobaltable(L2); // _G
1214 while( lua_next(L, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v
1215 {
1216 std::ignore = luaG_inter_copy(U, Source{ L }, Dest{ L2 }, 2, LookupMode::LaneBody); // _G k v
1217 // assign it in L2's globals table
1218 lua_rawset(L2, -3); // _G
1219 lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] k
1220 } // func libs priority globals package required gc_cb [... args ...]
1221 lua_pop( L2, 1); //
1222
1223 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1224 }
1225 STACK_CHECK(L, 0);
1226 STACK_CHECK(L2, 0);
1227
1228 // Lane main function
1229 LuaType const func_type{ lua_type_as_enum(L, 1) };
1230 if (func_type == LuaType::FUNCTION)
1231 {
1232 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END));
1233 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
1234 lua_pushvalue(L, 1); // func libs priority globals package required gc_cb [... args ...] func
1235 InterCopyResult const res{ luaG_inter_move(U, Source{ L }, Dest{ L2 }, 1, LookupMode::LaneBody) }; // func libs priority globals package required gc_cb [... args ...] // func
1236 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1237 if (res != InterCopyResult::Success)
1238 {
1239 luaL_error(L, "tried to copy unsupported types"); // doesn't return
1240 }
1241 }
1242 else if (func_type == LuaType::STRING)
1243 {
1244 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: compile lane body\n" INDENT_END));
1245 // compile the string
1246 if (luaL_loadstring(L2, lua_tostring(L, 1)) != 0) // func
1247 {
1248 luaL_error(L, "error when parsing lane function code"); // doesn't return
1249 }
1250 }
1251 else
1252 {
1253 luaL_error(L, "Expected function, got %s", lua_typename(L, func_type)); // doesn't return
1254 }
1255 STACK_CHECK(L, 0);
1256 STACK_CHECK(L2, 1);
1257 ASSERT_L(lua_isfunction(L2, 1));
1258
1259 // revive arguments
1260 if (nargs > 0)
1261 {
1262 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END));
1263 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
1264 InterCopyResult const res{ luaG_inter_move(U, Source{ L }, Dest{ L2 }, nargs, LookupMode::LaneBody) }; // func libs priority globals package required gc_cb // func [... args ...]
1265 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1266 if (res != InterCopyResult::Success)
1267 {
1268 luaL_error(L, "tried to copy unsupported types"); // doesn't return
1269 }
1270 }
1271 STACK_CHECK(L, -nargs);
1272 ASSERT_L(lua_gettop( L) == FIXED_ARGS);
1273
1274 // Store 'lane' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive).
1275 LANE_POINTER_REGKEY.setValue(L2, [lane](lua_State* L) { lua_pushlightuserdata(L, lane); }); // func [... args ...]
1276 STACK_CHECK(L2, 1 + nargs);
1277
1278 STACK_CHECK_RESET_REL(L, 0);
1279 // all went well, the lane's thread can start working
1280 onExit.success();
1281 // we should have the lane userdata on top of the stack
1282 STACK_CHECK(L, 1);
1283 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1284 return 1;
1285}
1286
1287// #################################################################################################
1288
1289//---
1290// = thread_gc( lane_ud )
1291//
1292// Cleanup for a thread userdata. If the thread is still executing, leave it
1293// alive as a free-running thread (will clean up itself).
1294//
1295// * Why NOT cancel/kill a loose thread:
1296//
1297// At least timer system uses a free-running thread, they should be handy
1298// and the issue of canceling/killing threads at gc is not very nice, either
1299// (would easily cause waits at gc cycle, which we don't want).
1300//
1301[[nodiscard]] static int lane_gc(lua_State* L)
1302{
1303 bool have_gc_cb{ false };
1304 Lane* const lane{ lua_toLane(L, 1) }; // ud
1305
1306 // if there a gc callback?
1307 lua_getiuservalue(L, 1, 1); // ud uservalue
1308 GCCB_KEY.pushKey(L); // ud uservalue __gc
1309 lua_rawget(L, -2); // ud uservalue gc_cb|nil
1310 if (!lua_isnil(L, -1))
1311 {
1312 lua_remove(L, -2); // ud gc_cb|nil
1313 lua_pushstring(L, lane->debug_name); // ud gc_cb name
1314 have_gc_cb = true;
1315 }
1316 else
1317 {
1318 lua_pop(L, 2); // ud
1319 }
1320
1321 // We can read 'lane->status' without locks, but not wait for it
1322 if (lane->m_status < Lane::Done)
1323 {
1324 // still running: will have to be cleaned up later
1325 selfdestruct_add(lane);
1326 assert(lane->selfdestruct_next);
1327 if (have_gc_cb)
1328 {
1329 lua_pushliteral(L, "selfdestruct"); // ud gc_cb name status
1330 lua_call(L, 2, 0); // ud
1331 }
1332 return 0;
1333 }
1334 else if (lane->L)
1335 {
1336 // no longer accessing the Lua VM: we can close right now
1337 lua_close(lane->L);
1338 lane->L = nullptr;
1339 // just in case, but s will be freed soon so...
1340 lane->debug_name = "<gc>";
1341 }
1342
1343 // Clean up after a (finished) thread
1344 delete lane;
1345
1346 // do this after lane cleanup in case the callback triggers an error
1347 if (have_gc_cb)
1348 {
1349 lua_pushliteral(L, "closed"); // ud gc_cb name status
1350 lua_call(L, 2, 0); // ud
1351 }
1352 return 0;
1353}
1354
1355// #################################################################################################
1356
1357//---
1358// str= thread_status( lane )
1359//
1360// Returns: "pending" not started yet
1361// -> "running" started, doing its work..
1362// <-> "waiting" blocked in a receive()
1363// -> "done" finished, results are there
1364// / "error" finished at an error, error value is there
1365// / "cancelled" execution cancelled by M (state gone)
1366//
1367[[nodiscard]] static char const* thread_status_string(Lane* lane_)
1368{
1369 Lane::Status const st{ lane_->m_status }; // read just once (volatile)
1370 char const* str =
1371 (st == Lane::Pending) ? "pending" :
1372 (st == Lane::Running) ? "running" : // like in 'co.status()'
1373 (st == Lane::Waiting) ? "waiting" :
1374 (st == Lane::Done) ? "done" :
1375 (st == Lane::Error) ? "error" :
1376 (st == Lane::Cancelled) ? "cancelled" : nullptr;
1377 return str;
1378}
1379
1380// #################################################################################################
1381
1382void push_thread_status(lua_State* L, Lane* lane_)
1383{
1384 char const* const str{ thread_status_string(lane_) };
1385 ASSERT_L(str);
1386
1387 std::ignore = lua_pushstring(L, str);
1388}
1389
1390// #################################################################################################
1391
1392//---
1393// [...] | [nil, err_any, stack_tbl]= thread_join( lane_ud [, wait_secs=-1] )
1394//
1395// timeout: returns nil
1396// done: returns return values (0..N)
1397// error: returns nil + error value [+ stack table]
1398// cancelled: returns nil
1399//
1400LUAG_FUNC(thread_join)
1401{
1402 Lane* const lane{ lua_toLane(L, 1) };
1403 lua_Duration const duration{ luaL_optnumber(L, 2, -1.0) };
1404 lua_State* const L2{ lane->L };
1405
1406 bool const done{ !lane->m_thread.joinable() || lane->waitForCompletion(duration) };
1407 if (!done || !L2)
1408 {
1409 STACK_GROW(L, 2);
1410 lua_pushnil(L);
1411 lua_pushliteral(L, "timeout");
1412 return 2;
1413 }
1414
1415 STACK_CHECK_START_REL(L, 0);
1416 // Thread is Done/Error/Cancelled; all ours now
1417
1418 int ret{ 0 };
1419 Universe* const U{ lane->U };
1420 // debug_name is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed
1421 // so store it in the userdata uservalue at a key that can't possibly collide
1422 securize_debug_threadname(L, lane);
1423 switch (lane->m_status)
1424 {
1425 case Lane::Done:
1426 {
1427 int const n{ lua_gettop(L2) }; // whole L2 stack
1428 if ((n > 0) && (luaG_inter_move(U, Source{ L2 }, Dest{ L }, n, LookupMode::LaneBody) != InterCopyResult::Success))
1429 {
1430 luaL_error(L, "tried to copy unsupported types"); // doesn't return
1431 }
1432 ret = n;
1433 }
1434 break;
1435
1436 case Lane::Error:
1437 {
1438 int const n{ lua_gettop(L2) };
1439 STACK_GROW(L, 3);
1440 lua_pushnil(L);
1441 // 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 ...
1442 if (luaG_inter_move(U, Source{ L2 }, Dest{ L }, n, LookupMode::LaneBody) != InterCopyResult::Success) // nil "err" [trace]
1443 {
1444 luaL_error(L, "tried to copy unsupported types: %s", lua_tostring(L, -n)); // doesn't return
1445 }
1446 ret = 1 + n;
1447 }
1448 break;
1449
1450 case Lane::Cancelled:
1451 ret = 0;
1452 break;
1453
1454 default:
1455 DEBUGSPEW_CODE(fprintf(stderr, "Status: %d\n", lane->m_status));
1456 ASSERT_L(false);
1457 ret = 0;
1458 }
1459 lua_close(L2);
1460 lane->L = nullptr;
1461 STACK_CHECK(L, ret);
1462 return ret;
1463}
1464
1465
1466//---
1467// thread_index( ud, key) -> value
1468//
1469// If key is found in the environment, return it
1470// If key is numeric, wait until the thread returns and populate the environment with the return values
1471// If the return values signal an error, propagate it
1472// If key is "status" return the thread status
1473// Else raise an error
1474LUAG_FUNC(thread_index)
1475{
1476 static constexpr int UD{ 1 };
1477 static constexpr int KEY{ 2 };
1478 static constexpr int USR{ 3 };
1479 Lane* const lane{ lua_toLane(L, UD) };
1480 ASSERT_L(lua_gettop(L) == 2);
1481
1482 STACK_GROW(L, 8); // up to 8 positions are needed in case of error propagation
1483
1484 // If key is numeric, wait until the thread returns and populate the environment with the return values
1485 if (lua_type(L, KEY) == LUA_TNUMBER)
1486 {
1487 // first, check that we don't already have an environment that holds the requested value
1488 {
1489 // If key is found in the uservalue, return it
1490 lua_getiuservalue(L, UD, 1);
1491 lua_pushvalue(L, KEY);
1492 lua_rawget(L, USR);
1493 if (!lua_isnil(L, -1))
1494 {
1495 return 1;
1496 }
1497 lua_pop(L, 1);
1498 }
1499 {
1500 // check if we already fetched the values from the thread or not
1501 lua_Integer key = lua_tointeger(L, KEY);
1502 lua_pushinteger(L, 0);
1503 lua_rawget(L, USR);
1504 bool const fetched{ !lua_isnil(L, -1) };
1505 lua_pop(L, 1); // back to our 2 args + uservalue on the stack
1506 if (!fetched)
1507 {
1508 lua_pushinteger(L, 0);
1509 lua_pushboolean(L, 1);
1510 lua_rawset(L, USR);
1511 // wait until thread has completed
1512 lua_pushcfunction(L, LG_thread_join);
1513 lua_pushvalue(L, UD);
1514 lua_call(L, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+
1515 switch (lane->m_status)
1516 {
1517 default:
1518 // this is an internal error, we probably never get here
1519 lua_settop(L, 0);
1520 lua_pushliteral(L, "Unexpected status: ");
1521 lua_pushstring(L, thread_status_string(lane));
1522 lua_concat(L, 2);
1523 raise_lua_error(L);
1524 [[fallthrough]]; // fall through if we are killed, as we got nil, "killed" on the stack
1525
1526 case Lane::Done: // got regular return values
1527 {
1528 int const nvalues{ lua_gettop(L) - 3 };
1529 for (int i = nvalues; i > 0; --i)
1530 {
1531 // pop the last element of the stack, to store it in the uservalue at its proper index
1532 lua_rawseti(L, USR, i);
1533 }
1534 }
1535 break;
1536
1537 case Lane::Error: // got 3 values: nil, errstring, callstack table
1538 // me[-2] could carry the stack table, but even
1539 // me[-1] is rather unnecessary (and undocumented);
1540 // use ':join()' instead. --AKa 22-Jan-2009
1541 ASSERT_L(lua_isnil(L, 4) && !lua_isnil(L, 5) && lua_istable(L, 6));
1542 // store errstring at key -1
1543 lua_pushnumber(L, -1);
1544 lua_pushvalue(L, 5);
1545 lua_rawset(L, USR);
1546 break;
1547
1548 case Lane::Cancelled:
1549 // do nothing
1550 break;
1551 }
1552 }
1553 lua_settop(L, 3); // UD KEY ENV
1554 if (key != -1)
1555 {
1556 lua_pushnumber(L, -1); // UD KEY ENV -1
1557 lua_rawget(L, USR); // UD KEY ENV "error"
1558 if (!lua_isnil(L, -1)) // an error was stored
1559 {
1560 // Note: Lua 5.1 interpreter is not prepared to show
1561 // non-string errors, so we use 'tostring()' here
1562 // to get meaningful output. --AKa 22-Jan-2009
1563 //
1564 // Also, the stack dump we get is no good; it only
1565 // lists our internal Lanes functions. There seems
1566 // to be no way to switch it off, though.
1567 //
1568 // Level 3 should show the line where 'h[x]' was read
1569 // but this only seems to work for string messages
1570 // (Lua 5.1.4). No idea, why. --AKa 22-Jan-2009
1571 lua_getmetatable(L, UD); // UD KEY ENV "error" mt
1572 lua_getfield(L, -1, "cached_error"); // UD KEY ENV "error" mt error()
1573 lua_getfield(L, -2, "cached_tostring"); // UD KEY ENV "error" mt error() tostring()
1574 lua_pushvalue(L, 4); // UD KEY ENV "error" mt error() tostring() "error"
1575 lua_call(L, 1, 1); // tostring( errstring) -- just in case // UD KEY ENV "error" mt error() "error"
1576 lua_pushinteger(L, 3); // UD KEY ENV "error" mt error() "error" 3
1577 lua_call(L, 2, 0); // error( tostring( errstring), 3) // UD KEY ENV "error" mt
1578 }
1579 else
1580 {
1581 lua_pop(L, 1); // back to our 3 arguments on the stack
1582 }
1583 }
1584 lua_rawgeti(L, USR, (int)key);
1585 }
1586 return 1;
1587 }
1588 if (lua_type(L, KEY) == LUA_TSTRING)
1589 {
1590 char const* const keystr{ lua_tostring(L, KEY) };
1591 lua_settop(L, 2); // keep only our original arguments on the stack
1592 if (strcmp( keystr, "status") == 0)
1593 {
1594 push_thread_status(L, lane); // push the string representing the status
1595 return 1;
1596 }
1597 // return UD.metatable[key]
1598 lua_getmetatable(L, UD); // UD KEY mt
1599 lua_replace(L, -3); // mt KEY
1600 lua_rawget(L, -2); // mt value
1601 // only "cancel" and "join" are registered as functions, any other string will raise an error
1602 if (lua_iscfunction(L, -1))
1603 {
1604 return 1;
1605 }
1606 return luaL_error(L, "can't index a lane with '%s'", keystr);
1607 }
1608 // unknown key
1609 lua_getmetatable(L, UD);
1610 lua_getfield(L, -1, "cached_error");
1611 lua_pushliteral(L, "Unknown key: ");
1612 lua_pushvalue(L, KEY);
1613 lua_concat(L, 2);
1614 lua_call(L, 1, 0); // error( "Unknown key: " .. key) -> doesn't return
1615 return 0;
1616}
1617
1618#if HAVE_LANE_TRACKING()
1619//---
1620// threads() -> {}|nil
1621//
1622// Return a list of all known lanes
1623LUAG_FUNC(threads)
1624{
1625 int const top{ lua_gettop(L) };
1626 Universe* const U{ universe_get(L) };
1627
1628 // List _all_ still running threads
1629 //
1630 std::lock_guard<std::mutex> guard{ U->tracking_cs };
1631 if (U->tracking_first && U->tracking_first != TRACKING_END)
1632 {
1633 Lane* lane{ U->tracking_first };
1634 int index = 0;
1635 lua_newtable(L); // {}
1636 while (lane != TRACKING_END)
1637 {
1638 // insert a { name, status } tuple, so that several lanes with the same name can't clobber each other
1639 lua_newtable(L); // {} {}
1640 lua_pushstring(L, lane->debug_name); // {} {} "name"
1641 lua_setfield(L, -2, "name"); // {} {}
1642 push_thread_status(L, lane); // {} {} "status"
1643 lua_setfield(L, -2, "status"); // {} {}
1644 lua_rawseti(L, -2, ++index); // {}
1645 lane = lane->tracking_next;
1646 }
1647 }
1648 return lua_gettop(L) - top; // 0 or 1
1649}
1650#endif // HAVE_LANE_TRACKING()
1651
1652/*
1653 * ###############################################################################################
1654 * ######################################## Timer support ########################################
1655 * ###############################################################################################
1656 */
1657
1658/*
1659* secs = now_secs()
1660*
1661* Returns the current time, as seconds. Resolution depends on std::system_clock implementation
1662* Can't use std::chrono::steady_clock because we need the same baseline as std::mktime
1663*/
1664LUAG_FUNC(now_secs)
1665{
1666 auto const now{ std::chrono::system_clock::now() };
1667 lua_Duration duration { now.time_since_epoch() };
1668
1669 lua_pushnumber(L, duration.count());
1670 return 1;
1671}
1672
1673// #################################################################################################
1674
1675/*
1676* wakeup_at_secs= wakeup_conv(date_tbl)
1677*/
1678LUAG_FUNC(wakeup_conv)
1679{
1680 // date_tbl
1681 // .year (four digits)
1682 // .month (1..12)
1683 // .day (1..31)
1684 // .hour (0..23)
1685 // .min (0..59)
1686 // .sec (0..61)
1687 // .yday (day of the year)
1688 // .isdst (daylight saving on/off)
1689
1690 STACK_CHECK_START_REL(L, 0);
1691 auto readInteger = [L](char const* name_)
1692 {
1693 lua_getfield(L, 1, name_);
1694 lua_Integer const val{ lua_tointeger(L, -1) };
1695 lua_pop(L, 1);
1696 return static_cast<int>(val);
1697 };
1698 int const year{ readInteger("year") };
1699 int const month{ readInteger("month") };
1700 int const day{ readInteger("day") };
1701 int const hour{ readInteger("hour") };
1702 int const min{ readInteger("min") };
1703 int const sec{ readInteger("sec") };
1704 STACK_CHECK(L, 0);
1705
1706 // If Lua table has '.isdst' we trust that. If it does not, we'll let
1707 // 'mktime' decide on whether the time is within DST or not (value -1).
1708 //
1709 lua_getfield(L, 1, "isdst" );
1710 int const isdst{ lua_isboolean(L, -1) ? lua_toboolean(L, -1) : -1 };
1711 lua_pop(L,1);
1712 STACK_CHECK(L, 0);
1713
1714 std::tm t{};
1715 t.tm_year = year - 1900;
1716 t.tm_mon= month-1; // 0..11
1717 t.tm_mday= day; // 1..31
1718 t.tm_hour= hour; // 0..23
1719 t.tm_min= min; // 0..59
1720 t.tm_sec= sec; // 0..60
1721 t.tm_isdst= isdst; // 0/1/negative
1722
1723 lua_pushnumber(L, static_cast<lua_Number>(std::mktime(&t))); // resolution: 1 second
1724 return 1;
1725}
1726
1727/*
1728 * ###############################################################################################
1729 * ######################################## Module linkage #######################################
1730 * ###############################################################################################
1731 */
1732
1733extern int LG_linda(lua_State* L);
1734static struct luaL_Reg const lanes_functions[] =
1735{
1736 { "linda", LG_linda },
1737 { "now_secs", LG_now_secs },
1738 { "wakeup_conv", LG_wakeup_conv },
1739 { "set_thread_priority", LG_set_thread_priority },
1740 { "set_thread_affinity", LG_set_thread_affinity },
1741 { "nameof", luaG_nameof },
1742 { "register", LG_register },
1743 { "set_singlethreaded", LG_set_singlethreaded },
1744 { nullptr, nullptr }
1745};
1746
1747// #################################################################################################
1748
1749// upvalue 1: module name
1750// upvalue 2: module table
1751// param 1: settings table
1752LUAG_FUNC(configure)
1753{
1754 // start with one-time initializations.
1755 {
1756 // C++ guarantees that the static variable initialization is threadsafe.
1757 static auto _ = std::invoke(
1758 []()
1759 {
1760#if (defined PLATFORM_OSX) && (defined _UTILBINDTHREADTOCPU)
1761 chudInitialize();
1762#endif
1763 return false;
1764 }
1765 );
1766 }
1767
1768 Universe* U = universe_get(L);
1769 bool const from_master_state{ U == nullptr };
1770 char const* name = luaL_checkstring(L, lua_upvalueindex(1));
1771 ASSERT_L(lua_type(L, 1) == LUA_TTABLE);
1772
1773 STACK_GROW(L, 4);
1774 STACK_CHECK_START_ABS(L, 1); // settings
1775
1776 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L));
1777 DEBUGSPEW_CODE(if (U) U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
1778
1779 if (U == nullptr)
1780 {
1781 U = universe_create(L); // settings universe
1782 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
1783 lua_newtable( L); // settings universe mt
1784 lua_getfield(L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout
1785 lua_getfield(L, 1, "shutdown_mode"); // settings universe mt shutdown_timeout shutdown_mode
1786 lua_pushcclosure(L, universe_gc, 2); // settings universe mt universe_gc
1787 lua_setfield(L, -2, "__gc"); // settings universe mt
1788 lua_setmetatable(L, -2); // settings universe
1789 lua_pop(L, 1); // settings
1790 lua_getfield(L, 1, "verbose_errors"); // settings verbose_errors
1791 U->verboseErrors = lua_toboolean(L, -1) ? true : false;
1792 lua_pop(L, 1); // settings
1793 lua_getfield(L, 1, "demote_full_userdata"); // settings demote_full_userdata
1794 U->demoteFullUserdata = lua_toboolean(L, -1) ? true : false;
1795 lua_pop(L, 1); // settings
1796#if HAVE_LANE_TRACKING()
1797 lua_getfield(L, 1, "track_lanes"); // settings track_lanes
1798 U->tracking_first = lua_toboolean(L, -1) ? TRACKING_END : nullptr;
1799 lua_pop(L, 1); // settings
1800#endif // HAVE_LANE_TRACKING()
1801 // Linked chains handling
1802 U->selfdestruct_first = SELFDESTRUCT_END;
1803 initialize_allocator_function( U, L);
1804 initialize_on_state_create( U, L);
1805 init_keepers( U, L);
1806 STACK_CHECK(L, 1);
1807
1808 // Initialize 'timer_deep'; a common Linda object shared by all states
1809 lua_pushcfunction(L, LG_linda); // settings lanes.linda
1810 lua_pushliteral(L, "lanes-timer"); // settings lanes.linda "lanes-timer"
1811 lua_call(L, 1, 1); // settings linda
1812 STACK_CHECK(L, 2);
1813
1814 // Proxy userdata contents is only a 'DeepPrelude*' pointer
1815 U->timer_deep = *lua_tofulluserdata<DeepPrelude*>(L, -1);
1816 // increment refcount so that this linda remains alive as long as the universe exists.
1817 U->timer_deep->m_refcount.fetch_add(1, std::memory_order_relaxed);
1818 lua_pop(L, 1); // settings
1819 }
1820 STACK_CHECK(L, 1);
1821
1822 // Serialize calls to 'require' from now on, also in the primary state
1823 serialize_require( DEBUGSPEW_PARAM_COMMA( U) L);
1824
1825 // Retrieve main module interface table
1826 lua_pushvalue(L, lua_upvalueindex( 2)); // settings M
1827 // remove configure() (this function) from the module interface
1828 lua_pushnil( L); // settings M nil
1829 lua_setfield(L, -2, "configure"); // settings M
1830 // add functions to the module's table
1831 luaG_registerlibfuncs(L, lanes_functions);
1832#if HAVE_LANE_TRACKING()
1833 // register core.threads() only if settings say it should be available
1834 if (U->tracking_first != nullptr)
1835 {
1836 lua_pushcfunction(L, LG_threads); // settings M LG_threads()
1837 lua_setfield(L, -2, "threads"); // settings M
1838 }
1839#endif // HAVE_LANE_TRACKING()
1840 STACK_CHECK(L, 2);
1841
1842 {
1843 char const* errmsg{ push_deep_proxy(Dest{ L }, U->timer_deep, 0, LookupMode::LaneBody) }; // settings M timer_deep
1844 if (errmsg != nullptr)
1845 {
1846 return luaL_error(L, errmsg);
1847 }
1848 lua_setfield(L, -2, "timer_gateway"); // settings M
1849 }
1850 STACK_CHECK(L, 2);
1851
1852 // prepare the metatable for threads
1853 // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname }
1854 //
1855 if (luaL_newmetatable(L, "Lane")) // settings M mt
1856 {
1857 lua_pushcfunction(L, lane_gc); // settings M mt lane_gc
1858 lua_setfield(L, -2, "__gc"); // settings M mt
1859 lua_pushcfunction(L, LG_thread_index); // settings M mt LG_thread_index
1860 lua_setfield(L, -2, "__index"); // settings M mt
1861 lua_getglobal(L, "error"); // settings M mt error
1862 ASSERT_L( lua_isfunction(L, -1));
1863 lua_setfield(L, -2, "cached_error"); // settings M mt
1864 lua_getglobal(L, "tostring"); // settings M mt tostring
1865 ASSERT_L( lua_isfunction(L, -1));
1866 lua_setfield(L, -2, "cached_tostring"); // settings M mt
1867 lua_pushcfunction(L, LG_thread_join); // settings M mt LG_thread_join
1868 lua_setfield(L, -2, "join"); // settings M mt
1869 lua_pushcfunction(L, LG_get_debug_threadname); // settings M mt LG_get_debug_threadname
1870 lua_setfield(L, -2, "get_debug_threadname"); // settings M mt
1871 lua_pushcfunction(L, LG_thread_cancel); // settings M mt LG_thread_cancel
1872 lua_setfield(L, -2, "cancel"); // settings M mt
1873 lua_pushliteral(L, "Lane"); // settings M mt "Lane"
1874 lua_setfield(L, -2, "__metatable"); // settings M mt
1875 }
1876
1877 lua_pushcclosure(L, LG_lane_new, 1); // settings M lane_new
1878 lua_setfield(L, -2, "lane_new"); // settings M
1879
1880 // we can't register 'lanes.require' normally because we want to create an upvalued closure
1881 lua_getglobal(L, "require"); // settings M require
1882 lua_pushcclosure(L, LG_require, 1); // settings M lanes.require
1883 lua_setfield(L, -2, "require"); // settings M
1884
1885 lua_pushfstring(
1886 L, "%d.%d.%d"
1887 , LANES_VERSION_MAJOR, LANES_VERSION_MINOR, LANES_VERSION_PATCH
1888 ); // settings M VERSION
1889 lua_setfield(L, -2, "version"); // settings M
1890
1891 lua_pushinteger(L, THREAD_PRIO_MAX); // settings M THREAD_PRIO_MAX
1892 lua_setfield(L, -2, "max_prio"); // settings M
1893
1894 CANCEL_ERROR.pushKey(L); // settings M CANCEL_ERROR
1895 lua_setfield(L, -2, "cancel_error"); // settings M
1896
1897 STACK_CHECK(L, 2); // reference stack contains only the function argument 'settings'
1898 // we'll need this every time we transfer some C function from/to this state
1899 LOOKUP_REGKEY.setValue(L, [](lua_State* L) { lua_newtable(L); }); // settings M
1900 STACK_CHECK(L, 2);
1901
1902 // register all native functions found in that module in the transferable functions database
1903 // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names)
1904 // for example in package.loaded["lanes.core"].*
1905 populate_func_lookup_table(L, -1, name);
1906 STACK_CHECK(L, 2);
1907
1908 // record all existing C/JIT-fast functions
1909 // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack
1910 if (from_master_state)
1911 {
1912 // don't do this when called during the initialization of a new lane,
1913 // because we will do it after on_state_create() is called,
1914 // and we don't want to skip _G because of caching in case globals are created then
1915 lua_pushglobaltable( L); // settings M _G
1916 populate_func_lookup_table(L, -1, nullptr);
1917 lua_pop(L, 1); // settings M
1918 }
1919 lua_pop(L, 1); // settings
1920
1921 // set _R[CONFIG_REGKEY] = settings
1922 CONFIG_REGKEY.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); });
1923 STACK_CHECK(L, 1);
1924 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END, L));
1925 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1926 // Return the settings table
1927 return 1;
1928}
1929
1930// #################################################################################################
1931
1932#if defined PLATFORM_WIN32 && !defined NDEBUG
1933#include <signal.h>
1934#include <conio.h>
1935
1936void signal_handler(int signal)
1937{
1938 if (signal == SIGABRT)
1939 {
1940 _cprintf("caught abnormal termination!");
1941 abort();
1942 }
1943}
1944
1945// helper to have correct callstacks when crashing a Win32 running on 64 bits Windows
1946// don't forget to toggle Debug/Exceptions/Win32 in visual Studio too!
1947static volatile long s_ecoc_initCount = 0;
1948static volatile int s_ecoc_go_ahead = 0;
1949static void EnableCrashingOnCrashes(void)
1950{
1951 if (InterlockedCompareExchange(&s_ecoc_initCount, 1, 0) == 0)
1952 {
1953 typedef BOOL(WINAPI * tGetPolicy)(LPDWORD lpFlags);
1954 typedef BOOL(WINAPI * tSetPolicy)(DWORD dwFlags);
1955 const DWORD EXCEPTION_SWALLOWING = 0x1;
1956
1957 HMODULE kernel32 = LoadLibraryA("kernel32.dll");
1958 if (kernel32)
1959 {
1960 tGetPolicy pGetPolicy = (tGetPolicy) GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
1961 tSetPolicy pSetPolicy = (tSetPolicy) GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
1962 if (pGetPolicy && pSetPolicy)
1963 {
1964 DWORD dwFlags;
1965 if (pGetPolicy(&dwFlags))
1966 {
1967 // Turn off the filter
1968 pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
1969 }
1970 }
1971 FreeLibrary(kernel32);
1972 }
1973 // typedef void (* SignalHandlerPointer)( int);
1974 /*SignalHandlerPointer previousHandler =*/signal(SIGABRT, signal_handler);
1975
1976 s_ecoc_go_ahead = 1; // let others pass
1977 }
1978 else
1979 {
1980 while (!s_ecoc_go_ahead)
1981 {
1982 Sleep(1);
1983 } // changes threads
1984 }
1985}
1986#endif // PLATFORM_WIN32 && !defined NDEBUG
1987
1988LANES_API int luaopen_lanes_core( lua_State* L)
1989{
1990#if defined PLATFORM_WIN32 && !defined NDEBUG
1991 EnableCrashingOnCrashes();
1992#endif // defined PLATFORM_WIN32 && !defined NDEBUG
1993
1994 STACK_GROW(L, 4);
1995 STACK_CHECK_START_REL(L, 0);
1996
1997 // Prevent PUC-Lua/LuaJIT mismatch. Hopefully this works for MoonJIT too
1998 lua_getglobal(L, "jit"); // {jit?}
1999#if LUAJIT_FLAVOR() == 0
2000 if (!lua_isnil(L, -1))
2001 return luaL_error(L, "Lanes is built for PUC-Lua, don't run from LuaJIT");
2002#else
2003 if (lua_isnil(L, -1))
2004 return luaL_error(L, "Lanes is built for LuaJIT, don't run from PUC-Lua");
2005#endif
2006 lua_pop(L, 1); //
2007 STACK_CHECK(L, 0);
2008
2009 // Create main module interface table
2010 // we only have 1 closure, which must be called to configure Lanes
2011 lua_newtable(L); // M
2012 lua_pushvalue(L, 1); // M "lanes.core"
2013 lua_pushvalue(L, -2); // M "lanes.core" M
2014 lua_pushcclosure(L, LG_configure, 2); // M LG_configure()
2015 CONFIG_REGKEY.pushValue(L); // M LG_configure() settings
2016 if (!lua_isnil(L, -1)) // this is not the first require "lanes.core": call configure() immediately
2017 {
2018 lua_pushvalue(L, -1); // M LG_configure() settings settings
2019 lua_setfield(L, -4, "settings"); // M LG_configure() settings
2020 lua_call(L, 1, 0); // M
2021 }
2022 else
2023 {
2024 // will do nothing on first invocation, as we haven't stored settings in the registry yet
2025 lua_setfield(L, -3, "settings"); // M LG_configure()
2026 lua_setfield(L, -2, "configure"); // M
2027 }
2028
2029 STACK_CHECK(L, 1);
2030 return 1;
2031}
2032
2033[[nodiscard]] static int default_luaopen_lanes(lua_State* L)
2034{
2035 int const rc{ luaL_loadfile(L, "lanes.lua") || lua_pcall(L, 0, 1, 0) };
2036 if (rc != LUA_OK)
2037 {
2038 return luaL_error(L, "failed to initialize embedded Lanes");
2039 }
2040 return 1;
2041}
2042
2043// call this instead of luaopen_lanes_core() when embedding Lua and Lanes in a custom application
2044LANES_API void luaopen_lanes_embedded( lua_State* L, lua_CFunction _luaopen_lanes)
2045{
2046 STACK_CHECK_START_REL(L, 0);
2047 // pre-require lanes.core so that when lanes.lua calls require "lanes.core" it finds it is already loaded
2048 luaL_requiref(L, "lanes.core", luaopen_lanes_core, 0); // ... lanes.core
2049 lua_pop(L, 1); // ...
2050 STACK_CHECK(L, 0);
2051 // call user-provided function that runs the chunk "lanes.lua" from wherever they stored it
2052 luaL_requiref(L, "lanes", _luaopen_lanes ? _luaopen_lanes : default_luaopen_lanes, 0); // ... lanes
2053 STACK_CHECK(L, 1);
2054}