diff options
author | Benoit Germain <b n t DOT g e r m a i n AT g m a i l DOT c o m> | 2013-10-22 19:11:57 +0200 |
---|---|---|
committer | Benoit Germain <b n t DOT g e r m a i n AT g m a i l DOT c o m> | 2013-10-22 19:11:57 +0200 |
commit | 162a8ed6d5abf8bb1e36a4f5794073f7870fd20c (patch) | |
tree | 1de1ff5e19d4cd0a34b41498194424bea61ea769 /src/lanes.c | |
parent | 44540b9335f3bbd2f6fda3e13329b28ec76b6d7a (diff) | |
download | lanes-162a8ed6d5abf8bb1e36a4f5794073f7870fd20c.tar.gz lanes-162a8ed6d5abf8bb1e36a4f5794073f7870fd20c.tar.bz2 lanes-162a8ed6d5abf8bb1e36a4f5794073f7870fd20c.zip |
errors inside finalizers generate a full stack just like any other error
Diffstat (limited to 'src/lanes.c')
-rw-r--r-- | src/lanes.c | 160 |
1 files changed, 88 insertions, 72 deletions
diff --git a/src/lanes.c b/src/lanes.c index 3a3cdf0..1c65614 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -52,7 +52,7 @@ | |||
52 | * ... | 52 | * ... |
53 | */ | 53 | */ |
54 | 54 | ||
55 | char const* VERSION = "3.7.0"; | 55 | char const* VERSION = "3.7.1"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
@@ -113,7 +113,7 @@ THE SOFTWARE. | |||
113 | * | 113 | * |
114 | * TBD: The full stack feature does not seem to work (try 'make error'). | 114 | * TBD: The full stack feature does not seem to work (try 'make error'). |
115 | */ | 115 | */ |
116 | #define ERROR_FULL_STACK | 116 | #define ERROR_FULL_STACK 1 // must be either 0 or 1 as we do some index arithmetics with it! |
117 | 117 | ||
118 | /* | 118 | /* |
119 | * Lane cancellation request modes | 119 | * Lane cancellation request modes |
@@ -195,6 +195,11 @@ static void cancel_error( lua_State*L ); | |||
195 | #define CANCEL_TEST_KEY ((void*)cancel_test) // used as registry key | 195 | #define CANCEL_TEST_KEY ((void*)cancel_test) // used as registry key |
196 | #define CANCEL_ERROR ((void*)cancel_error) // 'cancel_error' sentinel | 196 | #define CANCEL_ERROR ((void*)cancel_error) // 'cancel_error' sentinel |
197 | 197 | ||
198 | #if ERROR_FULL_STACK | ||
199 | static int lane_error( lua_State* L); | ||
200 | #define STACK_TRACE_KEY ((void*)lane_error) // used as registry key | ||
201 | #endif // ERROR_FULL_STACK | ||
202 | |||
198 | /* | 203 | /* |
199 | * registry[FINALIZER_REG_KEY] is either nil (no finalizers) or a table | 204 | * registry[FINALIZER_REG_KEY] is either nil (no finalizers) or a table |
200 | * of functions that Lanes will call after the executing 'pcall' has ended. | 205 | * of functions that Lanes will call after the executing 'pcall' has ended. |
@@ -1084,7 +1089,7 @@ LUAG_FUNC( set_finalizer ) | |||
1084 | //--- | 1089 | //--- |
1085 | // Run finalizers - if any - with the given parameters | 1090 | // Run finalizers - if any - with the given parameters |
1086 | // | 1091 | // |
1087 | // If 'rc' is nonzero, error message and stack index are available as: | 1092 | // If 'rc' is nonzero, error message and stack index (the latter only when ERROR_FULL_STACK == 1) are available as: |
1088 | // [-1]: stack trace (table) | 1093 | // [-1]: stack trace (table) |
1089 | // [-2]: error message (any type) | 1094 | // [-2]: error message (any type) |
1090 | // | 1095 | // |
@@ -1094,56 +1099,70 @@ LUAG_FUNC( set_finalizer ) | |||
1094 | // | 1099 | // |
1095 | // TBD: should we add stack trace on failing finalizer, wouldn't be hard.. | 1100 | // TBD: should we add stack trace on failing finalizer, wouldn't be hard.. |
1096 | // | 1101 | // |
1097 | static int run_finalizers( lua_State*L, int lua_rc ) | 1102 | static int run_finalizers( lua_State* L, int lua_rc) |
1098 | { | 1103 | { |
1099 | unsigned error_index, tbl_index; | 1104 | int error_index, finalizers_index; |
1100 | unsigned n; | 1105 | int n; |
1101 | int rc= 0; | 1106 | int err_handler_index = 0; |
1102 | 1107 | int rc = 0; // [err_msg {stack_trace}]? | |
1103 | if (!push_registry_table(L, FINALIZER_REG_KEY, FALSE /*don't create one*/)) | ||
1104 | return 0; // no finalizers | ||
1105 | 1108 | ||
1106 | tbl_index= lua_gettop(L); | 1109 | if( !push_registry_table( L, FINALIZER_REG_KEY, FALSE)) // [err_msg {stack_trace}]? {func [, ...]}? |
1107 | error_index= (lua_rc!=0) ? tbl_index-2 : 0; // absolute indices | 1110 | { |
1111 | return 0; // no finalizers | ||
1112 | } | ||
1108 | 1113 | ||
1109 | STACK_GROW(L,4); | 1114 | STACK_GROW( L, 5); |
1110 | 1115 | ||
1111 | // [-1]: { func [, ...] } | 1116 | finalizers_index = lua_gettop( L); |
1112 | // | ||
1113 | for( n = (unsigned int)lua_rawlen( L, -1); n > 0; -- n) | ||
1114 | { | ||
1115 | unsigned args= 0; | ||
1116 | lua_pushinteger( L,n ); | ||
1117 | lua_gettable( L, -2 ); | ||
1118 | |||
1119 | // [-1]: function | ||
1120 | // [-2]: finalizers table | ||
1121 | 1117 | ||
1122 | if (error_index) { | 1118 | #if ERROR_FULL_STACK |
1123 | lua_pushvalue( L, error_index ); | 1119 | lua_pushcfunction( L, lane_error); // [err_msg {stack_trace}]? {func [, ...]}? lane_error |
1124 | lua_pushvalue( L, error_index+1 ); // stack trace | 1120 | err_handler_index = lua_gettop( L); |
1125 | args= 2; | 1121 | #endif // ERROR_FULL_STACK |
1126 | } | 1122 | error_index = (lua_rc != LUA_OK) ? finalizers_index - (1 + ERROR_FULL_STACK) : 0; |
1127 | 1123 | ||
1128 | rc= lua_pcall( L, args, 0 /*retvals*/, 0 /*no errfunc*/ ); | 1124 | for( n = lua_rawlen( L, finalizers_index); n > 0; -- n) |
1129 | // | 1125 | { |
1130 | // LUA_ERRRUN / LUA_ERRMEM | 1126 | int args = 0; |
1131 | 1127 | lua_pushinteger( L, n); // [err_msg {stack_trace}]? {func [, ...]}? lane_error n | |
1132 | if( rc != LUA_OK) | 1128 | lua_gettable( L, finalizers_index); // [err_msg {stack_trace}]? {func [, ...]}? lane_error finalizer |
1133 | { | 1129 | ASSERT_L( lua_isfunction( L, -1)); |
1134 | // [-1]: error message | 1130 | if( error_index) |
1135 | // | 1131 | { |
1136 | // If one finalizer fails, don't run the others. Return this | 1132 | char const* err_msg = lua_tostring( L, error_index); |
1137 | // as the 'real' error, preceding that we could have had (or not) | 1133 | lua_pushvalue( L, error_index); // [err_msg {stack_trace}]? {func [, ...]}? lane_error finalizer err_msg |
1138 | // from the actual code. | 1134 | #if ERROR_FULL_STACK |
1139 | // | 1135 | lua_pushvalue( L, error_index + 1); // [err_msg {stack_trace}]? {func [, ...]}? lane_error finalizer err_msg {stack_trace} |
1140 | break; | 1136 | #endif // ERROR_FULL_STACK |
1141 | } | 1137 | args = 1 + ERROR_FULL_STACK; |
1142 | } | 1138 | } |
1143 | 1139 | ||
1144 | lua_remove(L,tbl_index); // take finalizer table out of stack | 1140 | rc = lua_pcall( L, args, 0, err_handler_index); // [err_msg {stack_trace}]? {func [, ...]}? lane_error err_msg2? |
1141 | // | ||
1142 | // LUA_ERRRUN / LUA_ERRMEM | ||
1143 | |||
1144 | if( rc != LUA_OK) | ||
1145 | { | ||
1146 | #if ERROR_FULL_STACK | ||
1147 | lua_pushlightuserdata( L, STACK_TRACE_KEY); // [err_msg {stack_trace}]? {func [, ...]}? lane_error err_msg2 STACK_TRACE_KEY | ||
1148 | lua_gettable( L, LUA_REGISTRYINDEX); // [err_msg {stack_trace}]? {func [, ...]}? lane_error err_msg2 {stack_trace2} | ||
1149 | #endif // ERROR_FULL_STACK | ||
1145 | 1150 | ||
1146 | return rc; | 1151 | // If one finalizer fails, don't run the others. Return this |
1152 | // as the 'real' error, replacing what we could have had (or not) | ||
1153 | // from the actual code. | ||
1154 | // | ||
1155 | break; | ||
1156 | } | ||
1157 | } | ||
1158 | |||
1159 | // remove error handler function (if any) and finalizers table from the stack | ||
1160 | #if ERROR_FULL_STACK | ||
1161 | lua_remove( L, err_handler_index); // [err_msg {stack_trace}]? {func [, ...]}? err_msg2 {stack_trace2} | ||
1162 | #endif // ERROR_FULL_STACK | ||
1163 | lua_remove( L, finalizers_index); // [err_msg {stack_trace}]? err_msg2 {stack_trace2} | ||
1164 | |||
1165 | return rc; | ||
1147 | } | 1166 | } |
1148 | 1167 | ||
1149 | /* | 1168 | /* |
@@ -1577,9 +1596,8 @@ LUAG_FUNC( set_singlethreaded) | |||
1577 | * implement a Lanes-specific 'pcall' of our own that does this). TBD!!! :) | 1596 | * implement a Lanes-specific 'pcall' of our own that does this). TBD!!! :) |
1578 | * --AKa 22-Jan-2009 | 1597 | * --AKa 22-Jan-2009 |
1579 | */ | 1598 | */ |
1580 | #ifdef ERROR_FULL_STACK | 1599 | #if ERROR_FULL_STACK |
1581 | 1600 | ||
1582 | # define STACK_TRACE_KEY ((void*)lane_error) // used as registry key | ||
1583 | # define EXTENDED_STACK_TRACE_KEY ((void*)LG_set_error_reporting) // used as registry key | 1601 | # define EXTENDED_STACK_TRACE_KEY ((void*)LG_set_error_reporting) // used as registry key |
1584 | 1602 | ||
1585 | LUAG_FUNC( set_error_reporting) | 1603 | LUAG_FUNC( set_error_reporting) |
@@ -1619,10 +1637,10 @@ static int lane_error( lua_State* L) | |||
1619 | 1637 | ||
1620 | // Don't do stack survey for cancelled lanes. | 1638 | // Don't do stack survey for cancelled lanes. |
1621 | // | 1639 | // |
1622 | #if 1 | ||
1623 | if( lua_touserdata( L, 1) == CANCEL_ERROR) | 1640 | if( lua_touserdata( L, 1) == CANCEL_ERROR) |
1641 | { | ||
1624 | return 1; // just pass on | 1642 | return 1; // just pass on |
1625 | #endif | 1643 | } |
1626 | 1644 | ||
1627 | lua_pushlightuserdata( L, EXTENDED_STACK_TRACE_KEY); | 1645 | lua_pushlightuserdata( L, EXTENDED_STACK_TRACE_KEY); |
1628 | lua_gettable( L, LUA_REGISTRYINDEX); | 1646 | lua_gettable( L, LUA_REGISTRYINDEX); |
@@ -1681,7 +1699,7 @@ static int lane_error( lua_State* L) | |||
1681 | } | 1699 | } |
1682 | 1700 | ||
1683 | lua_pushlightuserdata( L, STACK_TRACE_KEY); | 1701 | lua_pushlightuserdata( L, STACK_TRACE_KEY); |
1684 | lua_insert( L ,-2); | 1702 | lua_insert( L, -2); |
1685 | lua_settable( L, LUA_REGISTRYINDEX); | 1703 | lua_settable( L, LUA_REGISTRYINDEX); |
1686 | 1704 | ||
1687 | assert( lua_gettop( L) == 1); | 1705 | assert( lua_gettop( L) == 1); |
@@ -1771,7 +1789,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1771 | lua_pushcfunction( L, LG_cancel_test); | 1789 | lua_pushcfunction( L, LG_cancel_test); |
1772 | lua_setglobal( L, "cancel_test"); | 1790 | lua_setglobal( L, "cancel_test"); |
1773 | 1791 | ||
1774 | #ifdef ERROR_FULL_STACK | 1792 | #if ERROR_FULL_STACK |
1775 | // Tie "set_error_reporting()" to the state | 1793 | // Tie "set_error_reporting()" to the state |
1776 | // | 1794 | // |
1777 | lua_pushcfunction( L, LG_set_error_reporting); | 1795 | lua_pushcfunction( L, LG_set_error_reporting); |
@@ -1810,17 +1828,17 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1810 | // it through to the master. | 1828 | // it through to the master. |
1811 | } | 1829 | } |
1812 | 1830 | ||
1813 | #else | 1831 | #else // ERROR_FULL_STACK == 0 |
1814 | // This code does not use 'lane_error' | 1832 | // This code does not use 'lane_error' |
1815 | // | 1833 | // |
1816 | // [1]: function to run | 1834 | // [1]: function to run |
1817 | // [2..top]: parameters | 1835 | // [2..top]: parameters |
1818 | // | 1836 | // |
1819 | rc= lua_pcall( L, lua_gettop(L)-1, LUA_MULTRET, 0 /*no error handler*/ ); | 1837 | rc = lua_pcall( L, lua_gettop( L) - 1, LUA_MULTRET, 0); // no error handler |
1820 | // LUA_OK(0): no error | 1838 | // LUA_OK(0): no error |
1821 | // LUA_ERRRUN(2): a runtime error (error pushed on stack) | 1839 | // LUA_ERRRUN(2): a runtime error (error pushed on stack) |
1822 | // LUA_ERRMEM(4): memory allocation error | 1840 | // LUA_ERRMEM(4): memory allocation error |
1823 | #endif | 1841 | #endif // ERROR_FULL_STACK |
1824 | 1842 | ||
1825 | 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)))); | 1843 | 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)))); |
1826 | //STACK_DUMP(L); | 1844 | //STACK_DUMP(L); |
@@ -1828,21 +1846,19 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1828 | // | 1846 | // |
1829 | rc2 = run_finalizers( L, rc); | 1847 | rc2 = run_finalizers( L, rc); |
1830 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name( rc2))); | 1848 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name( rc2))); |
1831 | if( rc2 != LUA_OK) | 1849 | if( rc2 != LUA_OK) // Error within a finalizer! |
1832 | { | 1850 | { |
1833 | // Error within a finalizer! | 1851 | rc = rc2; // we're overruling the earlier script error or normal return |
1834 | // | 1852 | // the finalizer generated an error, the error message [and stack trace] are pushed on the stack |
1835 | // [-1]: error message | 1853 | // remove the rest so that only the error message [and stack trace] remain on the stack |
1836 | 1854 | #if ERROR_FULL_STACK | |
1837 | rc= rc2; // we're overruling the earlier script error or normal return | 1855 | lua_insert( L, 1); |
1838 | 1856 | lua_insert( L, 1); | |
1839 | lua_insert( L,1 ); // make error message [1] | 1857 | lua_settop( L, 2); |
1840 | lua_settop( L,1 ); // remove all rest | 1858 | #else // ERROR_FULL_STACK == 0 |
1841 | 1859 | lua_insert( L, 1); | |
1842 | // Place an empty stack table just to keep the API simple (always when | 1860 | lua_settop( L, 1); |
1843 | // there's an error, there's also stack table - though it may be empty). | 1861 | #endif // ERROR_FULL_STACK |
1844 | // | ||
1845 | lua_newtable(L); | ||
1846 | } | 1862 | } |
1847 | s->waiting_on = NULL; // just in case | 1863 | s->waiting_on = NULL; // just in case |
1848 | if( selfdestruct_remove( s)) // check and remove (under lock!) | 1864 | if( selfdestruct_remove( s)) // check and remove (under lock!) |
@@ -2317,7 +2333,7 @@ static int push_thread_status( lua_State*L, struct s_lane *s) | |||
2317 | // | 2333 | // |
2318 | // timeout: returns nil | 2334 | // timeout: returns nil |
2319 | // done: returns return values (0..N) | 2335 | // done: returns return values (0..N) |
2320 | // error: returns nil + error value + stack table | 2336 | // error: returns nil + error value [+ stack table] |
2321 | // cancelled: returns nil | 2337 | // cancelled: returns nil |
2322 | // | 2338 | // |
2323 | LUAG_FUNC( thread_join) | 2339 | LUAG_FUNC( thread_join) |
@@ -2361,11 +2377,11 @@ LUAG_FUNC( thread_join) | |||
2361 | 2377 | ||
2362 | case ERROR_ST: | 2378 | case ERROR_ST: |
2363 | lua_pushnil( L); | 2379 | lua_pushnil( L); |
2364 | if( luaG_inter_move( L2, L, 2, eLM_LaneBody) != 0) // error message at [-2], stack trace at [-1] | 2380 | if( luaG_inter_move( L2, L, 1 + ERROR_FULL_STACK, eLM_LaneBody) != 0) // error message at [-2], stack trace at [-1] |
2365 | { | 2381 | { |
2366 | return luaL_error( L, "tried to copy unsupported types"); | 2382 | return luaL_error( L, "tried to copy unsupported types"); |
2367 | } | 2383 | } |
2368 | ret= 3; | 2384 | ret = 2 + ERROR_FULL_STACK; |
2369 | break; | 2385 | break; |
2370 | 2386 | ||
2371 | case CANCELLED: | 2387 | case CANCELLED: |