diff options
| author | Benoit Germain <bnt period germain arrobase gmail period com> | 2014-01-20 15:34:25 +0100 |
|---|---|---|
| committer | Benoit Germain <bnt period germain arrobase gmail period com> | 2014-01-20 15:34:25 +0100 |
| commit | d92a2ca2a43c68854011e2f84ce0a75802d854be (patch) | |
| tree | e9bbd0b69f67609cdff23664d9a322e532c17a10 | |
| parent | cf9b09ff352611fb8cee3679127f3655cbe73ae7 (diff) | |
| download | lanes-d92a2ca2a43c68854011e2f84ce0a75802d854be.tar.gz lanes-d92a2ca2a43c68854011e2f84ce0a75802d854be.tar.bz2 lanes-d92a2ca2a43c68854011e2f84ce0a75802d854be.zip | |
Turn potential malicious-crafted code crashes into normal Lua errors
* bump version to 3.8.1
* new function lane:get_debug_threadname()
* Fix invalid memory accesses when fetching the name of a joined lane
with lanes:threads() (because its lua_State is closed)
* use luaL_newmetatable() to create the metatable for lane objects
* prevent malicious code from crashing by calling lane methods without
passing the lane as first argument (raise an error instead)
* set_debug_threadname() is no longer registered in the function lookup
databases because it holds a C pointer as upvalue and it might crash if
used maliciously
| -rw-r--r-- | CHANGES | 8 | ||||
| -rw-r--r-- | docs/index.html | 6 | ||||
| -rw-r--r-- | src/lanes.c | 207 | ||||
| -rw-r--r-- | tests/basic.lua | 9 |
4 files changed, 127 insertions, 103 deletions
| @@ -1,5 +1,13 @@ | |||
| 1 | CHANGES: | 1 | CHANGES: |
| 2 | 2 | ||
| 3 | CHANGE 92: BGe 20-Jan-14 | ||
| 4 | * version 3.8.1 | ||
| 5 | * new function lane:get_debug_threadname() | ||
| 6 | * Fix invalid memory accesses when fetching the name of a joined lane with lanes:threads() (because its lua_State is closed) | ||
| 7 | * use luaL_newmetatable() to create the metatable for lane objects | ||
| 8 | * prevent malicious code from crashing by calling lane methods without passing the lane as first argument (raise an error instead) | ||
| 9 | * set_debug_threadname() is no longer registered in the function lookup databases because it holds a C pointer as upvalue and it might crash if used maliciously | ||
| 10 | |||
| 3 | CHANGE 91: BGe 20-Jan-14 | 11 | CHANGE 91: BGe 20-Jan-14 |
| 4 | * version 3.8.0 | 12 | * version 3.8.0 |
| 5 | * linda:set() accepts multiple values to set in the specified slot | 13 | * linda:set() accepts multiple values to set in the specified slot |
diff --git a/docs/index.html b/docs/index.html index da37cef..7078b13 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 20-Jan-14, and applies to version <tt>3.8.0</tt>. | 73 | This document was revised on 20-Jan-14, and applies to version <tt>3.8.1</tt>. |
| 74 | </p> | 74 | </p> |
| 75 | </font> | 75 | </font> |
| 76 | </center> | 76 | </center> |
| @@ -630,7 +630,9 @@ | |||
| 630 | </table> | 630 | </table> |
| 631 | 631 | ||
| 632 | <p> | 632 | <p> |
| 633 | Each lane also gets a function <tt>set_debug_threadname()</tt> that it can use anytime to do as the name says. Supported debuggers are Microsoft Visual Studio (for the C side) and Decoda (for the Lua side). | 633 | Each lane also gets a global function <tt>set_debug_threadname()</tt> that it can use anytime to do as the name says. Supported debuggers are Microsoft Visual Studio (for the C side) and Decoda (for the Lua side). |
| 634 | <br/> | ||
| 635 | Starting with version 3.8.1, the lane has a new method <tt>lane:get_debug_threadname()</tt> that gives access to that name from the caller side (returns <tt>"<unnamed>"</tt> if unset, <tt>"<closed>"</tt> if the internal Lua state is closed). | ||
| 634 | </p> | 636 | </p> |
| 635 | 637 | ||
| 636 | <p> | 638 | <p> |
diff --git a/src/lanes.c b/src/lanes.c index f62c39f..604e43d 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
| @@ -52,7 +52,7 @@ | |||
| 52 | * ... | 52 | * ... |
| 53 | */ | 53 | */ |
| 54 | 54 | ||
| 55 | char const* VERSION = "3.8.0"; | 55 | char const* VERSION = "3.8.1"; |
| 56 | 56 | ||
| 57 | /* | 57 | /* |
| 58 | =============================================================================== | 58 | =============================================================================== |
| @@ -176,14 +176,14 @@ struct s_lane | |||
| 176 | // M: sets to NORMAL, if issued a kill changes to KILLED | 176 | // M: sets to NORMAL, if issued a kill changes to KILLED |
| 177 | // S: not used | 177 | // S: not used |
| 178 | 178 | ||
| 179 | struct s_lane * volatile selfdestruct_next; | 179 | struct s_lane* volatile selfdestruct_next; |
| 180 | // | 180 | // |
| 181 | // M: sets to non-NULL if facing lane handle '__gc' cycle but the lane | 181 | // M: sets to non-NULL if facing lane handle '__gc' cycle but the lane |
| 182 | // is still running | 182 | // is still running |
| 183 | // S: cleans up after itself if non-NULL at lane exit | 183 | // S: cleans up after itself if non-NULL at lane exit |
| 184 | 184 | ||
| 185 | #if HAVE_LANE_TRACKING | 185 | #if HAVE_LANE_TRACKING |
| 186 | struct s_lane * volatile tracking_next; | 186 | struct s_lane* volatile tracking_next; |
| 187 | #endif // HAVE_LANE_TRACKING | 187 | #endif // HAVE_LANE_TRACKING |
| 188 | // | 188 | // |
| 189 | // For tracking only | 189 | // For tracking only |
| @@ -193,10 +193,10 @@ struct s_lane | |||
| 193 | // 'struct s_lane' are malloc/free'd and the handle only carries a pointer. | 193 | // 'struct s_lane' are malloc/free'd and the handle only carries a pointer. |
| 194 | // This is not deep userdata since the handle's not portable among lanes. | 194 | // This is not deep userdata since the handle's not portable among lanes. |
| 195 | // | 195 | // |
| 196 | #define lua_toLane( L, i) (*((struct s_lane**) lua_touserdata( L, i))) | 196 | #define lua_toLane( L, i) (*((struct s_lane**) luaL_checkudata( L, i, "Lane"))) |
| 197 | 197 | ||
| 198 | #define CANCEL_TEST_KEY ((void*)get_lane) // used as registry key | 198 | #define CANCEL_TEST_KEY ((void*)get_lane_from_registry) // used as registry key |
| 199 | static inline struct s_lane* get_lane( lua_State* L) | 199 | static inline struct s_lane* get_lane_from_registry( lua_State* L) |
| 200 | { | 200 | { |
| 201 | struct s_lane* s; | 201 | struct s_lane* s; |
| 202 | STACK_GROW( L, 1); | 202 | STACK_GROW( L, 1); |
| @@ -220,7 +220,7 @@ static inline struct s_lane* get_lane( lua_State* L) | |||
| 220 | */ | 220 | */ |
| 221 | static inline enum e_cancel_request cancel_test( lua_State* L) | 221 | static inline enum e_cancel_request cancel_test( lua_State* L) |
| 222 | { | 222 | { |
| 223 | struct s_lane* const s = get_lane( L); | 223 | struct s_lane* const s = get_lane_from_registry( L); |
| 224 | // 's' is NULL for the original main state (and no-one can cancel that) | 224 | // 's' is NULL for the original main state (and no-one can cancel that) |
| 225 | return s ? s->cancel_request : CANCEL_NONE; | 225 | return s ? s->cancel_request : CANCEL_NONE; |
| 226 | } | 226 | } |
| @@ -317,7 +317,7 @@ struct s_lane* volatile tracking_first = NULL; // will change to TRACKING_END if | |||
| 317 | * Add the lane to tracking chain; the ones still running at the end of the | 317 | * Add the lane to tracking chain; the ones still running at the end of the |
| 318 | * whole process will be cancelled. | 318 | * whole process will be cancelled. |
| 319 | */ | 319 | */ |
| 320 | static void tracking_add( struct s_lane *s) | 320 | static void tracking_add( struct s_lane* s) |
| 321 | { | 321 | { |
| 322 | 322 | ||
| 323 | MUTEX_LOCK( &tracking_cs); | 323 | MUTEX_LOCK( &tracking_cs); |
| @@ -333,7 +333,7 @@ static void tracking_add( struct s_lane *s) | |||
| 333 | /* | 333 | /* |
| 334 | * A free-running lane has ended; remove it from tracking chain | 334 | * A free-running lane has ended; remove it from tracking chain |
| 335 | */ | 335 | */ |
| 336 | static bool_t tracking_remove( struct s_lane *s ) | 336 | static bool_t tracking_remove( struct s_lane* s) |
| 337 | { | 337 | { |
| 338 | bool_t found = FALSE; | 338 | bool_t found = FALSE; |
| 339 | MUTEX_LOCK( &tracking_cs); | 339 | MUTEX_LOCK( &tracking_cs); |
| @@ -344,7 +344,7 @@ static bool_t tracking_remove( struct s_lane *s ) | |||
| 344 | // | 344 | // |
| 345 | if (s->tracking_next != NULL) | 345 | if (s->tracking_next != NULL) |
| 346 | { | 346 | { |
| 347 | struct s_lane **ref= (struct s_lane **) &tracking_first; | 347 | struct s_lane** ref= (struct s_lane**) &tracking_first; |
| 348 | 348 | ||
| 349 | while( *ref != TRACKING_END) | 349 | while( *ref != TRACKING_END) |
| 350 | { | 350 | { |
| @@ -355,7 +355,7 @@ static bool_t tracking_remove( struct s_lane *s ) | |||
| 355 | found = TRUE; | 355 | found = TRUE; |
| 356 | break; | 356 | break; |
| 357 | } | 357 | } |
| 358 | ref = (struct s_lane **) &((*ref)->tracking_next); | 358 | ref = (struct s_lane**) &((*ref)->tracking_next); |
| 359 | } | 359 | } |
| 360 | assert( found); | 360 | assert( found); |
| 361 | } | 361 | } |
| @@ -502,7 +502,7 @@ LUAG_FUNC( linda_send) | |||
| 502 | 502 | ||
| 503 | { | 503 | { |
| 504 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | 504 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings |
| 505 | struct s_lane* const s = get_lane( L); | 505 | struct s_lane* const s = get_lane_from_registry( L); |
| 506 | if( s != NULL) | 506 | if( s != NULL) |
| 507 | { | 507 | { |
| 508 | cancel = s->cancel_request; // testing here causes no delays | 508 | cancel = s->cancel_request; // testing here causes no delays |
| @@ -664,7 +664,7 @@ LUAG_FUNC( linda_receive) | |||
| 664 | 664 | ||
| 665 | { | 665 | { |
| 666 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | 666 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings |
| 667 | struct s_lane* const s = get_lane( L); | 667 | struct s_lane* const s = get_lane_from_registry( L); |
| 668 | if( s != NULL) | 668 | if( s != NULL) |
| 669 | { | 669 | { |
| 670 | cancel = s->cancel_request; // testing here causes no delays | 670 | cancel = s->cancel_request; // testing here causes no delays |
| @@ -1342,7 +1342,7 @@ static MUTEX_T selfdestruct_cs; | |||
| 1342 | // | 1342 | // |
| 1343 | // Protects modifying the selfdestruct chain | 1343 | // Protects modifying the selfdestruct chain |
| 1344 | 1344 | ||
| 1345 | #define SELFDESTRUCT_END ((struct s_lane *)(-1)) | 1345 | #define SELFDESTRUCT_END ((struct s_lane*)(-1)) |
| 1346 | // | 1346 | // |
| 1347 | // The chain is ended by '(struct s_lane*)(-1)', not NULL: | 1347 | // The chain is ended by '(struct s_lane*)(-1)', not NULL: |
| 1348 | // 'selfdestruct_first -> ... -> ... -> (-1)' | 1348 | // 'selfdestruct_first -> ... -> ... -> (-1)' |
| @@ -1357,8 +1357,8 @@ int volatile selfdestructing_count = 0; | |||
| 1357 | * Add the lane to selfdestruct chain; the ones still running at the end of the | 1357 | * Add the lane to selfdestruct chain; the ones still running at the end of the |
| 1358 | * whole process will be cancelled. | 1358 | * whole process will be cancelled. |
| 1359 | */ | 1359 | */ |
| 1360 | static void selfdestruct_add( struct s_lane *s ) { | 1360 | static void selfdestruct_add( struct s_lane* s) |
| 1361 | 1361 | { | |
| 1362 | MUTEX_LOCK( &selfdestruct_cs ); | 1362 | MUTEX_LOCK( &selfdestruct_cs ); |
| 1363 | { | 1363 | { |
| 1364 | assert( s->selfdestruct_next == NULL ); | 1364 | assert( s->selfdestruct_next == NULL ); |
| @@ -1372,7 +1372,7 @@ static void selfdestruct_add( struct s_lane *s ) { | |||
| 1372 | /* | 1372 | /* |
| 1373 | * A free-running lane has ended; remove it from selfdestruct chain | 1373 | * A free-running lane has ended; remove it from selfdestruct chain |
| 1374 | */ | 1374 | */ |
| 1375 | static bool_t selfdestruct_remove( struct s_lane *s ) | 1375 | static bool_t selfdestruct_remove( struct s_lane* s) |
| 1376 | { | 1376 | { |
| 1377 | bool_t found = FALSE; | 1377 | bool_t found = FALSE; |
| 1378 | MUTEX_LOCK( &selfdestruct_cs ); | 1378 | MUTEX_LOCK( &selfdestruct_cs ); |
| @@ -1382,7 +1382,7 @@ static bool_t selfdestruct_remove( struct s_lane *s ) | |||
| 1382 | // cancel/kill). | 1382 | // cancel/kill). |
| 1383 | // | 1383 | // |
| 1384 | if (s->selfdestruct_next != NULL) { | 1384 | if (s->selfdestruct_next != NULL) { |
| 1385 | struct s_lane **ref= (struct s_lane **) &selfdestruct_first; | 1385 | struct s_lane** ref= (struct s_lane**) &selfdestruct_first; |
| 1386 | 1386 | ||
| 1387 | while( *ref != SELFDESTRUCT_END ) { | 1387 | while( *ref != SELFDESTRUCT_END ) { |
| 1388 | if (*ref == s) { | 1388 | if (*ref == s) { |
| @@ -1393,7 +1393,7 @@ static bool_t selfdestruct_remove( struct s_lane *s ) | |||
| 1393 | found= TRUE; | 1393 | found= TRUE; |
| 1394 | break; | 1394 | break; |
| 1395 | } | 1395 | } |
| 1396 | ref= (struct s_lane **) &((*ref)->selfdestruct_next); | 1396 | ref= (struct s_lane**) &((*ref)->selfdestruct_next); |
| 1397 | } | 1397 | } |
| 1398 | assert( found ); | 1398 | assert( found ); |
| 1399 | } | 1399 | } |
| @@ -1747,6 +1747,7 @@ static int lane_error( lua_State* L) | |||
| 1747 | 1747 | ||
| 1748 | LUAG_FUNC( set_debug_threadname) | 1748 | LUAG_FUNC( set_debug_threadname) |
| 1749 | { | 1749 | { |
| 1750 | // C s_lane structure is a light userdata upvalue | ||
| 1750 | struct s_lane* s = lua_touserdata( L, lua_upvalueindex( 1)); | 1751 | struct s_lane* s = lua_touserdata( L, lua_upvalueindex( 1)); |
| 1751 | luaL_checktype( L, -1, LUA_TSTRING); // "name" | 1752 | luaL_checktype( L, -1, LUA_TSTRING); // "name" |
| 1752 | // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global... | 1753 | // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global... |
| @@ -1761,6 +1762,14 @@ LUAG_FUNC( set_debug_threadname) | |||
| 1761 | return 0; | 1762 | return 0; |
| 1762 | } | 1763 | } |
| 1763 | 1764 | ||
| 1765 | LUAG_FUNC( get_debug_threadname) | ||
| 1766 | { | ||
| 1767 | struct s_lane* const s = lua_toLane( L, 1); | ||
| 1768 | luaL_argcheck( L, lua_gettop( L) == 1, 2, "too many arguments"); | ||
| 1769 | lua_pushstring( L, s->debug_name ? s->debug_name : "<unnamed>"); | ||
| 1770 | return 1; | ||
| 1771 | } | ||
| 1772 | |||
| 1764 | LUAG_FUNC( set_thread_priority) | 1773 | LUAG_FUNC( set_thread_priority) |
| 1765 | { | 1774 | { |
| 1766 | int const prio = luaL_checkint( L, 1); | 1775 | int const prio = luaL_checkint( L, 1); |
| @@ -1820,11 +1829,11 @@ static void thread_cleanup_handler( void* opaque) | |||
| 1820 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1829 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
| 1821 | 1830 | ||
| 1822 | //--- | 1831 | //--- |
| 1823 | static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | 1832 | static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs) |
| 1824 | { | 1833 | { |
| 1825 | struct s_lane *s= (struct s_lane *)vs; | 1834 | struct s_lane* s = (struct s_lane*) vs; |
| 1826 | int rc, rc2; | 1835 | int rc, rc2; |
| 1827 | lua_State*L= s->L; | 1836 | lua_State* L = s->L; |
| 1828 | #if HAVE_LANE_TRACKING | 1837 | #if HAVE_LANE_TRACKING |
| 1829 | if( tracking_first) | 1838 | if( tracking_first) |
| 1830 | { | 1839 | { |
| @@ -1833,7 +1842,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
| 1833 | #endif // HAVE_LANE_TRACKING | 1842 | #endif // HAVE_LANE_TRACKING |
| 1834 | THREAD_MAKE_ASYNCH_CANCELLABLE(); | 1843 | THREAD_MAKE_ASYNCH_CANCELLABLE(); |
| 1835 | THREAD_CLEANUP_PUSH( thread_cleanup_handler, s); | 1844 | THREAD_CLEANUP_PUSH( thread_cleanup_handler, s); |
| 1836 | s->status= RUNNING; // PENDING -> RUNNING | 1845 | s->status = RUNNING; // PENDING -> RUNNING |
| 1837 | 1846 | ||
| 1838 | // Tie "set_finalizer()" to the state | 1847 | // Tie "set_finalizer()" to the state |
| 1839 | // | 1848 | // |
| @@ -1842,10 +1851,9 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
| 1842 | lua_setglobal( L, "set_finalizer"); | 1851 | lua_setglobal( L, "set_finalizer"); |
| 1843 | 1852 | ||
| 1844 | // Tie "set_debug_threadname()" to the state | 1853 | // Tie "set_debug_threadname()" to the state |
| 1845 | // | 1854 | // But don't register it in the lookup database because of the s_lane pointer upvalue |
| 1846 | lua_pushlightuserdata( L, s); | 1855 | lua_pushlightuserdata( L, s); |
| 1847 | lua_pushcclosure( L, LG_set_debug_threadname, 1); | 1856 | lua_pushcclosure( L, LG_set_debug_threadname, 1); |
| 1848 | populate_func_lookup_table( L, -1, "set_debug_threadname"); | ||
| 1849 | lua_setglobal( L, "set_debug_threadname" ); | 1857 | lua_setglobal( L, "set_debug_threadname" ); |
| 1850 | 1858 | ||
| 1851 | // Tie "cancel_test()" to the state | 1859 | // Tie "cancel_test()" to the state |
| @@ -1862,7 +1870,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
| 1862 | lua_setglobal( L, "set_error_reporting"); | 1870 | lua_setglobal( L, "set_error_reporting"); |
| 1863 | 1871 | ||
| 1864 | STACK_GROW( L, 1 ); | 1872 | STACK_GROW( L, 1 ); |
| 1865 | lua_pushcfunction( L, lane_error ); | 1873 | lua_pushcfunction( L, lane_error); |
| 1866 | lua_insert( L, 1 ); | 1874 | lua_insert( L, 1 ); |
| 1867 | 1875 | ||
| 1868 | // [1]: error handler | 1876 | // [1]: error handler |
| @@ -1933,6 +1941,8 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
| 1933 | // | 1941 | // |
| 1934 | lua_close( s->L); | 1942 | lua_close( s->L); |
| 1935 | s->L = L = 0; | 1943 | s->L = L = 0; |
| 1944 | // debug_name is a pointer to an interned string, that no longer exists when the state is closed | ||
| 1945 | s->debug_name = "<closed>"; | ||
| 1936 | 1946 | ||
| 1937 | lane_cleanup( s); | 1947 | lane_cleanup( s); |
| 1938 | MUTEX_LOCK( &selfdestruct_cs); | 1948 | MUTEX_LOCK( &selfdestruct_cs); |
| @@ -2314,54 +2324,47 @@ LUAG_FUNC( thread_gc) | |||
| 2314 | // lane_h:cancel( [timeout] [, force [, forcekill_timeout]]) | 2324 | // lane_h:cancel( [timeout] [, force [, forcekill_timeout]]) |
| 2315 | LUAG_FUNC( thread_cancel) | 2325 | LUAG_FUNC( thread_cancel) |
| 2316 | { | 2326 | { |
| 2317 | if( lua_gettop( L) < 1 || lua_type( L, 1) != LUA_TUSERDATA) | 2327 | struct s_lane* s = lua_toLane( L, 1); |
| 2328 | double secs = 0.0; | ||
| 2329 | int force_i = 2; | ||
| 2330 | int forcekill_timeout_i = 3; | ||
| 2331 | |||
| 2332 | if( lua_isnumber( L, 2)) | ||
| 2318 | { | 2333 | { |
| 2319 | return luaL_error( L, "invalid argument #1, did you use ':' as you should?"); | 2334 | secs = lua_tonumber( L, 2); |
| 2335 | if( secs < 0.0 && lua_gettop( L) > 3) | ||
| 2336 | { | ||
| 2337 | return luaL_error( L, "can't force_kill a soft cancel"); | ||
| 2338 | } | ||
| 2339 | // negative timeout and force flag means we want to wake linda-waiting threads | ||
| 2340 | ++ force_i; | ||
| 2341 | ++ forcekill_timeout_i; | ||
| 2320 | } | 2342 | } |
| 2321 | else | 2343 | else if( lua_isnil( L, 2)) |
| 2322 | { | 2344 | { |
| 2323 | struct s_lane* s = lua_toLane( L, 1); | 2345 | ++ force_i; |
| 2324 | double secs = 0.0; | 2346 | ++ forcekill_timeout_i; |
| 2325 | int force_i = 2; | 2347 | } |
| 2326 | int forcekill_timeout_i = 3; | ||
| 2327 | 2348 | ||
| 2328 | if( lua_isnumber( L, 2)) | 2349 | { |
| 2329 | { | 2350 | bool_t force = lua_toboolean( L, force_i); // FALSE if nothing there |
| 2330 | secs = lua_tonumber( L, 2); | 2351 | double forcekill_timeout = luaL_optnumber( L, forcekill_timeout_i, 0.0); |
| 2331 | if( secs < 0.0 && lua_gettop( L) > 3) | ||
| 2332 | { | ||
| 2333 | return luaL_error( L, "can't force_kill a soft cancel"); | ||
| 2334 | } | ||
| 2335 | // negative timeout and force flag means we want to wake linda-waiting threads | ||
| 2336 | ++ force_i; | ||
| 2337 | ++ forcekill_timeout_i; | ||
| 2338 | } | ||
| 2339 | else if( lua_isnil( L, 2)) | ||
| 2340 | { | ||
| 2341 | ++ force_i; | ||
| 2342 | ++ forcekill_timeout_i; | ||
| 2343 | } | ||
| 2344 | 2352 | ||
| 2353 | switch( thread_cancel( L, s, secs, force, forcekill_timeout)) | ||
| 2345 | { | 2354 | { |
| 2346 | bool_t force = lua_toboolean( L, force_i); // FALSE if nothing there | 2355 | case CR_Timeout: |
| 2347 | double forcekill_timeout = luaL_optnumber( L, forcekill_timeout_i, 0.0); | 2356 | lua_pushboolean( L, 0); |
| 2348 | 2357 | lua_pushstring( L, "timeout"); | |
| 2349 | switch( thread_cancel( L, s, secs, force, forcekill_timeout)) | 2358 | return 2; |
| 2350 | { | ||
| 2351 | case CR_Timeout: | ||
| 2352 | lua_pushboolean( L, 0); | ||
| 2353 | lua_pushstring( L, "timeout"); | ||
| 2354 | return 2; | ||
| 2355 | 2359 | ||
| 2356 | case CR_Cancelled: | 2360 | case CR_Cancelled: |
| 2357 | lua_pushboolean( L, 1); | 2361 | lua_pushboolean( L, 1); |
| 2358 | return 1; | 2362 | return 1; |
| 2359 | 2363 | ||
| 2360 | case CR_Killed: | 2364 | case CR_Killed: |
| 2361 | lua_pushboolean( L, 0); | 2365 | lua_pushboolean( L, 0); |
| 2362 | lua_pushstring( L, "killed"); | 2366 | lua_pushstring( L, "killed"); |
| 2363 | return 2; | 2367 | return 2; |
| 2364 | } | ||
| 2365 | } | 2368 | } |
| 2366 | } | 2369 | } |
| 2367 | // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value" | 2370 | // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value" |
| @@ -2378,26 +2381,26 @@ LUAG_FUNC( thread_cancel) | |||
| 2378 | // / "error" finished at an error, error value is there | 2381 | // / "error" finished at an error, error value is there |
| 2379 | // / "cancelled" execution cancelled by M (state gone) | 2382 | // / "cancelled" execution cancelled by M (state gone) |
| 2380 | // | 2383 | // |
| 2381 | static char const * thread_status_string( struct s_lane *s) | 2384 | static char const * thread_status_string( struct s_lane* s) |
| 2382 | { | 2385 | { |
| 2383 | enum e_status st = s->status; // read just once (volatile) | 2386 | enum e_status st = s->status; // read just once (volatile) |
| 2384 | char const * str = | 2387 | char const* str = |
| 2385 | (s->mstatus == KILLED) ? "killed" : // new to v3.3.0! | 2388 | (s->mstatus == KILLED) ? "killed" : // new to v3.3.0! |
| 2386 | (st==PENDING) ? "pending" : | 2389 | (st == PENDING) ? "pending" : |
| 2387 | (st==RUNNING) ? "running" : // like in 'co.status()' | 2390 | (st == RUNNING) ? "running" : // like in 'co.status()' |
| 2388 | (st==WAITING) ? "waiting" : | 2391 | (st == WAITING) ? "waiting" : |
| 2389 | (st==DONE) ? "done" : | 2392 | (st == DONE) ? "done" : |
| 2390 | (st==ERROR_ST) ? "error" : | 2393 | (st == ERROR_ST) ? "error" : |
| 2391 | (st==CANCELLED) ? "cancelled" : NULL; | 2394 | (st == CANCELLED) ? "cancelled" : NULL; |
| 2392 | return str; | 2395 | return str; |
| 2393 | } | 2396 | } |
| 2394 | 2397 | ||
| 2395 | static int push_thread_status( lua_State*L, struct s_lane *s) | 2398 | static int push_thread_status( lua_State* L, struct s_lane* s) |
| 2396 | { | 2399 | { |
| 2397 | char const * const str = thread_status_string( s); | 2400 | char const* const str = thread_status_string( s); |
| 2398 | ASSERT_L( str); | 2401 | ASSERT_L( str); |
| 2399 | 2402 | ||
| 2400 | lua_pushstring( L, str ); | 2403 | lua_pushstring( L, str); |
| 2401 | return 1; | 2404 | return 1; |
| 2402 | } | 2405 | } |
| 2403 | 2406 | ||
| @@ -2413,8 +2416,8 @@ static int push_thread_status( lua_State*L, struct s_lane *s) | |||
| 2413 | LUAG_FUNC( thread_join) | 2416 | LUAG_FUNC( thread_join) |
| 2414 | { | 2417 | { |
| 2415 | struct s_lane* const s = lua_toLane( L, 1); | 2418 | struct s_lane* const s = lua_toLane( L, 1); |
| 2416 | double wait_secs= luaL_optnumber(L,2,-1.0); | 2419 | double wait_secs = luaL_optnumber( L, 2, -1.0); |
| 2417 | lua_State*L2= s->L; | 2420 | lua_State* L2 = s->L; |
| 2418 | int ret; | 2421 | int ret; |
| 2419 | bool_t done; | 2422 | bool_t done; |
| 2420 | 2423 | ||
| @@ -2467,6 +2470,8 @@ LUAG_FUNC( thread_join) | |||
| 2467 | ASSERT_L( FALSE ); ret= 0; | 2470 | ASSERT_L( FALSE ); ret= 0; |
| 2468 | } | 2471 | } |
| 2469 | lua_close( L2); | 2472 | lua_close( L2); |
| 2473 | // debug_name is a pointer to an interned string, that no longer exists when the state is closed | ||
| 2474 | s->debug_name = "<closed>"; | ||
| 2470 | } | 2475 | } |
| 2471 | s->L = 0; | 2476 | s->L = 0; |
| 2472 | 2477 | ||
| @@ -2487,7 +2492,7 @@ LUAG_FUNC( thread_index) | |||
| 2487 | int const UD = 1; | 2492 | int const UD = 1; |
| 2488 | int const KEY = 2; | 2493 | int const KEY = 2; |
| 2489 | int const USR = 3; | 2494 | int const USR = 3; |
| 2490 | struct s_lane *s = lua_toLane( L, UD); | 2495 | struct s_lane* const s = lua_toLane( L, UD); |
| 2491 | ASSERT_L( lua_gettop( L) == 2); | 2496 | ASSERT_L( lua_gettop( L) == 2); |
| 2492 | 2497 | ||
| 2493 | STACK_GROW( L, 8); // up to 8 positions are needed in case of error propagation | 2498 | STACK_GROW( L, 8); // up to 8 positions are needed in case of error propagation |
| @@ -2956,28 +2961,32 @@ LUAG_FUNC( configure) | |||
| 2956 | STACK_MID( L, 1); | 2961 | STACK_MID( L, 1); |
| 2957 | 2962 | ||
| 2958 | // prepare the metatable for threads | 2963 | // prepare the metatable for threads |
| 2959 | // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join } | 2964 | // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname } |
| 2960 | // | 2965 | // |
| 2961 | lua_newtable( L); // settings M mt | 2966 | if( luaL_newmetatable( L, "Lane")) // settings M mt |
| 2962 | lua_pushcfunction( L, LG_thread_gc); // settings M mt LG_thread_gc | 2967 | { |
| 2963 | lua_setfield( L, -2, "__gc"); // settings M mt | 2968 | lua_pushcfunction( L, LG_thread_gc); // settings M mt LG_thread_gc |
| 2964 | lua_pushcfunction( L, LG_thread_index); // settings M mt LG_thread_index | 2969 | lua_setfield( L, -2, "__gc"); // settings M mt |
| 2965 | lua_setfield( L, -2, "__index"); // settings M mt | 2970 | lua_pushcfunction( L, LG_thread_index); // settings M mt LG_thread_index |
| 2966 | lua_getglobal( L, "error"); // settings M mt error | 2971 | lua_setfield( L, -2, "__index"); // settings M mt |
| 2967 | ASSERT_L( lua_isfunction( L, -1)); | 2972 | lua_getglobal( L, "error"); // settings M mt error |
| 2968 | lua_setfield( L, -2, "cached_error"); // settings M mt | 2973 | ASSERT_L( lua_isfunction( L, -1)); |
| 2969 | lua_getglobal( L, "tostring"); // settings M mt tostring | 2974 | lua_setfield( L, -2, "cached_error"); // settings M mt |
| 2970 | ASSERT_L( lua_isfunction( L, -1)); | 2975 | lua_getglobal( L, "tostring"); // settings M mt tostring |
| 2971 | lua_setfield( L, -2, "cached_tostring"); // settings M mt | 2976 | ASSERT_L( lua_isfunction( L, -1)); |
| 2972 | lua_pushcfunction( L, LG_thread_join); // settings M mt LG_thread_join | 2977 | lua_setfield( L, -2, "cached_tostring"); // settings M mt |
| 2973 | lua_setfield( L, -2, "join"); // settings M mt | 2978 | lua_pushcfunction( L, LG_thread_join); // settings M mt LG_thread_join |
| 2974 | lua_pushcfunction( L, LG_thread_cancel); // settings M mt LG_thread_cancel | 2979 | lua_setfield( L, -2, "join"); // settings M mt |
| 2975 | lua_setfield( L, -2, "cancel"); // settings M mt | 2980 | lua_pushcfunction( L, LG_get_debug_threadname); // settings M mt LG_get_debug_threadname |
| 2976 | lua_pushliteral( L, "Lane"); // settings M mt "Lane" | 2981 | lua_setfield( L, -2, "get_debug_threadname"); // settings M mt |
| 2977 | lua_setfield( L, -2, "__metatable"); // settings M mt | 2982 | lua_pushcfunction( L, LG_thread_cancel); // settings M mt LG_thread_cancel |
| 2983 | lua_setfield( L, -2, "cancel"); // settings M mt | ||
| 2984 | lua_pushliteral( L, "Lane"); // settings M mt "Lane" | ||
| 2985 | lua_setfield( L, -2, "__metatable"); // settings M mt | ||
| 2986 | } | ||
| 2978 | 2987 | ||
| 2979 | lua_pushcclosure( L, LG_thread_new, 1); // settings M LG_thread_new | 2988 | lua_pushcclosure( L, LG_thread_new, 1); // settings M LG_thread_new |
| 2980 | lua_setfield(L, -2, "thread_new"); // settings M | 2989 | lua_setfield( L, -2, "thread_new"); // settings M |
| 2981 | 2990 | ||
| 2982 | // we can't register 'lanes.require' normally because we want to create an upvalued closure | 2991 | // we can't register 'lanes.require' normally because we want to create an upvalued closure |
| 2983 | lua_getglobal( L, "require"); // settings M require | 2992 | lua_getglobal( L, "require"); // settings M require |
diff --git a/tests/basic.lua b/tests/basic.lua index 1f4eb1e..5b0d8a7 100644 --- a/tests/basic.lua +++ b/tests/basic.lua | |||
| @@ -413,6 +413,8 @@ PRINT( "\n\n", "---=== :join test ===---", "\n\n") | |||
| 413 | 413 | ||
| 414 | local S= lanes_gen( "table", | 414 | local S= lanes_gen( "table", |
| 415 | function(arg) | 415 | function(arg) |
| 416 | set_debug_threadname "join test lane" | ||
| 417 | set_finalizer( function() end) | ||
| 416 | aux= {} | 418 | aux= {} |
| 417 | for i, v in ipairs(arg) do | 419 | for i, v in ipairs(arg) do |
| 418 | table.insert (aux, 1, v) | 420 | table.insert (aux, 1, v) |
| @@ -422,11 +424,14 @@ local S= lanes_gen( "table", | |||
| 422 | end ) | 424 | end ) |
| 423 | 425 | ||
| 424 | h= S { 12, 13, 14 } -- execution starts, h[1..3] will get the return values | 426 | h= S { 12, 13, 14 } -- execution starts, h[1..3] will get the return values |
| 425 | 427 | -- wait a bit so that the lane hasa chance to set its debug name | |
| 428 | linda:receive(0.5, "gloupti") | ||
| 429 | print( "joining with '" .. h:get_debug_threadname() .. "'") | ||
| 426 | local a,b,c,d= h:join() | 430 | local a,b,c,d= h:join() |
| 427 | if h.status == "error" then | 431 | if h.status == "error" then |
| 428 | print( "h error: " , a, b, c, d) | 432 | print( h:get_debug_threadname(), "error: " , a, b, c, d) |
| 429 | else | 433 | else |
| 434 | print( h:get_debug_threadname(), a,b,c,d) | ||
| 430 | assert(a==14) | 435 | assert(a==14) |
| 431 | assert(b==13) | 436 | assert(b==13) |
| 432 | assert(c==12) | 437 | assert(c==12) |
