diff options
-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) |