diff options
Diffstat (limited to 'src/lanes.cpp')
-rw-r--r-- | src/lanes.cpp | 1037 |
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 | |||
174 | bool Lane::waitForCompletion(lua_Duration duration_) | 172 | bool 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 | |||
188 | static void lane_main(Lane* lane); | 187 | static void lane_main(Lane* lane); |
189 | void Lane::startThread(int priority_) | 188 | void 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 |
234 | static constexpr RegistryUniqueKey kFinalizerRegKey{ 0xFE936BFAA718FEEAull }; | 236 | static 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. |
258 | static void push_finalizers_table(lua_State* L_) | 258 | static 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) | |||
300 | static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) | 298 | static 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 | // |
594 | LUAG_FUNC( set_singlethreaded) | 563 | LUAG_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 |
636 | static constexpr RegistryUniqueKey kExtendedStackTraceRegKey{ 0x38147AD48FB426E2ull }; // used as registry key | 604 | static constexpr RegistryUniqueKey kExtendedStackTraceRegKey{ 0x38147AD48FB426E2ull }; // used as registry key |
637 | 605 | ||
638 | LUAG_FUNC( set_error_reporting) | 606 | LUAG_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) | |||
779 | LUAG_FUNC(set_thread_affinity) | 740 | LUAG_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 | ||
799 | static struct errcode_name s_errcodes[] = | 761 | static 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 | }; |
809 | static char const* get_errcode_name( int _code) | 770 | static 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 | |||
822 | static void lane_main(Lane* lane_) | 783 | static 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 |
920 | LUAG_FUNC(require) | 877 | LUAG_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 | // |
976 | LUAG_FUNC(lane_new) | 933 | LUAG_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 | */ |
1640 | LUAG_FUNC(now_secs) | 1541 | LUAG_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 | */ | ||
1654 | LUAG_FUNC(wakeup_conv) | 1553 | LUAG_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 | ||
1707 | extern int LG_linda(lua_State* L_); | 1605 | extern int LG_linda(lua_State* L_); |
1708 | static struct luaL_Reg const lanes_functions[] = | 1606 | static 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 | ||
1912 | void signal_handler(int signal) | 1807 | void 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! |
1923 | static volatile long s_ecoc_initCount = 0; | 1819 | static volatile long s_ecoc_initCount = 0; |
1924 | static volatile int s_ecoc_go_ahead = 0; | 1820 | static volatile int s_ecoc_go_ahead = 0; |
1925 | static void EnableCrashingOnCrashes(void) | 1821 | static 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 | ||
1964 | LANES_API int luaopen_lanes_core( lua_State* L_) | 1853 | // ################################################################################################# |
1854 | |||
1855 | LANES_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 |
2020 | LANES_API void luaopen_lanes_embedded( lua_State* L_, lua_CFunction _luaopen_lanes) | 1911 | LANES_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 | } |