aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-05-17 15:34:44 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-05-17 15:34:44 +0200
commit823d3d570236e32bdbe270306df5602ff586c4a7 (patch)
tree88e60e92f4c5bf618588ec0b64f295540390ae8f /src
parentd359e8cf7d71c2f80a1706fef38e603478ac003f (diff)
downloadlanes-823d3d570236e32bdbe270306df5602ff586c4a7.tar.gz
lanes-823d3d570236e32bdbe270306df5602ff586c4a7.tar.bz2
lanes-823d3d570236e32bdbe270306df5602ff586c4a7.zip
Error reporting revamp
* set_error_reporting() is gone * new lane generator setting error_reporting_level * basic/extended stack trace is selectable at runtime instead of compile-time
Diffstat (limited to 'src')
-rw-r--r--src/lane.cpp428
-rw-r--r--src/lane.h21
-rw-r--r--src/lanes.cpp42
-rw-r--r--src/lanes.lua21
4 files changed, 292 insertions, 220 deletions
diff --git a/src/lane.cpp b/src/lane.cpp
index f320071..e321a52 100644
--- a/src/lane.cpp
+++ b/src/lane.cpp
@@ -28,16 +28,15 @@ THE SOFTWARE.
28 28
29#include "debugspew.h" 29#include "debugspew.h"
30#include "intercopycontext.h" 30#include "intercopycontext.h"
31#include "tools.h"
32#include "threading.h" 31#include "threading.h"
32#include "tools.h"
33 33
34// ################################################################################################# 34// #################################################################################################
35 35
36/* Do you want full call stacks, or just the line where the error happened? 36// xxh64 of string "error" generated at https://www.pelock.com/products/hash-calculator
37 * 37static constexpr UniqueKey kCachedError{ 0xD6F35DD608D0A203ull };
38 * TBD: The full stack feature does not seem to work (try 'make error'). 38// xxh64 of string "tostring" generated at https://www.pelock.com/products/hash-calculator
39 */ 39static constexpr UniqueKey kCachedTostring{ 0xAB5EA23BCEA0C35Cull };
40#define ERROR_FULL_STACK 1 // must be either 0 or 1 as we do some index arithmetics with it!
41 40
42// ################################################################################################# 41// #################################################################################################
43// ######################################### Lua API ############################################### 42// ######################################### Lua API ###############################################
@@ -77,26 +76,6 @@ static LUAG_FUNC(set_finalizer)
77 76
78// ################################################################################################# 77// #################################################################################################
79 78
80#if ERROR_FULL_STACK
81LUAG_FUNC(set_error_reporting)
82{
83 luaL_checktype(L_, 1, LUA_TSTRING);
84 char const* _mode{ lua_tostring(L_, 1) };
85 lua_pushliteral(L_, "extended");
86 bool const _extended{ strcmp(_mode, "extended") == 0 };
87 bool const _basic{ strcmp(_mode, "basic") == 0 };
88 if (!_extended && !_basic) {
89 raise_luaL_error(L_, "unsupported error reporting model %s", _mode);
90 }
91
92 kExtendedStackTraceRegKey.setValue(L_, [extended = _extended](lua_State* L_) { lua_pushboolean(L_, extended ? 1 : 0); });
93 return 0;
94}
95
96#endif // ERROR_FULL_STACK
97
98// #################################################################################################
99
100// upvalue #1 is the lane userdata 79// upvalue #1 is the lane userdata
101static LUAG_FUNC(set_debug_threadname) 80static LUAG_FUNC(set_debug_threadname)
102{ 81{
@@ -172,7 +151,7 @@ static LUAG_FUNC(thread_join)
172 int const _n{ lua_gettop(_L2) }; // L_: lane L2: "err" [trace] 151 int const _n{ lua_gettop(_L2) }; // L_: lane L2: "err" [trace]
173 STACK_GROW(L_, 3); 152 STACK_GROW(L_, 3);
174 lua_pushnil(L_); // L_: lane nil 153 lua_pushnil(L_); // L_: lane nil
175 // 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 ... 154 // even when _lane->errorTraceLevel != Minimal, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ...
176 InterCopyContext _c{ _lane->U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} }; 155 InterCopyContext _c{ _lane->U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} };
177 if (_c.inter_move(_n) != InterCopyResult::Success) { // L_: lane nil "err" [trace] L2: 156 if (_c.inter_move(_n) != InterCopyResult::Success) { // L_: lane nil "err" [trace] L2:
178 raise_luaL_error(L_, "tried to copy unsupported types: %s", lua_tostring(L_, -_n)); 157 raise_luaL_error(L_, "tried to copy unsupported types: %s", lua_tostring(L_, -_n));
@@ -197,143 +176,177 @@ static LUAG_FUNC(thread_join)
197 176
198// ################################################################################################# 177// #################################################################################################
199 178
200// lane:__index(key,usr) -> value 179// key is numeric, wait until the thread returns and populate the environment with the return values
201//
202// If key is found in the environment, return it
203// If key is numeric, wait until the thread returns and populate the environment with the return values
204// If the return values signal an error, propagate it 180// If the return values signal an error, propagate it
205// If key is "status" return the thread status 181// Else If key is found in the environment, return it
206// Else raise an error 182static int thread_index_number(lua_State* L_)
207LUAG_FUNC(thread_index)
208{ 183{
209 static constexpr int kSelf{ 1 }; 184 static constexpr int kSelf{ 1 };
210 static constexpr int kKey{ 2 }; 185 static constexpr int kKey{ 2 };
186 static constexpr int kUsr{ 3 };
187
211 Lane* const _lane{ ToLane(L_, kSelf) }; 188 Lane* const _lane{ ToLane(L_, kSelf) };
212 LUA_ASSERT(L_, lua_gettop(L_) == 2); 189 LUA_ASSERT(L_, lua_gettop(L_) == 2); // L_: lane n
213 190
214 STACK_GROW(L_, 8); // up to 8 positions are needed in case of error propagation 191 // first, check that we don't already have an environment that holds the requested value
192 // If key is found in the uservalue, return it
193 lua_getiuservalue(L_, kSelf, 1); // L_: lane n {uv}
194 lua_pushvalue(L_, kKey); // L_: lane n {uv} n
195 lua_rawget(L_, kUsr); // L_: lane n {uv} v|nil
196 if (!lua_isnil(L_, -1)) {
197 return 1;
198 }
199 lua_pop(L_, 1); // L_: lane n {uv}
200
201 // check if we already fetched the values from the thread or not
202 lua_pushinteger(L_, 0); // L_: lane n {uv} 0
203 lua_rawget(L_, kUsr); // L_: lane n {uv} uv[0]|nil
204 bool const _fetched{ !lua_isnil(L_, -1) };
205 lua_pop(L_, 1); // L_: lane n {uv}
206 if (!_fetched) {
207 lua_pushinteger(L_, 0); // L_: lane n {uv} 0
208 lua_pushboolean(L_, 1); // L_: lane n {uv} 0 true
209 lua_rawset(L_, kUsr); // L_: lane n {uv}
210 // wait until thread has completed, transfer everything from the lane's stack to our side
211 lua_pushcfunction(L_, LG_thread_join); // L_: lane n {uv} join
212 lua_pushvalue(L_, kSelf); // L_: lane n {uv} join lane
213 lua_call(L_, 1, LUA_MULTRET); // lane:join() // L_: lane n {uv} ...
214 switch (_lane->status) {
215 default:
216 // this is an internal error, we probably never get here
217 lua_settop(L_, 0); // L_:
218 lua_pushliteral(L_, "Unexpected status: "); // L_: "Unexpected status: "
219 _lane->pushThreadStatus(L_); // L_: "Unexpected status: " "<status>"
220 lua_concat(L_, 2); // L_: "Unexpected status: <status>"
221 raise_lua_error(L_);
222
223 case Lane::Done: // got regular return values
224 {
225 int const _nvalues{ lua_gettop(L_) - 3 }; // L_: lane n {uv} ...
226 for (int _i = _nvalues; _i > 0; --_i) {
227 // pop the last element of the stack, to store it in the uservalue at its proper index
228 lua_rawseti(L_, kUsr, _i); // L_: lane n {uv}
229 }
230 }
231 break;
215 232
216 // If key is numeric, wait until the thread returns and populate the environment with the return values 233 case Lane::Error: // got 2 or 3 values: nil, errstring, and possibly a callstack table
217 if (lua_type(L_, kKey) == LUA_TNUMBER) { 234 if (_lane->errorTraceLevel == Lane::Minimal) {
218 static constexpr int kUsr{ 3 }; 235 LUA_ASSERT(L_, lua_gettop(L_) == 5 && lua_isnil(L_, 4) && !lua_isnil(L_, 5)); // L_: lane n {uv} nil "<msg>"
219 // first, check that we don't already have an environment that holds the requested value 236 } else {
220 { 237 LUA_ASSERT(L_, lua_gettop(L_) == 6 && lua_isnil(L_, 4) && !lua_isnil(L_, 5) && lua_istable(L_, 6));
221 // If key is found in the uservalue, return it 238 lua_insert(L_, -2); // L_: lane n {uv} nil {trace} "<msg>"
222 lua_getiuservalue(L_, kSelf, 1);
223 lua_pushvalue(L_, kKey);
224 lua_rawget(L_, kUsr);
225 if (!lua_isnil(L_, -1)) {
226 return 1;
227 } 239 }
228 lua_pop(L_, 1); 240 // uv[-1] = "<msg>"
241 lua_rawseti(L_, kUsr, -1); // L_: lane n {uv} nil {trace}?
242 break;
243
244 case Lane::Cancelled:
245 // do nothing
246 break;
229 } 247 }
230 { 248 }
231 // check if we already fetched the values from the thread or not 249 STACK_GROW(L_, 6); // up to 6 positions are needed in case of error propagation
232 lua_pushinteger(L_, 0); 250 lua_settop(L_, 3); // L_: lane n {uv}
233 lua_rawget(L_, kUsr); 251 int const _key{ static_cast<int>(lua_tointeger(L_, kKey)) };
234 bool const _fetched{ !lua_isnil(L_, -1) }; 252 if (_key != -1) {
235 lua_pop(L_, 1); // back to our 2 args + uservalue on the stack 253 lua_rawgeti(L_, kUsr, -1); // L_: lane n {uv} <error>|nil
236 if (!_fetched) { 254 if (!lua_isnil(L_, -1)) { // an error was stored // L_: lane n {uv} <error>
237 lua_pushinteger(L_, 0); 255 lua_getmetatable(L_, 1); // L_: lane n {uv} <error> {mt}
238 lua_pushboolean(L_, 1); 256 lua_replace(L_, -3); // L_: lane n {mt} <error>
239 lua_rawset(L_, kUsr); 257#if LUA_VERSION_NUM == 501
240 // wait until thread has completed 258 // Note: Lua 5.1 interpreter is not prepared to show
241 lua_pushcfunction(L_, LG_thread_join); 259 // non-string errors, so we use 'tostring()' here
242 lua_pushvalue(L_, kSelf); 260 // to get meaningful output. --AKa 22-Jan-2009
243 lua_call(L_, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+ 261 //
244 switch (_lane->status) { 262 // Also, the stack dump we get is no good; it only
245 default: 263 // lists our internal Lanes functions. There seems
246 // this is an internal error, we probably never get here 264 // to be no way to switch it off, though.
247 lua_settop(L_, 0); 265 //
248 lua_pushliteral(L_, "Unexpected status: "); 266 // Level 3 should show the line where 'h[x]' was read
249 _lane->pushThreadStatus(L_); 267 // but this only seems to work for string messages
250 lua_concat(L_, 2); 268 // (Lua 5.1.4). No idea, why. --AKa 22-Jan-2009
251 raise_lua_error(L_); 269 if (!lua_isstring(L_, -1)) {
252 [[fallthrough]]; // fall through if we are killed, as we got nil, "killed" on the stack 270 kCachedTostring.pushKey(L_); // L_: lane n {mt} <error> kCachedTostring
253 271 lua_rawget(L_, -3); // L_: lane n {mt} <error> tostring()
254 case Lane::Done: // got regular return values 272 lua_insert(L_, -2); // L_: lane n {mt} tostring() <error>
255 { 273 lua_call(L_, 1, 1); // tostring(errstring) // L_: lane n {mt} "error"
256 int const _nvalues{ lua_gettop(L_) - 3 };
257 for (int _i = _nvalues; _i > 0; --_i) {
258 // pop the last element of the stack, to store it in the uservalue at its proper index
259 lua_rawseti(L_, kUsr, _i);
260 }
261 }
262 break;
263
264 case Lane::Error: // got 3 values: nil, errstring, callstack table
265 // me[-2] could carry the stack table, but even
266 // me[-1] is rather unnecessary (and undocumented);
267 // use ':join()' instead. --AKa 22-Jan-2009
268 LUA_ASSERT(L_, lua_isnil(L_, 4) && !lua_isnil(L_, 5) && lua_istable(L_, 6));
269 // store errstring at key -1
270 lua_pushnumber(L_, -1);
271 lua_pushvalue(L_, 5);
272 lua_rawset(L_, kUsr);
273 break;
274
275 case Lane::Cancelled:
276 // do nothing
277 break;
278 }
279 }
280 lua_settop(L_, 3); // L_: self KEY ENV
281 int const _key{ static_cast<int>(lua_tointeger(L_, kKey)) };
282 if (_key != -1) {
283 lua_pushnumber(L_, -1); // L_: self KEY ENV -1
284 lua_rawget(L_, kUsr); // L_: self KEY ENV "error"|nil
285 if (!lua_isnil(L_, -1)) { // L_: an error was stored
286 // Note: Lua 5.1 interpreter is not prepared to show
287 // non-string errors, so we use 'tostring()' here
288 // to get meaningful output. --AKa 22-Jan-2009
289 //
290 // Also, the stack dump we get is no good; it only
291 // lists our internal Lanes functions. There seems
292 // to be no way to switch it off, though.
293 //
294 // Level 3 should show the line where 'h[x]' was read
295 // but this only seems to work for string messages
296 // (Lua 5.1.4). No idea, why. --AKa 22-Jan-2009
297 lua_getmetatable(L_, kSelf); // L_: self KEY ENV "error" mt
298 lua_getfield(L_, -1, "cached_error"); // L_: self KEY ENV "error" mt error()
299 lua_getfield(L_, -2, "cached_tostring"); // L_: self KEY ENV "error" mt error() tostring()
300 lua_pushvalue(L_, 4); // L_: self KEY ENV "error" mt error() tostring() "error"
301 lua_call(L_, 1, 1); // tostring(errstring) -- just in case // L_: self KEY ENV "error" mt error() "error"
302 lua_pushinteger(L_, 3); // L_: self KEY ENV "error" mt error() "error" 3
303 lua_call(L_, 2, 0); // error(tostring(errstring), 3) -> doesn't return // L_: self KEY ENV "error" mt
304 } else {
305 lua_pop(L_, 1); // L_: self KEY ENV
306 }
307 } 274 }
308 lua_rawgeti(L_, kUsr, _key); 275#endif // LUA_VERSION_NUM == 501
276 kCachedError.pushKey(L_); // L_: lane n {mt} "error" kCachedError
277 lua_rawget(L_, -3); // L_: lane n {mt} "error" error()
278 lua_replace(L_, -3); // L_: lane n error() "error"
279 lua_pushinteger(L_, 3); // L_: lane n error() "error" 3
280 lua_call(L_, 2, 0); // error(tostring(errstring), 3) -> doesn't return // L_: lane n
281 raise_luaL_error(L_, "%s: should not get here!", _lane->debugName);
282 } else {
283 lua_pop(L_, 1); // L_: lane n {uv}
309 } 284 }
285 }
286 lua_rawgeti(L_, kUsr, _key); // L_: lane n {uv} uv[n]
287 return 1;
288}
289
290// #################################################################################################
291
292// If key is "status" return the thread status
293// If key is found in the environment, return it
294// Else raise an error
295static int thread_index_string(lua_State* L_)
296{
297 static constexpr int kSelf{ 1 };
298 static constexpr int kKey{ 2 };
299
300 Lane* const _lane{ ToLane(L_, kSelf) };
301 LUA_ASSERT(L_, lua_gettop(L_) == 2); // L_: lane "key"
302
303 char const* const _keystr{ lua_tostring(L_, kKey) };
304 lua_settop(L_, 2); // keep only our original arguments on the stack
305 if (strcmp(_keystr, "status") == 0) {
306 _lane->pushThreadStatus(L_); // L_: lane "key" "<status>"
310 return 1; 307 return 1;
311 } 308 }
312 if (lua_type(L_, kKey) == LUA_TSTRING) { 309 if (strcmp(_keystr, "error_trace_level") == 0) {
313 char const* const _keystr{ lua_tostring(L_, kKey) }; 310 _lane->pushErrorTraceLevel(L_); // L_: lane "key" "<level>"
314 lua_settop(L_, 2); // keep only our original arguments on the stack
315 if (strcmp(_keystr, "status") == 0) {
316 _lane->pushThreadStatus(L_); // push the string representing the status
317 return 1;
318 }
319 // return self.metatable[key]
320 lua_getmetatable(L_, kSelf); // L_: self KEY mt
321 lua_replace(L_, -3); // L_: mt KEY
322 lua_rawget(L_, -2); // L_: mt value
323 // only "cancel" and "join" are registered as functions, any other string will raise an error
324 if (!lua_iscfunction(L_, -1)) {
325 raise_luaL_error(L_, "can't index a lane with '%s'", _keystr);
326 }
327 return 1; 311 return 1;
328 } 312 }
329 // unknown key 313 // return self.metatable[key]
330 lua_getmetatable(L_, kSelf); // L_: mt 314 lua_getmetatable(L_, kSelf); // L_: lane "key" mt
331 lua_getfield(L_, -1, "cached_error"); // L_: mt error 315 lua_replace(L_, -3); // L_: mt "key"
332 lua_pushliteral(L_, "Unknown key: "); // L_: mt error "Unknown key: " 316 lua_rawget(L_, -2); // L_: mt value
333 lua_pushvalue(L_, kKey); // L_: mt error "Unknown key: " k 317 // only "cancel" and "join" are registered as functions, any other string will raise an error
334 lua_concat(L_, 2); // L_: mt error "Unknown key: <k>" 318 if (!lua_iscfunction(L_, -1)) {
335 lua_call(L_, 1, 0); // error( "Unknown key: " .. key) -> doesn't return // L_: mt 319 raise_luaL_error(L_, "can't index a lane with '%s'", _keystr);
336 return 0; 320 }
321 return 1;
322}
323
324// #################################################################################################
325
326// lane:__index(key,usr) -> value
327static LUAG_FUNC(thread_index)
328{
329 static constexpr int kSelf{ 1 };
330 static constexpr int kKey{ 2 };
331 Lane* const _lane{ ToLane(L_, kSelf) };
332 LUA_ASSERT(L_, lua_gettop(L_) == 2);
333
334 switch (lua_type(L_, kKey)) {
335 case LUA_TNUMBER:
336 return thread_index_number(L_); // stack modification is undefined, returned value is at the top
337
338 case LUA_TSTRING:
339 return thread_index_string(L_); // stack modification is undefined, returned value is at the top
340
341 default: // unknown key
342 lua_getmetatable(L_, kSelf); // L_: mt
343 lua_getfield(L_, -1, "cached_error"); // L_: mt error
344 lua_pushliteral(L_, "Unknown key: "); // L_: mt error "Unknown key: "
345 lua_pushvalue(L_, kKey); // L_: mt error "Unknown key: " k
346 lua_concat(L_, 2); // L_: mt error "Unknown key: <k>"
347 lua_call(L_, 1, 0); // error( "Unknown key: " .. key) -> doesn't return // L_: mt
348 raise_luaL_error(L_, "%s[%s]: should not get here!", _lane->debugName, lua_typename(L_, lua_type(L_, kKey)));
349 }
337} 350}
338 351
339// ################################################################################################# 352// #################################################################################################
@@ -387,7 +400,6 @@ static char const* get_errcode_name(LuaError _code)
387 * implement a Lanes-specific 'pcall' of our own that does this). TBD!!! :) 400 * implement a Lanes-specific 'pcall' of our own that does this). TBD!!! :)
388 * --AKa 22-Jan-2009 401 * --AKa 22-Jan-2009
389 */ 402 */
390#if ERROR_FULL_STACK
391 403
392// xxh64 of string "kStackTraceRegKey" generated at https://www.pelock.com/products/hash-calculator 404// xxh64 of string "kStackTraceRegKey" generated at https://www.pelock.com/products/hash-calculator
393static constexpr RegistryUniqueKey kStackTraceRegKey{ 0x3F327747CACAA904ull }; 405static constexpr RegistryUniqueKey kStackTraceRegKey{ 0x3F327747CACAA904ull };
@@ -457,13 +469,12 @@ static constexpr RegistryUniqueKey kStackTraceRegKey{ 0x3F327747CACAA904ull };
457 STACK_CHECK(L_, 1); 469 STACK_CHECK(L_, 1);
458 return 1; // the untouched error value 470 return 1; // the untouched error value
459} 471}
460#endif // ERROR_FULL_STACK
461 472
462// ################################################################################################# 473// #################################################################################################
463// ########################################## Finalizer ############################################ 474// ########################################## Finalizer ############################################
464// ################################################################################################# 475// #################################################################################################
465 476
466static void push_stack_trace(lua_State* L_, LuaError rc_, int stk_base_) 477static void push_stack_trace(lua_State* L_, Lane::ErrorTraceLevel errorTraceLevel_, LuaError rc_, int stk_base_)
467{ 478{
468 // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry 479 // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry
469 switch (rc_) { 480 switch (rc_) {
@@ -471,8 +482,7 @@ static void push_stack_trace(lua_State* L_, LuaError rc_, int stk_base_)
471 break; 482 break;
472 483
473 case LuaError::ERRRUN: // cancellation or a runtime error 484 case LuaError::ERRRUN: // cancellation or a runtime error
474#if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler 485 if (errorTraceLevel_ != Lane::Minimal) { // when not Minimal, we installed a handler
475 {
476 STACK_CHECK_START_REL(L_, 0); 486 STACK_CHECK_START_REL(L_, 0);
477 // fetch the call stack table from the registry where the handler stored it 487 // fetch the call stack table from the registry where the handler stored it
478 STACK_GROW(L_, 1); 488 STACK_GROW(L_, 1);
@@ -484,11 +494,10 @@ static void push_stack_trace(lua_State* L_, LuaError rc_, int stk_base_)
484 // For other errors, the message can be whatever was thrown, and we should have a stack trace table 494 // For other errors, the message can be whatever was thrown, and we should have a stack trace table
485 LUA_ASSERT(L_, lua_type(L_, 1 + stk_base_) == (kCancelError.equals(L_, stk_base_) ? LUA_TNIL : LUA_TTABLE)); 495 LUA_ASSERT(L_, lua_type(L_, 1 + stk_base_) == (kCancelError.equals(L_, stk_base_) ? LUA_TNIL : LUA_TTABLE));
486 // Just leaving the stack trace table on the stack is enough to get it through to the master. 496 // Just leaving the stack trace table on the stack is enough to get it through to the master.
497 } else {
498 // any kind of error can be thrown with error(), or through a lane/linda cancellation
499 LUA_ASSERT(L_, lua_gettop(L_) == stk_base_);
487 } 500 }
488#else // !ERROR_FULL_STACK
489 // any kind of error can be thrown with error(), or through a lane/linda cancellation
490 LUA_ASSERT(L_, lua_gettop(L_) == stk_base_);
491#endif // !ERROR_FULL_STACK
492 break; 501 break;
493 502
494 case LuaError::ERRMEM: // memory allocation error (handler not called) 503 case LuaError::ERRMEM: // memory allocation error (handler not called)
@@ -504,7 +513,7 @@ static void push_stack_trace(lua_State* L_, LuaError rc_, int stk_base_)
504//--- 513//---
505// Run finalizers - if any - with the given parameters 514// Run finalizers - if any - with the given parameters
506// 515//
507// If 'rc' is nonzero, error message and stack index (the latter only when ERROR_FULL_STACK == 1) are available as: 516// If 'rc' is nonzero, error message and stack index (the latter only when errorTraceLevel_ == 1) are available as:
508// [-1]: stack trace (table) 517// [-1]: stack trace (table)
509// [-2]: error message (any type) 518// [-2]: error message (any type)
510// 519//
@@ -515,7 +524,7 @@ static void push_stack_trace(lua_State* L_, LuaError rc_, int stk_base_)
515// TBD: should we add stack trace on failing finalizer, wouldn't be hard.. 524// TBD: should we add stack trace on failing finalizer, wouldn't be hard..
516// 525//
517 526
518[[nodiscard]] static LuaError run_finalizers(lua_State* L_, LuaError lua_rc_) 527[[nodiscard]] static LuaError run_finalizers(lua_State* L_, Lane::ErrorTraceLevel errorTraceLevel_, LuaError lua_rc_)
519{ 528{
520 kFinalizerRegKey.pushValue(L_); // L_: ... finalizers? 529 kFinalizerRegKey.pushValue(L_); // L_: ... finalizers?
521 if (lua_isnil(L_, -1)) { 530 if (lua_isnil(L_, -1)) {
@@ -526,7 +535,7 @@ static void push_stack_trace(lua_State* L_, LuaError rc_, int stk_base_)
526 STACK_GROW(L_, 5); 535 STACK_GROW(L_, 5);
527 536
528 int const _finalizers_index{ lua_gettop(L_) }; 537 int const _finalizers_index{ lua_gettop(L_) };
529 int const _err_handler_index{ ERROR_FULL_STACK ? (lua_pushcfunction(L_, lane_error), lua_gettop(L_)) : 0 }; 538 int const _err_handler_index{ (errorTraceLevel_ != Lane::Minimal) ? (lua_pushcfunction(L_, lane_error), lua_gettop(L_)) : 0 };
530 539
531 LuaError _rc{ LuaError::OK }; 540 LuaError _rc{ LuaError::OK };
532 for (int _n = static_cast<int>(lua_rawlen(L_, _finalizers_index)); _n > 0; --_n) { 541 for (int _n = static_cast<int>(lua_rawlen(L_, _finalizers_index)); _n > 0; --_n) {
@@ -548,7 +557,7 @@ static void push_stack_trace(lua_State* L_, LuaError rc_, int stk_base_)
548 // if no error from the main body, finalizer doesn't receive any argument, else it gets the error message and optional stack trace 557 // if no error from the main body, finalizer doesn't receive any argument, else it gets the error message and optional stack trace
549 _rc = ToLuaError(lua_pcall(L_, _args, 0, _err_handler_index)); // L_: ... finalizers lane_error err_msg2? 558 _rc = ToLuaError(lua_pcall(L_, _args, 0, _err_handler_index)); // L_: ... finalizers lane_error err_msg2?
550 if (_rc != LuaError::OK) { 559 if (_rc != LuaError::OK) {
551 push_stack_trace(L_, _rc, lua_gettop(L_)); // L_: ... finalizers lane_error err_msg2? trace 560 push_stack_trace(L_, errorTraceLevel_, _rc, lua_gettop(L_)); // L_: ... finalizers lane_error err_msg2? trace
552 // If one finalizer fails, don't run the others. Return this 561 // If one finalizer fails, don't run the others. Return this
553 // as the 'real' error, replacing what we could have had (or not) 562 // as the 'real' error, replacing what we could have had (or not)
554 // from the actual code. 563 // from the actual code.
@@ -558,8 +567,8 @@ static void push_stack_trace(lua_State* L_, LuaError rc_, int stk_base_)
558 } 567 }
559 568
560 if (_rc != LuaError::OK) { 569 if (_rc != LuaError::OK) {
561 // ERROR_FULL_STACK accounts for the presence of lane_error on the stack 570 // errorTraceLevel_ accounts for the presence of lane_error on the stack
562 int const _nb_err_slots{ lua_gettop(L_) - _finalizers_index - ERROR_FULL_STACK }; 571 int const _nb_err_slots{ lua_gettop(L_) - _finalizers_index - ((errorTraceLevel_ != Lane::Minimal) ? 1 : 0) };
563 // a finalizer generated an error, this is what we leave of the stack 572 // a finalizer generated an error, this is what we leave of the stack
564 for (int _n = _nb_err_slots; _n > 0; --_n) { 573 for (int _n = _nb_err_slots; _n > 0; --_n) {
565 lua_replace(L_, _n); 574 lua_replace(L_, _n);
@@ -651,38 +660,27 @@ static void lane_main(Lane* lane_)
651 lane_->ready.wait(); 660 lane_->ready.wait();
652 LuaError _rc{ LuaError::ERRRUN }; 661 LuaError _rc{ LuaError::ERRRUN };
653 if (lane_->status == Lane::Pending) { // nothing wrong happened during preparation, we can work 662 if (lane_->status == Lane::Pending) { // nothing wrong happened during preparation, we can work
654 // At this point, the lane function and arguments are on the stack 663 // At this point, the lane function and arguments are on the stack, possibly preceded by the error handler
655 int const _nargs{ lua_gettop(_L) - 1 }; 664 int const _errorHandlerCount{ lane_->errorTraceLevel == Lane::Minimal ? 0 : 1};
665 int const _nargs{ lua_gettop(_L) - 1 - _errorHandlerCount };
656 DEBUGSPEW_CODE(Universe* _U = universe_get(_L)); 666 DEBUGSPEW_CODE(Universe* _U = universe_get(_L));
657 lane_->status = Lane::Running; // Pending -> Running 667 lane_->status = Lane::Running; // Pending -> Running
658 668
659 PrepareLaneHelpers(lane_); 669 PrepareLaneHelpers(lane_);
660 670
661 // this could be done in lane_new before the lane body function is pushed on the stack to avoid unnecessary stack slot shifting around 671 _rc = ToLuaError(lua_pcall(_L, _nargs, LUA_MULTRET, _errorHandlerCount)); // L: eh? retvals|err
662#if ERROR_FULL_STACK
663 // Tie "set_error_reporting()" to the state
664 lua_pushcfunction(_L, LG_set_error_reporting);
665 populate_func_lookup_table(_L, -1, "set_error_reporting");
666 lua_setglobal(_L, "set_error_reporting");
667 672
668 STACK_GROW(_L, 1); 673 if (_errorHandlerCount) {
669 lua_pushcfunction(_L, lane_error); // L: func args handler 674 lua_remove(_L, 1); // L: retvals|error
670 lua_insert(_L, 1); // L: handler func args 675 }
671#endif // L: ERROR_FULL_STACK
672
673 _rc = ToLuaError(lua_pcall(_L, _nargs, LUA_MULTRET, ERROR_FULL_STACK)); // L: retvals|err
674
675#if ERROR_FULL_STACK
676 lua_remove(_L, 1); // L: retvals|error
677#endif // ERROR_FULL_STACK
678 676
679 // in case of error and if it exists, fetch stack trace from registry and push it 677 // in case of error and if it exists, fetch stack trace from registry and push it
680 push_stack_trace(_L, _rc, 1); // L: retvals|error [trace] 678 push_stack_trace(_L, lane_->errorTraceLevel, _rc, 1); // L: retvals|error [trace]
681 679
682 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)))); 680 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))));
683 // Call finalizers, if the script has set them up. 681 // Call finalizers, if the script has set them up.
684 // 682 //
685 LuaError const _rc2{ run_finalizers(_L, _rc) }; 683 LuaError const _rc2{ run_finalizers(_L, lane_->errorTraceLevel, _rc) };
686 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END(_U), _L, get_errcode_name(_rc2))); 684 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END(_U), _L, get_errcode_name(_rc2)));
687 if (_rc2 != LuaError::OK) { // Error within a finalizer! 685 if (_rc2 != LuaError::OK) { // Error within a finalizer!
688 // the finalizer generated an error, and left its own error message [and stack trace] on the stack 686 // the finalizer generated an error, and left its own error message [and stack trace] on the stack
@@ -779,10 +777,13 @@ static void lane_main(Lane* lane_)
779// #################################### Lane implementation ######################################## 777// #################################### Lane implementation ########################################
780// ################################################################################################# 778// #################################################################################################
781 779
782Lane::Lane(Universe* U_, lua_State* L_) 780Lane::Lane(Universe* U_, lua_State* L_, ErrorTraceLevel errorTraceLevel_)
783: U{ U_ } 781: U{ U_ }
784, L{ L_ } 782, L{ L_ }
783, errorTraceLevel{ errorTraceLevel_ }
785{ 784{
785 assert(errorTraceLevel == ErrorTraceLevel::Minimal || errorTraceLevel == ErrorTraceLevel::Basic || errorTraceLevel == ErrorTraceLevel::Extended);
786 kExtendedStackTraceRegKey.setValue(L_, [yes = errorTraceLevel == ErrorTraceLevel::Extended ? 1 : 0](lua_State* L_) { lua_pushboolean(L_, yes); });
786#if HAVE_LANE_TRACKING() 787#if HAVE_LANE_TRACKING()
787 U->tracker.tracking_add(this); 788 U->tracker.tracking_add(this);
788#endif // HAVE_LANE_TRACKING() 789#endif // HAVE_LANE_TRACKING()
@@ -822,6 +823,29 @@ void Lane::changeDebugName(int nameIdx_)
822 823
823// ################################################################################################# 824// #################################################################################################
824 825
826//---
827// str= thread_status( lane )
828//
829// Returns: "pending" not started yet
830// -> "running" started, doing its work..
831// <-> "waiting" blocked in a receive()
832// -> "done" finished, results are there
833// / "error" finished at an error, error value is there
834// / "cancelled" execution cancelled by M (state gone)
835//
836[[nodiscard]] char const* Lane::errorTraceLevelString() const
837{
838 char const* const _str{
839 (errorTraceLevel == ErrorTraceLevel::Minimal) ? "minimal" :
840 (errorTraceLevel == ErrorTraceLevel::Basic) ? "basic" :
841 (errorTraceLevel == ErrorTraceLevel::Extended) ? "extended" :
842 nullptr
843 };
844 return _str;
845}
846
847// #################################################################################################
848
825namespace global { 849namespace global {
826 static struct luaL_Reg const sLaneFunctions[] = { 850 static struct luaL_Reg const sLaneFunctions[] = {
827 { "__gc", lane_gc }, 851 { "__gc", lane_gc },
@@ -840,18 +864,30 @@ void Lane::PushMetatable(lua_State* L_)
840 if (luaL_newmetatable(L_, kLaneMetatableName)) { // L_: mt 864 if (luaL_newmetatable(L_, kLaneMetatableName)) { // L_: mt
841 luaG_registerlibfuncs(L_, global::sLaneFunctions); 865 luaG_registerlibfuncs(L_, global::sLaneFunctions);
842 // cache error() and tostring() 866 // cache error() and tostring()
843 lua_getglobal(L_, "error"); // L_: mt error 867 kCachedError.pushKey(L_); // L_: mt kCachedError
844 LUA_ASSERT(L_, lua_isfunction(L_, -1)); 868 lua_getglobal(L_, "error"); // L_: mt kCachedError error()
845 lua_setfield(L_, -2, "cached_error"); // L_: mt 869 lua_rawset(L_, -3); // L_: mt
846 lua_getglobal(L_, "tostring"); // L_: mt tostring 870 kCachedTostring.pushKey(L_); // L_: mt kCachedTostring
847 LUA_ASSERT(L_, lua_isfunction(L_, -1)); 871 lua_getglobal(L_, "tostring"); // L_: mt kCachedTostring tostring()
848 lua_setfield(L_, -2, "cached_tostring"); // L_: mt 872 lua_rawset(L_, -3); // L_: mt
849 // hide the actual metatable from getmetatable() 873 // hide the actual metatable from getmetatable()
850 lua_pushliteral(L_, kLaneMetatableName); // L_: mt "Lane" 874 lua_pushliteral(L_, kLaneMetatableName); // L_: mt "Lane"
851 lua_setfield(L_, -2, "__metatable"); // L_: mt 875 lua_setfield(L_, -2, "__metatable"); // L_: mt
852 } 876 }
853 STACK_CHECK(L_, 1); 877 STACK_CHECK(L_, 1);
854} 878}
879
880// #################################################################################################
881
882[[nodiscard]] int Lane::pushErrorHandler() const
883{
884 if (errorTraceLevel != ErrorTraceLevel::Minimal) {
885 lua_pushcfunction(L, lane_error);
886 return 1;
887 }
888 return 0;
889}
890
855// ################################################################################################# 891// #################################################################################################
856 892
857void Lane::pushThreadStatus(lua_State* L_) const 893void Lane::pushThreadStatus(lua_State* L_) const
@@ -864,6 +900,16 @@ void Lane::pushThreadStatus(lua_State* L_) const
864 900
865// ################################################################################################# 901// #################################################################################################
866 902
903void Lane::pushErrorTraceLevel(lua_State* L_) const
904{
905 char const* const _str{ errorTraceLevelString() };
906 LUA_ASSERT(L_, _str);
907
908 lua_pushstring(L_, _str);
909}
910
911// #################################################################################################
912
867// intern the debug name in the caller lua state so that the pointer remains valid after the lane's state is closed 913// intern the debug name in the caller lua state so that the pointer remains valid after the lane's state is closed
868void Lane::securizeDebugName(lua_State* L_) 914void Lane::securizeDebugName(lua_State* L_)
869{ 915{
diff --git a/src/lane.h b/src/lane.h
index c0b5246..984d433 100644
--- a/src/lane.h
+++ b/src/lane.h
@@ -64,6 +64,14 @@ class Lane
64 }; 64 };
65 using enum Status; 65 using enum Status;
66 66
67 enum class ErrorTraceLevel
68 {
69 Minimal, // no error handler function when running the lane body
70 Basic, // lane body errors caught by a error handler
71 Extended // same as above, but with more data extracted from the debug infos
72 };
73 using enum ErrorTraceLevel;
74
67 // the thread 75 // the thread
68 std::jthread thread; 76 std::jthread thread;
69 // a latch to wait for the lua_State to be ready 77 // a latch to wait for the lua_State to be ready
@@ -104,10 +112,11 @@ class Lane
104 // S: cleans up after itself if non-nullptr at lane exit 112 // S: cleans up after itself if non-nullptr at lane exit
105 113
106#if HAVE_LANE_TRACKING() 114#if HAVE_LANE_TRACKING()
115 // For tracking only
107 Lane* volatile tracking_next{ nullptr }; 116 Lane* volatile tracking_next{ nullptr };
108#endif // HAVE_LANE_TRACKING() 117#endif // HAVE_LANE_TRACKING()
109 // 118
110 // For tracking only 119 ErrorTraceLevel const errorTraceLevel{ Basic };
111 120
112 [[nodiscard]] static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internalAllocator.alloc(size_); } 121 [[nodiscard]] static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internalAllocator.alloc(size_); }
113 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception 122 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
@@ -115,18 +124,20 @@ class Lane
115 // this one is for us, to make sure memory is freed by the correct allocator 124 // this one is for us, to make sure memory is freed by the correct allocator
116 static void operator delete(void* p_) { static_cast<Lane*>(p_)->U->internalAllocator.free(p_, sizeof(Lane)); } 125 static void operator delete(void* p_) { static_cast<Lane*>(p_)->U->internalAllocator.free(p_, sizeof(Lane)); }
117 126
118 Lane(Universe* U_, lua_State* L_); 127 Lane(Universe* U_, lua_State* L_, ErrorTraceLevel errorTraceLevel_);
119 ~Lane(); 128 ~Lane();
120 129
121 void changeDebugName(int nameIdx_); 130 void changeDebugName(int nameIdx_);
122 void close() { lua_State* _L{ L }; L = nullptr; lua_close(_L); } 131 void close() { lua_State* _L{ L }; L = nullptr; lua_close(_L); }
132 [[nodiscard]] char const* errorTraceLevelString() const;
133 [[nodiscard]] int pushErrorHandler() const;
134 void pushErrorTraceLevel(lua_State* L_) const;
135 static void PushMetatable(lua_State* L_);
123 void pushThreadStatus(lua_State* L_) const; 136 void pushThreadStatus(lua_State* L_) const;
124 void securizeDebugName(lua_State* L_); 137 void securizeDebugName(lua_State* L_);
125 void startThread(int priority_); 138 void startThread(int priority_);
126 [[nodiscard]] char const* threadStatusString() const; 139 [[nodiscard]] char const* threadStatusString() const;
127 [[nodiscard]] bool waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_); 140 [[nodiscard]] bool waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_);
128
129 static void PushMetatable(lua_State* L_);
130}; 141};
131 142
132// ################################################################################################# 143// #################################################################################################
diff --git a/src/lanes.cpp b/src/lanes.cpp
index b3f7be7..7b730cd 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -205,32 +205,33 @@ LUAG_FUNC(register)
205 205
206// ################################################################################################# 206// #################################################################################################
207 207
208//--- 208//--- [] means can be nil
209// lane_ud = lane_new( function 209// lane_ud = lane_new( function
210// , [libs_str] 210// , [libs_str]
211// , [priority_int=0] 211// , [priority_int]
212// , [globals_tbl] 212// , [globals_tbl]
213// , [package_tbl] 213// , [package_tbl]
214// , [required_tbl] 214// , [required_tbl]
215// , [gc_cb_func] 215// , [gc_cb_func]
216// , [name] 216// , [name]
217// , error_trace_level
217// [, ... args ...]) 218// [, ... args ...])
218// 219//
219// Upvalues: metatable to use for 'lane_ud' 220// Upvalues: metatable to use for 'lane_ud'
220// 221//
221LUAG_FUNC(lane_new) 222LUAG_FUNC(lane_new)
222{ 223{
223 // first 8 args: func libs priority globals package required gc_cb name 224 // first 9 args: func libs priority globals package required gc_cb name error_trace_level
224 char const* const _libs_str{ lua_tostring(L_, 2) }; 225 char const* const _libs_str{ lua_tostring(L_, 2) };
225 bool const _have_priority{ !lua_isnoneornil(L_, 3) }; 226 bool const _have_priority{ !lua_isnoneornil(L_, 3) };
226 int const _priority{ _have_priority ? static_cast<int>(lua_tointeger(L_, 3)) : kThreadPrioDefault };
227 int const _globals_idx{ lua_isnoneornil(L_, 4) ? 0 : 4 }; 227 int const _globals_idx{ lua_isnoneornil(L_, 4) ? 0 : 4 };
228 int const _package_idx{ lua_isnoneornil(L_, 5) ? 0 : 5 }; 228 int const _package_idx{ lua_isnoneornil(L_, 5) ? 0 : 5 };
229 int const _required_idx{ lua_isnoneornil(L_, 6) ? 0 : 6 }; 229 int const _required_idx{ lua_isnoneornil(L_, 6) ? 0 : 6 };
230 int const _gc_cb_idx{ lua_isnoneornil(L_, 7) ? 0 : 7 }; 230 int const _gc_cb_idx{ lua_isnoneornil(L_, 7) ? 0 : 7 };
231 int const _name_idx{ lua_isnoneornil(L_, 8) ? 0 : 8 }; 231 int const _name_idx{ lua_isnoneornil(L_, 8) ? 0 : 8 };
232 int const _error_trace_level_idx{ 9 };
232 233
233 static constexpr int kFixedArgsIdx{ 8 }; 234 static constexpr int kFixedArgsIdx{ 9 };
234 int const _nargs{ lua_gettop(L_) - kFixedArgsIdx }; 235 int const _nargs{ lua_gettop(L_) - kFixedArgsIdx };
235 Universe* const _U{ universe_get(L_) }; 236 Universe* const _U{ universe_get(L_) };
236 LUA_ASSERT(L_, _nargs >= 0); 237 LUA_ASSERT(L_, _nargs >= 0);
@@ -238,6 +239,7 @@ LUAG_FUNC(lane_new)
238 // public Lanes API accepts a generic range -3/+3 239 // public Lanes API accepts a generic range -3/+3
239 // that will be remapped into the platform-specific scheduler priority scheme 240 // that will be remapped into the platform-specific scheduler priority scheme
240 // On some platforms, -3 is equivalent to -2 and +3 to +2 241 // On some platforms, -3 is equivalent to -2 and +3 to +2
242 int const _priority{ _have_priority ? static_cast<int>(lua_tointeger(L_, 3)) : kThreadPrioDefault };
241 if (_have_priority && (_priority < kThreadPrioMin || _priority > kThreadPrioMax)) { 243 if (_have_priority && (_priority < kThreadPrioMin || _priority > kThreadPrioMax)) {
242 raise_luaL_error(L_, "Priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _priority); 244 raise_luaL_error(L_, "Priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _priority);
243 } 245 }
@@ -246,11 +248,11 @@ LUAG_FUNC(lane_new)
246 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END(_U))); 248 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END(_U)));
247 249
248 // populate with selected libraries at the same time. 250 // populate with selected libraries at the same time.
249 lua_State* const _L2{ luaG_newstate(_U, SourceState{ L_ }, _libs_str) }; // L_: [8 args] ... L2: 251 lua_State* const _L2{ luaG_newstate(_U, SourceState{ L_ }, _libs_str) }; // L_: [9 args] ... L2:
250 STACK_CHECK_START_REL(_L2, 0); 252 STACK_CHECK_START_REL(_L2, 0);
251 253
252 // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) 254 // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread)
253 Lane* const _lane{ new (_U) Lane{ _U, _L2 } }; 255 Lane* const _lane{ new (_U) Lane{ _U, _L2, static_cast<Lane::ErrorTraceLevel>(lua_tointeger(L_, _error_trace_level_idx)) } };
254 if (_lane == nullptr) { 256 if (_lane == nullptr) {
255 raise_luaL_error(L_, "could not create lane: out of memory"); 257 raise_luaL_error(L_, "could not create lane: out of memory");
256 } 258 }
@@ -410,7 +412,7 @@ LUAG_FUNC(lane_new)
410 lua_pop(_L2, 1); // L_: [8 args] args... n "modname" L2: 412 lua_pop(_L2, 1); // L_: [8 args] args... n "modname" L2:
411 } 413 }
412 } 414 }
413 lua_pop(L_, 1); // L_: func libs priority globals package required gc_cb [... args ...] n 415 lua_pop(L_, 1); // L_: func libs priority globals package required gc_cb [... args ...] n
414 ++_nbRequired; 416 ++_nbRequired;
415 } // L_: [8 args] args... 417 } // L_: [8 args] args...
416 } 418 }
@@ -443,35 +445,36 @@ LUAG_FUNC(lane_new)
443 STACK_CHECK(_L2, 0); 445 STACK_CHECK(_L2, 0);
444 446
445 // Lane main function 447 // Lane main function
448 int const errorHandlerCount{ _lane->pushErrorHandler() }; // L2: eh?
446 LuaType const _func_type{ lua_type_as_enum(L_, 1) }; 449 LuaType const _func_type{ lua_type_as_enum(L_, 1) };
447 if (_func_type == LuaType::FUNCTION) { 450 if (_func_type == LuaType::FUNCTION) {
448 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END(_U))); 451 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END(_U)));
449 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); 452 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U });
450 lua_pushvalue(L_, 1); // L_: [8 args] args... func L2: 453 lua_pushvalue(L_, 1); // L_: [8 args] args... func L2: eh?
451 InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; 454 InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
452 InterCopyResult const _res{ _c.inter_move(1) }; // L_: [8 args] args... L2: func 455 InterCopyResult const _res{ _c.inter_move(1) }; // L_: [8 args] args... L2: eh? func
453 if (_res != InterCopyResult::Success) { 456 if (_res != InterCopyResult::Success) {
454 raise_luaL_error(L_, "tried to copy unsupported types"); 457 raise_luaL_error(L_, "tried to copy unsupported types");
455 } 458 }
456 } else if (_func_type == LuaType::STRING) { 459 } else if (_func_type == LuaType::STRING) {
457 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: compile lane body\n" INDENT_END(_U))); 460 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: compile lane body\n" INDENT_END(_U)));
458 // compile the string 461 // compile the string
459 if (luaL_loadstring(_L2, lua_tostring(L_, 1)) != 0) { // L_: [8 args] args... L2: func 462 if (luaL_loadstring(_L2, lua_tostring(L_, 1)) != 0) { // L_: [8 args] args... L2: eh? func
460 raise_luaL_error(L_, "error when parsing lane function code"); 463 raise_luaL_error(L_, "error when parsing lane function code");
461 } 464 }
462 } else { 465 } else {
463 raise_luaL_error(L_, "Expected function, got %s", lua_typename(L_, _func_type)); 466 raise_luaL_error(L_, "Expected function, got %s", lua_typename(L_, _func_type));
464 } 467 }
465 STACK_CHECK(L_, 0); 468 STACK_CHECK(L_, 0);
466 STACK_CHECK(_L2, 1); 469 STACK_CHECK(_L2, errorHandlerCount + 1);
467 LUA_ASSERT(L_, lua_isfunction(_L2, 1)); 470 LUA_ASSERT(L_, lua_isfunction(_L2, errorHandlerCount + 1));
468 471
469 // revive arguments 472 // revive arguments
470 if (_nargs > 0) { 473 if (_nargs > 0) {
471 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END(_U))); 474 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END(_U)));
472 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); 475 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U });
473 InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; 476 InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
474 InterCopyResult const res{ _c.inter_move(_nargs) }; // L_: [8 args] L2: func args... 477 InterCopyResult const res{ _c.inter_move(_nargs) }; // L_: [8 args] L2: eh? func args...
475 if (res != InterCopyResult::Success) { 478 if (res != InterCopyResult::Success) {
476 raise_luaL_error(L_, "tried to copy unsupported types"); 479 raise_luaL_error(L_, "tried to copy unsupported types");
477 } 480 }
@@ -480,8 +483,8 @@ LUAG_FUNC(lane_new)
480 LUA_ASSERT(L_, lua_gettop(L_) == kFixedArgsIdx); 483 LUA_ASSERT(L_, lua_gettop(L_) == kFixedArgsIdx);
481 484
482 // Store 'lane' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive). 485 // Store 'lane' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive).
483 kLanePointerRegKey.setValue(_L2, [lane = _lane](lua_State* L_) { lua_pushlightuserdata(L_, lane); });// L_: [8 args] L2: func args... 486 kLanePointerRegKey.setValue(_L2, [lane = _lane](lua_State* L_) { lua_pushlightuserdata(L_, lane); });// L_: [8 args] L2: eh? func args...
484 STACK_CHECK(_L2, 1 + _nargs); 487 STACK_CHECK(_L2, errorHandlerCount + 1 + _nargs);
485 488
486 STACK_CHECK_RESET_REL(L_, 0); 489 STACK_CHECK_RESET_REL(L_, 0);
487 // all went well, the lane's thread can start working 490 // all went well, the lane's thread can start working
@@ -579,7 +582,7 @@ LUAG_FUNC(wakeup_conv)
579// ######################################## Module linkage ######################################### 582// ######################################## Module linkage #########################################
580// ################################################################################################# 583// #################################################################################################
581 584
582extern int LG_linda(lua_State* L_); 585extern LUAG_FUNC(linda);
583 586
584namespace global { 587namespace global {
585 static struct luaL_Reg const sLanesFunctions[] = { 588 static struct luaL_Reg const sLanesFunctions[] = {
@@ -700,9 +703,8 @@ LUAG_FUNC(configure)
700 STACK_CHECK(L_, 2); 703 STACK_CHECK(L_, 2);
701 704
702 // prepare the metatable for threads 705 // prepare the metatable for threads
703 // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname } 706 // contains keys: { __gc, __index, cancel, join, get_debug_threadname }
704 Lane::PushMetatable(L_); 707 Lane::PushMetatable(L_); // L_: settings M {lane_mt}
705
706 lua_pushcclosure(L_, LG_lane_new, 1); // L_: settings M lane_new 708 lua_pushcclosure(L_, LG_lane_new, 1); // L_: settings M lane_new
707 lua_setfield(L_, -2, "lane_new"); // L_: settings M 709 lua_setfield(L_, -2, "lane_new"); // L_: settings M
708 710
diff --git a/src/lanes.lua b/src/lanes.lua
index 85d8e58..ff5fdf3 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -65,6 +65,8 @@ local type = assert(type)
65 65
66-- ################################################################################################# 66-- #################################################################################################
67 67
68local isLuaJIT = (package and package.loaded.jit and jit.version) and true or false
69
68local default_params = 70local default_params =
69{ 71{
70 nb_keepers = 1, 72 nb_keepers = 1,
@@ -77,9 +79,9 @@ local default_params =
77 demote_full_userdata = nil, 79 demote_full_userdata = nil,
78 verbose_errors = false, 80 verbose_errors = false,
79 -- LuaJIT provides a thread-unsafe allocator by default, so we need to protect it when used in parallel lanes 81 -- LuaJIT provides a thread-unsafe allocator by default, so we need to protect it when used in parallel lanes
80 allocator = (package and package.loaded.jit and jit.version) and "protected" or nil, 82 allocator = isLuaJIT and "protected" or nil,
81 -- it looks also like LuaJIT allocator may not appreciate direct use of its allocator for other purposes than the VM operation 83 -- it looks also like LuaJIT allocator may not appreciate direct use of its allocator for other purposes than the VM operation
82 internal_allocator = (package and package.loaded.jit and jit.version) and "libc" or "allocator" 84 internal_allocator = isLuaJIT and "libc" or "allocator"
83} 85}
84 86
85-- ################################################################################################# 87-- #################################################################################################
@@ -207,12 +209,23 @@ end
207 209
208-- ################################################################################################# 210-- #################################################################################################
209 211
212-- must match Lane::ErrorTraceLevel values
213local error_trace_levels = {
214 minimal = 0,
215 basic = 1,
216 extended = 2
217}
218
210local opt_validators = 219local opt_validators =
211{ 220{
212 gc_cb = function(v_) 221 gc_cb = function(v_)
213 local tv = type(v_) 222 local tv = type(v_)
214 return (tv == "function") and v_ or raise_option_error("gc_cb", tv, v_) 223 return (tv == "function") and v_ or raise_option_error("gc_cb", tv, v_)
215 end, 224 end,
225 error_trace_level = function(v_)
226 local tv = type(v_)
227 return (error_trace_levels[v_] ~= nil) and v_ or raise_option_error("error_trace_level", tv, v_)
228 end,
216 globals = function(v_) 229 globals = function(v_)
217 local tv = type(v_) 230 local tv = type(v_)
218 return (tv == "table") and v_ or raise_option_error("globals", tv, v_) 231 return (tv == "table") and v_ or raise_option_error("globals", tv, v_)
@@ -343,10 +356,10 @@ local gen = function(...)
343 end 356 end
344 357
345 local core_lane_new = assert(core.lane_new) 358 local core_lane_new = assert(core.lane_new)
346 local priority, globals, package, required, gc_cb, name = opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb, opt.name 359 local priority, globals, package, required, gc_cb, name, error_trace_level = opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb, opt.name, error_trace_levels[opt.error_trace_level]
347 return function(...) 360 return function(...)
348 -- must pass functions args last else they will be truncated to the first one 361 -- must pass functions args last else they will be truncated to the first one
349 return core_lane_new(func, libs, priority, globals, package, required, gc_cb, name, ...) 362 return core_lane_new(func, libs, priority, globals, package, required, gc_cb, name, error_trace_level, ...)
350 end 363 end
351end -- gen() 364end -- gen()
352 365