diff options
author | Benoit Germain <bnt period germain arrobase gmail period com> | 2014-03-20 16:49:15 +0100 |
---|---|---|
committer | Benoit Germain <bnt period germain arrobase gmail period com> | 2014-03-20 16:49:15 +0100 |
commit | 5db999e79a1dd28365231c449840236b204a1ca9 (patch) | |
tree | beed00430548b28d8e1c0a316bb30059971adebe | |
parent | 43343cc59b54ebf216bc3519abcf4cd3e1f0243f (diff) | |
download | lanes-5db999e79a1dd28365231c449840236b204a1ca9.tar.gz lanes-5db999e79a1dd28365231c449840236b204a1ca9.tar.bz2 lanes-5db999e79a1dd28365231c449840236b204a1ca9.zip |
Fixed error handling when handler isn't called
* bumped version to 3.9.4
* set_finalizer throws an error if provided finalizer isn't a function
* fix error handling when the error doesn't generate an error handler
call (IOW, all errors but LUA_ERRRUN)
* provide callstack if LUA_ERRRUN occurs inside a finalizer
-rw-r--r-- | CHANGES | 6 | ||||
-rw-r--r-- | docs/index.html | 6 | ||||
-rw-r--r-- | src/lanes.c | 413 |
3 files changed, 218 insertions, 207 deletions
@@ -1,5 +1,11 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHANGE 108: BGe 20-Mar-14 | ||
4 | * bumped version to 3.9.4 | ||
5 | * set_finalizer throws an error if provided finalizer isn't a function | ||
6 | * fix error handling when the error doesn't generate an error handler call (IOW, all errors but LUA_ERRRUN) | ||
7 | * provide callstack if LUA_ERRRUN occurs inside a finalizer | ||
8 | |||
3 | CHANGE 107: BGe 19-Mar-14 | 9 | CHANGE 107: BGe 19-Mar-14 |
4 | * Make sure we don't mutex-wrap require() more than once, just in case | 10 | * Make sure we don't mutex-wrap require() more than once, just in case |
5 | 11 | ||
diff --git a/docs/index.html b/docs/index.html index c4a6441..da5da71 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -70,7 +70,7 @@ | |||
70 | </p> | 70 | </p> |
71 | 71 | ||
72 | <p> | 72 | <p> |
73 | This document was revised on 27-Feb-14, and applies to version <tt>3.9.3</tt>. | 73 | This document was revised on 20-Mar-14, and applies to version <tt>3.9.4</tt>. |
74 | </p> | 74 | </p> |
75 | </font> | 75 | </font> |
76 | </center> | 76 | </center> |
@@ -253,6 +253,10 @@ | |||
253 | At the same time, <tt>configure()</tt> itself will be replaced by another function that raises an error if called again with differing arguments, if any. | 253 | At the same time, <tt>configure()</tt> itself will be replaced by another function that raises an error if called again with differing arguments, if any. |
254 | </p> | 254 | </p> |
255 | <p> | 255 | <p> |
256 | Also, once Lanes is initialized, <tt>require()</tt> is replaced by another one that wraps it inside a mutex, both in the main state and in all created lanes. This prevents multiple thread-unsafe module initializations from several lanes to occur simultaneously. | ||
257 | It remains to be seen whether this is actually useful or not: If a module is already threadsafe, protecting its initialization isn't useful. And if it is not, any parallel operation may crash without Lanes being able to do anything about it. | ||
258 | </p> | ||
259 | <p> | ||
256 | <b>IMPORTANT NOTE:</b> Starting with version 3.7.0, only the first occurence of <tt>require "lanes"</tt> must be followed by a call to <tt>.configure()</tt>. From this point, a simple <tt>require "lanes"</tt> will do wherever you need to require lanes again. | 260 | <b>IMPORTANT NOTE:</b> Starting with version 3.7.0, only the first occurence of <tt>require "lanes"</tt> must be followed by a call to <tt>.configure()</tt>. From this point, a simple <tt>require "lanes"</tt> will do wherever you need to require lanes again. |
257 | </p> | 261 | </p> |
258 | 262 | ||
diff --git a/src/lanes.c b/src/lanes.c index 6b3264a..9e79f64 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -52,7 +52,7 @@ | |||
52 | * ... | 52 | * ... |
53 | */ | 53 | */ |
54 | 54 | ||
55 | char const* VERSION = "3.9.3"; | 55 | char const* VERSION = "3.9.4"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
@@ -293,26 +293,29 @@ struct s_Linda; | |||
293 | * Returns: TRUE if a table was pushed | 293 | * Returns: TRUE if a table was pushed |
294 | * FALSE if no table found, not created, and nothing pushed | 294 | * FALSE if no table found, not created, and nothing pushed |
295 | */ | 295 | */ |
296 | static bool_t push_registry_table( lua_State*L, void *key, bool_t create ) { | 296 | static bool_t push_registry_table( lua_State* L, void* key, bool_t create) |
297 | 297 | { | |
298 | STACK_GROW(L,3); | 298 | STACK_GROW( L, 3); |
299 | 299 | STACK_CHECK( L); | |
300 | lua_pushlightuserdata( L, key ); | 300 | lua_pushlightuserdata( L, key); // key |
301 | lua_gettable( L, LUA_REGISTRYINDEX ); | 301 | lua_gettable( L, LUA_REGISTRYINDEX); // t? |
302 | 302 | ||
303 | if (lua_isnil(L,-1)) { | 303 | if( lua_isnil( L, -1)) // nil? |
304 | lua_pop(L,1); | 304 | { |
305 | 305 | lua_pop( L, 1); // | |
306 | if (!create) return FALSE; // nothing pushed | 306 | |
307 | 307 | if( !create) | |
308 | lua_newtable(L); | 308 | { |
309 | lua_pushlightuserdata( L, key ); | 309 | return FALSE; |
310 | lua_pushvalue(L,-2); // duplicate of the table | 310 | } |
311 | lua_settable( L, LUA_REGISTRYINDEX ); | 311 | |
312 | 312 | lua_newtable(L); // t | |
313 | // [-1]: table that's also bound in registry | 313 | lua_pushlightuserdata( L, key); // t key |
314 | } | 314 | lua_pushvalue( L, -2); // t key t |
315 | return TRUE; // table pushed | 315 | lua_rawset( L, LUA_REGISTRYINDEX); // t |
316 | } | ||
317 | STACK_END( L, 1); | ||
318 | return TRUE; // table pushed | ||
316 | } | 319 | } |
317 | 320 | ||
318 | #if HAVE_LANE_TRACKING | 321 | #if HAVE_LANE_TRACKING |
@@ -1281,20 +1284,18 @@ LUAG_FUNC( linda) | |||
1281 | // Add a function that will be called when exiting the lane, either via | 1284 | // Add a function that will be called when exiting the lane, either via |
1282 | // normal return or an error. | 1285 | // normal return or an error. |
1283 | // | 1286 | // |
1284 | LUAG_FUNC( set_finalizer ) | 1287 | LUAG_FUNC( set_finalizer) |
1285 | { | 1288 | { |
1286 | STACK_GROW(L,3); | 1289 | luaL_argcheck( L, lua_isfunction( L, 1), 1, "finalizer should be a function"); |
1287 | 1290 | luaL_argcheck( L, lua_gettop( L) == 1, 1, "too many arguments"); | |
1288 | // Get the current finalizer table (if any) | 1291 | // Get the current finalizer table (if any) |
1289 | // | 1292 | push_registry_table( L, FINALIZER_REG_KEY, TRUE /*do create if none*/); // finalizer {finalisers} |
1290 | push_registry_table( L, FINALIZER_REG_KEY, TRUE /*do create if none*/ ); | 1293 | STACK_GROW( L, 2); |
1291 | 1294 | lua_pushinteger( L, lua_rawlen( L, -1) + 1); // finalizer {finalisers} idx | |
1292 | lua_pushinteger( L, lua_rawlen(L,-1)+1 ); | 1295 | lua_pushvalue( L, 1); // finalizer {finalisers} idx finalizer |
1293 | lua_pushvalue( L, 1 ); // copy of the function | 1296 | lua_rawset( L, -3); // finalizer {finalisers} |
1294 | lua_settable( L, -3 ); | 1297 | lua_pop( L, 2); // |
1295 | 1298 | return 0; | |
1296 | lua_pop(L,1); | ||
1297 | return 0; | ||
1298 | } | 1299 | } |
1299 | 1300 | ||
1300 | 1301 | ||
@@ -1311,14 +1312,15 @@ LUAG_FUNC( set_finalizer ) | |||
1311 | // | 1312 | // |
1312 | // TBD: should we add stack trace on failing finalizer, wouldn't be hard.. | 1313 | // TBD: should we add stack trace on failing finalizer, wouldn't be hard.. |
1313 | // | 1314 | // |
1315 | static void push_stack_trace( lua_State* L, int rc_, int stk_base_); | ||
1316 | |||
1314 | static int run_finalizers( lua_State* L, int lua_rc) | 1317 | static int run_finalizers( lua_State* L, int lua_rc) |
1315 | { | 1318 | { |
1316 | int error_index, finalizers_index; | 1319 | int finalizers_index; |
1317 | int n; | 1320 | int n; |
1318 | int err_handler_index = 0; | 1321 | int err_handler_index = 0; |
1319 | int rc = 0; // [err_msg {stack_trace}]? | 1322 | int rc = LUA_OK; // ... |
1320 | 1323 | if( !push_registry_table( L, FINALIZER_REG_KEY, FALSE)) // ... finalizers? | |
1321 | if( !push_registry_table( L, FINALIZER_REG_KEY, FALSE)) // [err_msg {stack_trace}]? {func [, ...]}? | ||
1322 | { | 1324 | { |
1323 | return 0; // no finalizers | 1325 | return 0; // no finalizers |
1324 | } | 1326 | } |
@@ -1328,51 +1330,58 @@ static int run_finalizers( lua_State* L, int lua_rc) | |||
1328 | finalizers_index = lua_gettop( L); | 1330 | finalizers_index = lua_gettop( L); |
1329 | 1331 | ||
1330 | #if ERROR_FULL_STACK | 1332 | #if ERROR_FULL_STACK |
1331 | lua_pushcfunction( L, lane_error); // [err_msg {stack_trace}]? {func [, ...]}? lane_error | 1333 | lua_pushcfunction( L, lane_error); // ... finalizers lane_error |
1332 | err_handler_index = lua_gettop( L); | 1334 | err_handler_index = lua_gettop( L); |
1333 | #endif // ERROR_FULL_STACK | 1335 | #endif // ERROR_FULL_STACK |
1334 | error_index = (lua_rc != LUA_OK) ? finalizers_index - (1 + ERROR_FULL_STACK) : 0; | ||
1335 | 1336 | ||
1336 | for( n = (int) lua_rawlen( L, finalizers_index); n > 0; -- n) | 1337 | for( n = (int) lua_rawlen( L, finalizers_index); n > 0; -- n) |
1337 | { | 1338 | { |
1338 | int args = 0; | 1339 | int args = 0; |
1339 | lua_pushinteger( L, n); // [err_msg {stack_trace}]? {func [, ...]}? lane_error n | 1340 | lua_pushinteger( L, n); // ... finalizers lane_error n |
1340 | lua_gettable( L, finalizers_index); // [err_msg {stack_trace}]? {func [, ...]}? lane_error finalizer | 1341 | lua_rawget( L, finalizers_index); // ... finalizers lane_error finalizer |
1341 | ASSERT_L( lua_isfunction( L, -1)); | 1342 | ASSERT_L( lua_isfunction( L, -1)); |
1342 | if( error_index) | 1343 | if( lua_rc != LUA_OK) // we have an error message and an optional stack trace at the bottom of the stack |
1343 | { | 1344 | { |
1344 | //char const* err_msg = lua_tostring( L, error_index); | 1345 | ASSERT_L( finalizers_index == 2 || finalizers_index == 3); |
1345 | lua_pushvalue( L, error_index); // [err_msg {stack_trace}]? {func [, ...]}? lane_error finalizer err_msg | 1346 | //char const* err_msg = lua_tostring( L, 1); |
1346 | #if ERROR_FULL_STACK | 1347 | lua_pushvalue( L, 1); // ... finalizers lane_error finalizer err_msg |
1347 | lua_pushvalue( L, error_index + 1); // [err_msg {stack_trace}]? {func [, ...]}? lane_error finalizer err_msg {stack_trace} | 1348 | // note we don't always have a stack trace for example when CANCEL_ERROR, or when we got an error that doesn't call our handler, such as LUA_ERRMEM |
1348 | #endif // ERROR_FULL_STACK | 1349 | if( finalizers_index == 3) |
1349 | args = 1 + ERROR_FULL_STACK; | 1350 | { |
1351 | lua_pushvalue( L, 2); // ... finalizers lane_error finalizer err_msg stack_trace | ||
1352 | } | ||
1353 | args = finalizers_index - 1; | ||
1350 | } | 1354 | } |
1351 | 1355 | ||
1352 | rc = lua_pcall( L, args, 0, err_handler_index); // [err_msg {stack_trace}]? {func [, ...]}? lane_error err_msg2? | 1356 | // if no error from the main body, finlizer doesn't receive any argument, else it gets the error message and optional stack trace |
1353 | // | 1357 | rc = lua_pcall( L, args, 0, err_handler_index); // ... finalizers lane_error err_msg2? |
1354 | // LUA_ERRRUN / LUA_ERRMEM | ||
1355 | |||
1356 | if( rc != LUA_OK) | 1358 | if( rc != LUA_OK) |
1357 | { | 1359 | { |
1358 | #if ERROR_FULL_STACK | 1360 | push_stack_trace( L, rc, lua_gettop( L)); |
1359 | lua_pushlightuserdata( L, STACK_TRACE_KEY); // [err_msg {stack_trace}]? {func [, ...]}? lane_error err_msg2 STACK_TRACE_KEY | ||
1360 | lua_gettable( L, LUA_REGISTRYINDEX); // [err_msg {stack_trace}]? {func [, ...]}? lane_error err_msg2 {stack_trace2} | ||
1361 | #endif // ERROR_FULL_STACK | ||
1362 | |||
1363 | // If one finalizer fails, don't run the others. Return this | 1361 | // If one finalizer fails, don't run the others. Return this |
1364 | // as the 'real' error, replacing what we could have had (or not) | 1362 | // as the 'real' error, replacing what we could have had (or not) |
1365 | // from the actual code. | 1363 | // from the actual code. |
1366 | // | ||
1367 | break; | 1364 | break; |
1368 | } | 1365 | } |
1366 | // no error, proceed to next finalizer // ... finalizers lane_error | ||
1369 | } | 1367 | } |
1370 | 1368 | ||
1371 | // remove error handler function (if any) and finalizers table from the stack | 1369 | if( rc != LUA_OK) |
1372 | #if ERROR_FULL_STACK | 1370 | { |
1373 | lua_remove( L, err_handler_index); // [err_msg {stack_trace}]? {func [, ...]}? err_msg2 {stack_trace2} | 1371 | // ERROR_FULL_STACK accounts for the presence of lane_error on the stack |
1374 | #endif // ERROR_FULL_STACK | 1372 | int nb_err_slots = lua_gettop( L) - finalizers_index - ERROR_FULL_STACK; |
1375 | lua_remove( L, finalizers_index); // [err_msg {stack_trace}]? err_msg2 {stack_trace2} | 1373 | // a finalizer generated an error, this is what we leave of the stack |
1374 | for( n = nb_err_slots; n > 0; -- n) | ||
1375 | { | ||
1376 | lua_replace( L, n); | ||
1377 | } | ||
1378 | // leave on the stack only the error and optional stack trace produced by the error in the finalizer | ||
1379 | lua_settop( L, nb_err_slots); | ||
1380 | } | ||
1381 | else // no error from the finalizers, make sure only the original return values from the lane body remain on the stack | ||
1382 | { | ||
1383 | lua_settop( L, finalizers_index - 1); | ||
1384 | } | ||
1376 | 1385 | ||
1377 | return rc; | 1386 | return rc; |
1378 | } | 1387 | } |
@@ -1896,6 +1905,40 @@ static int lane_error( lua_State* L) | |||
1896 | } | 1905 | } |
1897 | #endif // ERROR_FULL_STACK | 1906 | #endif // ERROR_FULL_STACK |
1898 | 1907 | ||
1908 | static void push_stack_trace( lua_State* L, int rc_, int stk_base_) | ||
1909 | { | ||
1910 | // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry | ||
1911 | switch( rc_) | ||
1912 | { | ||
1913 | case LUA_OK: // no error, body return values are on the stack | ||
1914 | break; | ||
1915 | |||
1916 | case LUA_ERRRUN: // cancellation or a runtime error | ||
1917 | #if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler | ||
1918 | { | ||
1919 | // fetch the call stack table from the registry where the handler stored it | ||
1920 | STACK_GROW( L, 1); | ||
1921 | lua_pushlightuserdata( L, STACK_TRACE_KEY); // err STACK_TRACE_KEY | ||
1922 | // yields nil if no stack was generated (in case of cancellation for example) | ||
1923 | lua_gettable( L, LUA_REGISTRYINDEX); // err trace|nil | ||
1924 | |||
1925 | // For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed | ||
1926 | // For other errors, the message should be a string, and we should have a stack trace table | ||
1927 | ASSERT_L( (lua_istable( L, 1 + stk_base_) && lua_type( L, stk_base_) == LUA_TSTRING) || (lua_touserdata( L, stk_base_) == CANCEL_ERROR)); | ||
1928 | // Just leaving the stack trace table on the stack is enough to get it through to the master. | ||
1929 | break; | ||
1930 | } | ||
1931 | #endif // fall through if not ERROR_FULL_STACK | ||
1932 | |||
1933 | case LUA_ERRMEM: // memory allocation error (handler not called) | ||
1934 | case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition) | ||
1935 | default: | ||
1936 | // we should have a single value which is either a string (the error message) or CANCEL_ERROR | ||
1937 | ASSERT_L( (lua_gettop( L) == stk_base_) && ((lua_type( L, stk_base_) == LUA_TSTRING) || (lua_touserdata( L, stk_base_) == CANCEL_ERROR))); | ||
1938 | break; | ||
1939 | } | ||
1940 | } | ||
1941 | |||
1899 | LUAG_FUNC( set_debug_threadname) | 1942 | LUAG_FUNC( set_debug_threadname) |
1900 | { | 1943 | { |
1901 | // C s_lane structure is a light userdata upvalue | 1944 | // C s_lane structure is a light userdata upvalue |
@@ -1979,152 +2022,107 @@ static void thread_cleanup_handler( void* opaque) | |||
1979 | } | 2022 | } |
1980 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 2023 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1981 | 2024 | ||
1982 | //--- | ||
1983 | static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs) | 2025 | static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs) |
1984 | { | 2026 | { |
1985 | struct s_lane* s = (struct s_lane*) vs; | 2027 | struct s_lane* s = (struct s_lane*) vs; |
1986 | int rc, rc2; | 2028 | int rc, rc2; |
1987 | lua_State* L = s->L; | 2029 | lua_State* L = s->L; |
1988 | DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); | 2030 | // Called with the lane function and arguments on the stack |
2031 | int const nargs = lua_gettop( L) - 1; | ||
2032 | DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); | ||
1989 | #if HAVE_LANE_TRACKING | 2033 | #if HAVE_LANE_TRACKING |
1990 | if( s->U->tracking_first) | 2034 | if( s->U->tracking_first) |
1991 | { | 2035 | { |
1992 | tracking_add( s); | 2036 | tracking_add( s); |
1993 | } | 2037 | } |
1994 | #endif // HAVE_LANE_TRACKING | 2038 | #endif // HAVE_LANE_TRACKING |
1995 | THREAD_MAKE_ASYNCH_CANCELLABLE(); | 2039 | THREAD_MAKE_ASYNCH_CANCELLABLE(); |
1996 | THREAD_CLEANUP_PUSH( thread_cleanup_handler, s); | 2040 | THREAD_CLEANUP_PUSH( thread_cleanup_handler, s); |
1997 | s->status = RUNNING; // PENDING -> RUNNING | 2041 | s->status = RUNNING; // PENDING -> RUNNING |
1998 | 2042 | ||
1999 | // Tie "set_finalizer()" to the state | 2043 | // Tie "set_finalizer()" to the state |
2000 | // | 2044 | lua_pushcfunction( L, LG_set_finalizer); |
2001 | lua_pushcfunction( L, LG_set_finalizer ); | 2045 | populate_func_lookup_table( L, -1, "set_finalizer"); |
2002 | populate_func_lookup_table( L, -1, "set_finalizer"); | 2046 | lua_setglobal( L, "set_finalizer"); |
2003 | lua_setglobal( L, "set_finalizer"); | 2047 | |
2004 | 2048 | // Tie "set_debug_threadname()" to the state | |
2005 | // Tie "set_debug_threadname()" to the state | 2049 | // But don't register it in the lookup database because of the s_lane pointer upvalue |
2006 | // But don't register it in the lookup database because of the s_lane pointer upvalue | 2050 | lua_pushlightuserdata( L, s); |
2007 | lua_pushlightuserdata( L, s); | 2051 | lua_pushcclosure( L, LG_set_debug_threadname, 1); |
2008 | lua_pushcclosure( L, LG_set_debug_threadname, 1); | 2052 | lua_setglobal( L, "set_debug_threadname" ); |
2009 | lua_setglobal( L, "set_debug_threadname" ); | 2053 | |
2010 | 2054 | // Tie "cancel_test()" to the state | |
2011 | // Tie "cancel_test()" to the state | 2055 | lua_pushcfunction( L, LG_cancel_test); |
2012 | // | 2056 | populate_func_lookup_table( L, -1, "cancel_test"); |
2013 | lua_pushcfunction( L, LG_cancel_test); | 2057 | lua_setglobal( L, "cancel_test"); |
2014 | populate_func_lookup_table( L, -1, "cancel_test"); | ||
2015 | lua_setglobal( L, "cancel_test"); | ||
2016 | 2058 | ||
2017 | #if ERROR_FULL_STACK | 2059 | #if ERROR_FULL_STACK |
2018 | // Tie "set_error_reporting()" to the state | 2060 | // Tie "set_error_reporting()" to the state |
2019 | // | 2061 | lua_pushcfunction( L, LG_set_error_reporting); |
2020 | lua_pushcfunction( L, LG_set_error_reporting); | 2062 | populate_func_lookup_table( L, -1, "set_error_reporting"); |
2021 | populate_func_lookup_table( L, -1, "set_error_reporting"); | 2063 | lua_setglobal( L, "set_error_reporting"); |
2022 | lua_setglobal( L, "set_error_reporting"); | ||
2023 | |||
2024 | STACK_GROW( L, 1 ); | ||
2025 | lua_pushcfunction( L, lane_error); | ||
2026 | lua_insert( L, 1 ); | ||
2027 | |||
2028 | // [1]: error handler | ||
2029 | // [2]: function to run | ||
2030 | // [3..top]: parameters | ||
2031 | // | ||
2032 | rc= lua_pcall( L, lua_gettop(L)-2, LUA_MULTRET, 1 /*error handler*/ ); | ||
2033 | // 0: no error, body return values are on the stack | ||
2034 | // LUA_ERRRUN: cancellation or a runtime error (error pushed on stack) | ||
2035 | // LUA_ERRMEM: memory allocation error | ||
2036 | // LUA_ERRERR: error while running the error handler (if any) | ||
2037 | 2064 | ||
2038 | assert( rc!=LUA_ERRERR ); // since we've authored it | 2065 | STACK_GROW( L, 1); |
2039 | 2066 | lua_pushcfunction( L, lane_error); // func args handler | |
2040 | lua_remove(L,1); // remove error handler | 2067 | lua_insert( L, 1); // handler func args |
2068 | #endif // ERROR_FULL_STACK | ||
2041 | 2069 | ||
2042 | // Lua 5.1 error handler is limited to one return value; taking stack trace via registry | 2070 | rc = lua_pcall( L, nargs, LUA_MULTRET, ERROR_FULL_STACK); // retvals|err |
2043 | if( rc != LUA_OK) | ||
2044 | { | ||
2045 | STACK_GROW(L,1); | ||
2046 | lua_pushlightuserdata( L, STACK_TRACE_KEY ); | ||
2047 | lua_gettable(L, LUA_REGISTRYINDEX); // yields nil if no stack was generated (in case of cancellation for example) | ||
2048 | 2071 | ||
2049 | // For cancellation, a stack trace isn't placed | 2072 | #if ERROR_FULL_STACK |
2050 | // | 2073 | lua_remove( L, 1); // retvals|error |
2051 | assert( lua_istable(L,2) || (lua_touserdata(L,1)==CANCEL_ERROR) ); | 2074 | # endif // ERROR_FULL_STACK |
2052 | |||
2053 | // Just leaving the stack trace table on the stack is enough to get | ||
2054 | // it through to the master. | ||
2055 | } | ||
2056 | 2075 | ||
2057 | #else // ERROR_FULL_STACK == 0 | 2076 | // in case of error and if it exists, fetch stack trace from registry and push it |
2058 | // This code does not use 'lane_error' | 2077 | push_stack_trace( L, rc, 1); |
2059 | // | ||
2060 | // [1]: function to run | ||
2061 | // [2..top]: parameters | ||
2062 | // | ||
2063 | rc = lua_pcall( L, lua_gettop( L) - 1, LUA_MULTRET, 0); // no error handler | ||
2064 | // LUA_OK(0): no error | ||
2065 | // LUA_ERRRUN(2): a runtime error (error pushed on stack) | ||
2066 | // LUA_ERRMEM(4): memory allocation error | ||
2067 | #endif // ERROR_FULL_STACK | ||
2068 | 2078 | ||
2069 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name( rc), (lua_touserdata(L,1)==CANCEL_ERROR) ? "cancelled" : lua_typename( L, lua_type( L, 1)))); | 2079 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name( rc), (lua_touserdata( L, 1)==CANCEL_ERROR) ? "cancelled" : lua_typename( L, lua_type( L, 1)))); |
2070 | //STACK_DUMP(L); | 2080 | //STACK_DUMP(L); |
2071 | // Call finalizers, if the script has set them up. | 2081 | // Call finalizers, if the script has set them up. |
2072 | // | 2082 | // |
2073 | rc2 = run_finalizers( L, rc); | 2083 | rc2 = run_finalizers( L, rc); |
2074 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name( rc2))); | 2084 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name( rc2))); |
2075 | if( rc2 != LUA_OK) // Error within a finalizer! | 2085 | if( rc2 != LUA_OK) // Error within a finalizer! |
2076 | { | 2086 | { |
2077 | rc = rc2; // we're overruling the earlier script error or normal return | 2087 | // the finalizer generated an error, and left its own error message [and stack trace] on the stack |
2078 | // the finalizer generated an error, the error message [and stack trace] are pushed on the stack | 2088 | rc = rc2; // we're overruling the earlier script error or normal return |
2079 | // remove the rest so that only the error message [and stack trace] remain on the stack | 2089 | } |
2080 | #if ERROR_FULL_STACK | 2090 | s->waiting_on = NULL; // just in case |
2081 | lua_insert( L, 1); | 2091 | if( selfdestruct_remove( s)) // check and remove (under lock!) |
2082 | lua_insert( L, 1); | 2092 | { |
2083 | lua_settop( L, 2); | 2093 | // We're a free-running thread and no-one's there to clean us up. |
2084 | #else // ERROR_FULL_STACK == 0 | 2094 | // |
2085 | lua_insert( L, 1); | 2095 | lua_close( s->L); |
2086 | lua_settop( L, 1); | ||
2087 | #endif // ERROR_FULL_STACK | ||
2088 | } | ||
2089 | s->waiting_on = NULL; // just in case | ||
2090 | if( selfdestruct_remove( s)) // check and remove (under lock!) | ||
2091 | { | ||
2092 | // We're a free-running thread and no-one's there to clean us up. | ||
2093 | // | ||
2094 | lua_close( s->L); | ||
2095 | 2096 | ||
2096 | MUTEX_LOCK( &s->U->selfdestruct_cs); | 2097 | MUTEX_LOCK( &s->U->selfdestruct_cs); |
2097 | // done with lua_close(), terminal shutdown sequence may proceed | 2098 | // done with lua_close(), terminal shutdown sequence may proceed |
2098 | -- s->U->selfdestructing_count; | 2099 | -- s->U->selfdestructing_count; |
2099 | MUTEX_UNLOCK( &s->U->selfdestruct_cs); | 2100 | MUTEX_UNLOCK( &s->U->selfdestruct_cs); |
2100 | 2101 | ||
2101 | lane_cleanup( s); // s is freed at this point | 2102 | lane_cleanup( s); // s is freed at this point |
2102 | } | 2103 | } |
2103 | else | 2104 | else |
2104 | { | 2105 | { |
2105 | // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them | 2106 | // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them |
2106 | 2107 | ||
2107 | enum e_status st= | 2108 | enum e_status st = (rc == 0) ? DONE : (lua_touserdata( L, 1) == CANCEL_ERROR) ? CANCELLED : ERROR_ST; |
2108 | (rc==0) ? DONE | ||
2109 | : (lua_touserdata(L,1)==CANCEL_ERROR) ? CANCELLED | ||
2110 | : ERROR_ST; | ||
2111 | 2109 | ||
2112 | // Posix no PTHREAD_TIMEDJOIN: | 2110 | // Posix no PTHREAD_TIMEDJOIN: |
2113 | // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change | 2111 | // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change |
2114 | // | 2112 | // |
2115 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | 2113 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
2116 | MUTEX_LOCK( &s->done_lock); | 2114 | MUTEX_LOCK( &s->done_lock); |
2117 | { | 2115 | { |
2118 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 2116 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
2119 | s->status = st; | 2117 | s->status = st; |
2120 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | 2118 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
2121 | SIGNAL_ONE( &s->done_signal); // wake up master (while 's->done_lock' is on) | 2119 | SIGNAL_ONE( &s->done_signal); // wake up master (while 's->done_lock' is on) |
2122 | } | 2120 | } |
2123 | MUTEX_UNLOCK( &s->done_lock); | 2121 | MUTEX_UNLOCK( &s->done_lock); |
2124 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 2122 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
2125 | } | 2123 | } |
2126 | THREAD_CLEANUP_POP( FALSE); | 2124 | THREAD_CLEANUP_POP( FALSE); |
2127 | return 0; // ignored | 2125 | return 0; // ignored |
2128 | } | 2126 | } |
2129 | 2127 | ||
2130 | // --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required | 2128 | // --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required |
@@ -2312,11 +2310,12 @@ LUAG_FUNC( thread_new) | |||
2312 | DEBUGSPEW_CODE( -- U->debugspew_indent_depth); | 2310 | DEBUGSPEW_CODE( -- U->debugspew_indent_depth); |
2313 | } | 2311 | } |
2314 | 2312 | ||
2313 | |||
2314 | STACK_CHECK( L); | ||
2315 | STACK_CHECK( L2); | ||
2315 | ASSERT_L( lua_gettop( L2) == 0); | 2316 | ASSERT_L( lua_gettop( L2) == 0); |
2316 | 2317 | ||
2317 | // Lane main function | 2318 | // Lane main function |
2318 | // | ||
2319 | STACK_CHECK( L); | ||
2320 | if( lua_type( L, 1) == LUA_TFUNCTION) | 2319 | if( lua_type( L, 1) == LUA_TFUNCTION) |
2321 | { | 2320 | { |
2322 | int res; | 2321 | int res; |
@@ -2340,7 +2339,7 @@ LUAG_FUNC( thread_new) | |||
2340 | } | 2339 | } |
2341 | } | 2340 | } |
2342 | 2341 | ||
2343 | ASSERT_L( lua_gettop( L2) == 1); | 2342 | STACK_MID( L2, 1); |
2344 | ASSERT_L( lua_isfunction( L2, 1)); | 2343 | ASSERT_L( lua_isfunction( L2, 1)); |
2345 | 2344 | ||
2346 | // revive arguments | 2345 | // revive arguments |
@@ -2359,8 +2358,7 @@ LUAG_FUNC( thread_new) | |||
2359 | } | 2358 | } |
2360 | STACK_MID( L, 0); | 2359 | STACK_MID( L, 0); |
2361 | 2360 | ||
2362 | ASSERT_L( (uint_t)lua_gettop( L2) == 1 + args); | 2361 | STACK_END( L2, 1 + args); |
2363 | ASSERT_L( lua_isfunction( L2, 1)); | ||
2364 | 2362 | ||
2365 | // 's' is allocated from heap, not Lua, since its life span may surpass | 2363 | // 's' is allocated from heap, not Lua, since its life span may surpass |
2366 | // the handle's (if free running thread) | 2364 | // the handle's (if free running thread) |
@@ -2616,20 +2614,19 @@ LUAG_FUNC( thread_join) | |||
2616 | double wait_secs = luaL_optnumber( L, 2, -1.0); | 2614 | double wait_secs = luaL_optnumber( L, 2, -1.0); |
2617 | lua_State* L2 = s->L; | 2615 | lua_State* L2 = s->L; |
2618 | int ret; | 2616 | int ret; |
2619 | bool_t done; | 2617 | bool_t done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status); |
2620 | |||
2621 | done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status); | ||
2622 | if( !done || !L2) | 2618 | if( !done || !L2) |
2623 | { | 2619 | { |
2624 | return 0; // timeout: pushes none, leaves 'L2' alive | 2620 | return 0; // timeout: pushes none, leaves 'L2' alive |
2625 | } | 2621 | } |
2626 | 2622 | ||
2623 | STACK_CHECK( L); | ||
2627 | // Thread is DONE/ERROR_ST/CANCELLED; all ours now | 2624 | // Thread is DONE/ERROR_ST/CANCELLED; all ours now |
2628 | 2625 | ||
2629 | if( s->mstatus == KILLED) // OS thread was killed if thread_cancel was forced | 2626 | if( s->mstatus == KILLED) // OS thread was killed if thread_cancel was forced |
2630 | { | 2627 | { |
2631 | // in that case, even if the thread was killed while DONE/ERROR_ST/CANCELLED, ignore regular return values | 2628 | // in that case, even if the thread was killed while DONE/ERROR_ST/CANCELLED, ignore regular return values |
2632 | STACK_GROW( L, 1); | 2629 | STACK_GROW( L, 2); |
2633 | lua_pushnil( L); | 2630 | lua_pushnil( L); |
2634 | lua_pushliteral( L, "killed"); | 2631 | lua_pushliteral( L, "killed"); |
2635 | ret = 2; | 2632 | ret = 2; |
@@ -2654,13 +2651,17 @@ LUAG_FUNC( thread_join) | |||
2654 | break; | 2651 | break; |
2655 | 2652 | ||
2656 | case ERROR_ST: | 2653 | case ERROR_ST: |
2657 | STACK_GROW( L, 1); | ||
2658 | lua_pushnil( L); | ||
2659 | if( luaG_inter_move( U, L2, L, 1 + ERROR_FULL_STACK, eLM_LaneBody) != 0) // error message at [-2], stack trace at [-1] | ||
2660 | { | 2654 | { |
2661 | return luaL_error( L, "tried to copy unsupported types"); | 2655 | int const n = lua_gettop( L2); |
2656 | STACK_GROW( L, 3); | ||
2657 | lua_pushnil( L); | ||
2658 | // 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 ... | ||
2659 | if( luaG_inter_move( U, L2, L, n, eLM_LaneBody) != 0) // nil "err" [trace] | ||
2660 | { | ||
2661 | return luaL_error( L, "tried to copy unsupported types: %s", lua_tostring( L, -n)); | ||
2662 | } | ||
2663 | ret = 1 + n; | ||
2662 | } | 2664 | } |
2663 | ret = 2 + ERROR_FULL_STACK; | ||
2664 | break; | 2665 | break; |
2665 | 2666 | ||
2666 | case CANCELLED: | 2667 | case CANCELLED: |
@@ -2675,7 +2676,7 @@ LUAG_FUNC( thread_join) | |||
2675 | lua_close( L2); | 2676 | lua_close( L2); |
2676 | } | 2677 | } |
2677 | s->L = 0; | 2678 | s->L = 0; |
2678 | 2679 | STACK_END( L, ret); | |
2679 | return ret; | 2680 | return ret; |
2680 | } | 2681 | } |
2681 | 2682 | ||