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