diff options
Diffstat (limited to 'src/lane.cpp')
-rw-r--r-- | src/lane.cpp | 428 |
1 files changed, 237 insertions, 191 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 | * | 37 | static 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 | */ | 39 | static 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 | ||
81 | LUAG_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 |
101 | static LUAG_FUNC(set_debug_threadname) | 80 | static 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 | 182 | static int thread_index_number(lua_State* L_) |
207 | LUAG_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 | ||
295 | static 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 | ||
327 | static 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 |
393 | static constexpr RegistryUniqueKey kStackTraceRegKey{ 0x3F327747CACAA904ull }; | 405 | static 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 | ||
466 | static void push_stack_trace(lua_State* L_, LuaError rc_, int stk_base_) | 477 | static 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 | ||
782 | Lane::Lane(Universe* U_, lua_State* L_) | 780 | Lane::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 | |||
825 | namespace global { | 849 | namespace 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 | ||
857 | void Lane::pushThreadStatus(lua_State* L_) const | 893 | void Lane::pushThreadStatus(lua_State* L_) const |
@@ -864,6 +900,16 @@ void Lane::pushThreadStatus(lua_State* L_) const | |||
864 | 900 | ||
865 | // ################################################################################################# | 901 | // ################################################################################################# |
866 | 902 | ||
903 | void 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 |
868 | void Lane::securizeDebugName(lua_State* L_) | 914 | void Lane::securizeDebugName(lua_State* L_) |
869 | { | 915 | { |