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 /src | |
| 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
Diffstat (limited to 'src')
| -rw-r--r-- | src/lanes.c | 413 |
1 files changed, 207 insertions, 206 deletions
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 | ||
