aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <bnt period germain arrobase gmail period com>2014-01-22 12:28:45 +0100
committerBenoit Germain <bnt period germain arrobase gmail period com>2014-01-22 12:28:45 +0100
commit9808ae3e21ac812ef705a7c1a0b10f49825023c5 (patch)
treee5cebe59b603645fa1fdedd42f56a4d243e8783d /src
parent57ccc40847716069053a68e9d6079355dd5a1795 (diff)
downloadlanes-9808ae3e21ac812ef705a7c1a0b10f49825023c5.tar.gz
lanes-9808ae3e21ac812ef705a7c1a0b10f49825023c5.tar.bz2
lanes-9808ae3e21ac812ef705a7c1a0b10f49825023c5.zip
new lane launcher option gc_cb
* bumped version to 3.8.2 * new lane launcher option gc_cb to set a callback that is invoked when a lane is garbage collected * Fix more invalid memory accesses when fetching the name of a joined lane with lanes:threads() (because its lua_State is closed)
Diffstat (limited to 'src')
-rw-r--r--src/lanes.c111
-rw-r--r--src/lanes.lua14
2 files changed, 93 insertions, 32 deletions
diff --git a/src/lanes.c b/src/lanes.c
index 604e43d..a806c16 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -52,7 +52,7 @@
52 * ... 52 * ...
53 */ 53 */
54 54
55char const* VERSION = "3.8.1"; 55char const* VERSION = "3.8.2";
56 56
57/* 57/*
58=============================================================================== 58===============================================================================
@@ -209,6 +209,19 @@ static inline struct s_lane* get_lane_from_registry( lua_State* L)
209 return s; 209 return s;
210} 210}
211 211
212// intern the debug name in the specified lua state so that the pointer remains valid when the lane's state is closed
213static void securize_debug_threadname( lua_State* L, struct s_lane* s)
214{
215 STACK_CHECK( L);
216 STACK_GROW( L, 3);
217 lua_getuservalue( L, 1);
218 lua_newtable( L);
219 s->debug_name = lua_pushstring( L, s->debug_name);
220 lua_rawset( L, -3);
221 lua_pop( L, 1);
222 STACK_END( L, 0);
223}
224
212/* 225/*
213* Check if the thread in question ('L') has been signalled for cancel. 226* Check if the thread in question ('L') has been signalled for cancel.
214* 227*
@@ -1766,7 +1779,7 @@ LUAG_FUNC( get_debug_threadname)
1766{ 1779{
1767 struct s_lane* const s = lua_toLane( L, 1); 1780 struct s_lane* const s = lua_toLane( L, 1);
1768 luaL_argcheck( L, lua_gettop( L) == 1, 2, "too many arguments"); 1781 luaL_argcheck( L, lua_gettop( L) == 1, 2, "too many arguments");
1769 lua_pushstring( L, s->debug_name ? s->debug_name : "<unnamed>"); 1782 lua_pushstring( L, s->debug_name);
1770 return 1; 1783 return 1;
1771} 1784}
1772 1785
@@ -1998,6 +2011,8 @@ LUAG_FUNC( require)
1998 return 1; 2011 return 1;
1999} 2012}
2000 2013
2014LUAG_FUNC( thread_gc);
2015#define GCCB_KEY (void*)LG_thread_gc
2001//--- 2016//---
2002// lane_ud= thread_new( function, [libs_str], 2017// lane_ud= thread_new( function, [libs_str],
2003// [cancelstep_uint=0], 2018// [cancelstep_uint=0],
@@ -2005,6 +2020,7 @@ LUAG_FUNC( require)
2005// [globals_tbl], 2020// [globals_tbl],
2006// [package_tbl], 2021// [package_tbl],
2007// [required], 2022// [required],
2023// [gc_cb],
2008// [... args ...] ) 2024// [... args ...] )
2009// 2025//
2010// Upvalues: metatable to use for 'lane_ud' 2026// Upvalues: metatable to use for 'lane_ud'
@@ -2022,8 +2038,9 @@ LUAG_FUNC( thread_new)
2022 uint_t glob = lua_isnoneornil( L, 5) ? 0 : 5; 2038 uint_t glob = lua_isnoneornil( L, 5) ? 0 : 5;
2023 uint_t package = lua_isnoneornil( L, 6) ? 0 : 6; 2039 uint_t package = lua_isnoneornil( L, 6) ? 0 : 6;
2024 uint_t required = lua_isnoneornil( L, 7) ? 0 : 7; 2040 uint_t required = lua_isnoneornil( L, 7) ? 0 : 7;
2041 uint_t gc_cb = lua_isnoneornil( L, 8) ? 0 : 8;
2025 2042
2026#define FIXED_ARGS 7 2043#define FIXED_ARGS 8
2027 uint_t args = lua_gettop(L) - FIXED_ARGS; 2044 uint_t args = lua_gettop(L) - FIXED_ARGS;
2028 2045
2029 // public Lanes API accepts a generic range -3/+3 2046 // public Lanes API accepts a generic range -3/+3
@@ -2203,7 +2220,7 @@ LUAG_FUNC( thread_new)
2203 } 2220 }
2204 STACK_MID( L, 0); 2221 STACK_MID( L, 0);
2205 2222
2206 ASSERT_L( (uint_t)lua_gettop( L2) == 1+args); 2223 ASSERT_L( (uint_t)lua_gettop( L2) == 1 + args);
2207 ASSERT_L( lua_isfunction( L2, 1)); 2224 ASSERT_L( lua_isfunction( L2, 1));
2208 2225
2209 // 's' is allocated from heap, not Lua, since its life span may surpass 2226 // 's' is allocated from heap, not Lua, since its life span may surpass
@@ -2217,9 +2234,9 @@ LUAG_FUNC( thread_new)
2217 2234
2218 //memset( s, 0, sizeof(struct s_lane) ); 2235 //memset( s, 0, sizeof(struct s_lane) );
2219 s->L = L2; 2236 s->L = L2;
2220 s->status= PENDING; 2237 s->status = PENDING;
2221 s->waiting_on = NULL; 2238 s->waiting_on = NULL;
2222 s->debug_name = NULL; 2239 s->debug_name = "<unnamed>";
2223 s->cancel_request = CANCEL_NONE; 2240 s->cancel_request = CANCEL_NONE;
2224 2241
2225#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 2242#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
@@ -2238,25 +2255,32 @@ LUAG_FUNC( thread_new)
2238 lua_setmetatable( L, -2); 2255 lua_setmetatable( L, -2);
2239 STACK_MID( L, 1); 2256 STACK_MID( L, 1);
2240 2257
2241 // Clear environment for the userdata 2258 // Create uservalue for the userdata
2242 // 2259 // (this is where lane body return values will be stored when the handle is indexed by a numeric key)
2243 lua_newtable( L); 2260 lua_newtable( L);
2261
2262 // Store the gc_cb callback in the uservalue
2263 if( gc_cb > 0)
2264 {
2265 lua_pushlightuserdata( L, GCCB_KEY);
2266 lua_pushvalue( L, gc_cb);
2267 lua_rawset( L, -3);
2268 }
2269
2244 lua_setuservalue( L, -2); 2270 lua_setuservalue( L, -2);
2245 2271
2246 // Place 's' in registry, for 'cancel_test()' (even if 'cs'==0 we still 2272 // Store 's' in the lane's registry, for 'cancel_test()' (even if 'cs'==0 we still do cancel tests at pending send/receive).
2247 // do cancel tests at pending send/receive).
2248 //
2249 lua_pushlightuserdata( L2, CANCEL_TEST_KEY); 2273 lua_pushlightuserdata( L2, CANCEL_TEST_KEY);
2250 lua_pushlightuserdata( L2, s); 2274 lua_pushlightuserdata( L2, s);
2251 lua_rawset( L2, LUA_REGISTRYINDEX); 2275 lua_rawset( L2, LUA_REGISTRYINDEX);
2252 2276
2253 if( cs) 2277 if( cs)
2254 { 2278 {
2255 lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cs ); 2279 lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cs);
2256 } 2280 }
2257 2281
2258 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: launching thread\n" INDENT_END)); 2282 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: launching thread\n" INDENT_END));
2259 THREAD_CREATE( &s->thread, lane_main, s, prio ); 2283 THREAD_CREATE( &s->thread, lane_main, s, prio);
2260 STACK_END( L, 1); 2284 STACK_END( L, 1);
2261 2285
2262 DEBUGSPEW_CODE( -- debugspew_indent_depth); 2286 DEBUGSPEW_CODE( -- debugspew_indent_depth);
@@ -2279,7 +2303,23 @@ LUAG_FUNC( thread_new)
2279// 2303//
2280LUAG_FUNC( thread_gc) 2304LUAG_FUNC( thread_gc)
2281{ 2305{
2282 struct s_lane* s = lua_toLane( L, 1); 2306 bool_t have_gc_cb = FALSE;
2307 struct s_lane* s = lua_toLane( L, 1); // ud
2308
2309 // if there a gc callback?
2310 lua_getuservalue( L, 1); // ud uservalue
2311 lua_pushlightuserdata( L, GCCB_KEY); // ud uservalue __gc
2312 lua_rawget( L, -2); // ud uservalue gc_cb|nil
2313 if( !lua_isnil( L, -1))
2314 {
2315 lua_remove( L, -2); // ud gc_cb|nil
2316 lua_pushstring( L, s->debug_name); // ud gc_cb name
2317 have_gc_cb = TRUE;
2318 }
2319 else
2320 {
2321 lua_pop( L, 2); // ud
2322 }
2283 2323
2284 // We can read 's->status' without locks, but not wait for it 2324 // We can read 's->status' without locks, but not wait for it
2285 // test KILLED state first, as it doesn't need to enter the selfdestruct chain 2325 // test KILLED state first, as it doesn't need to enter the selfdestruct chain
@@ -2291,13 +2331,17 @@ LUAG_FUNC( thread_gc)
2291 DEBUGSPEW_CODE( fprintf( stderr, "** Joining with a killed thread (needs testing) **")); 2331 DEBUGSPEW_CODE( fprintf( stderr, "** Joining with a killed thread (needs testing) **"));
2292 // make sure the thread is no longer running, just like thread_join() 2332 // make sure the thread is no longer running, just like thread_join()
2293 if(! THREAD_ISNULL( s->thread)) 2333 if(! THREAD_ISNULL( s->thread))
2334 {
2294 THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status); 2335 THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status);
2295 // we know the thread was killed while the Lua VM was not doing anything: we should be able to close it without crashing 2336 }
2296 // now, thread_cancel() will not forcefully kill a lane with s->status >= DONE, so I am not sure it can ever happen
2297 if( s->status >= DONE && s->L) 2337 if( s->status >= DONE && s->L)
2298 { 2338 {
2339 // we know the thread was killed while the Lua VM was not doing anything: we should be able to close it without crashing
2340 // now, thread_cancel() will not forcefully kill a lane with s->status >= DONE, so I am not sure it can ever happen
2299 lua_close( s->L); 2341 lua_close( s->L);
2300 s->L = 0; 2342 s->L = 0;
2343 // just in case, but s will be freed soon so...
2344 s->debug_name = "<gc>";
2301 } 2345 }
2302 DEBUGSPEW_CODE( fprintf( stderr, "** Joined ok **")); 2346 DEBUGSPEW_CODE( fprintf( stderr, "** Joined ok **"));
2303 } 2347 }
@@ -2306,18 +2350,31 @@ LUAG_FUNC( thread_gc)
2306 // still running: will have to be cleaned up later 2350 // still running: will have to be cleaned up later
2307 selfdestruct_add( s); 2351 selfdestruct_add( s);
2308 assert( s->selfdestruct_next); 2352 assert( s->selfdestruct_next);
2353 if( have_gc_cb)
2354 {
2355 lua_pushliteral( L, "selfdestruct"); // ud gc_cb name status
2356 lua_call( L, 2, 0); // ud
2357 }
2309 return 0; 2358 return 0;
2310
2311 } 2359 }
2312 else if( s->L) 2360 else if( s->L)
2313 { 2361 {
2314 // no longer accessing the Lua VM: we can close right now 2362 // no longer accessing the Lua VM: we can close right now
2315 lua_close( s->L); 2363 lua_close( s->L);
2316 s->L = 0; 2364 s->L = 0;
2365 // just in case, but s will be freed soon so...
2366 s->debug_name = "<gc>";
2317 } 2367 }
2318 2368
2319 // Clean up after a (finished) thread 2369 // Clean up after a (finished) thread
2320 lane_cleanup( s); 2370 lane_cleanup( s);
2371
2372 // do this after lane cleanup in case the callback triggers an error
2373 if( have_gc_cb)
2374 {
2375 lua_pushliteral( L, "closed"); // ud gc_cb name status
2376 lua_call( L, 2, 0); // ud
2377 }
2321 return 0; 2378 return 0;
2322} 2379}
2323 2380
@@ -2422,23 +2479,26 @@ LUAG_FUNC( thread_join)
2422 bool_t done; 2479 bool_t done;
2423 2480
2424 done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status); 2481 done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status);
2425 if (!done || !L2) 2482 if( !done || !L2)
2483 {
2426 return 0; // timeout: pushes none, leaves 'L2' alive 2484 return 0; // timeout: pushes none, leaves 'L2' alive
2485 }
2427 2486
2428 // Thread is DONE/ERROR_ST/CANCELLED; all ours now 2487 // Thread is DONE/ERROR_ST/CANCELLED; all ours now
2429 2488
2430 STACK_GROW( L, 1);
2431
2432 if( s->mstatus == KILLED) // OS thread was killed if thread_cancel was forced 2489 if( s->mstatus == KILLED) // OS thread was killed if thread_cancel was forced
2433 { 2490 {
2434 // in that case, even if the thread was killed while DONE/ERROR_ST/CANCELLED, ignore regular return values 2491 // in that case, even if the thread was killed while DONE/ERROR_ST/CANCELLED, ignore regular return values
2435 2492 STACK_GROW( L, 1);
2436 lua_pushnil( L); 2493 lua_pushnil( L);
2437 lua_pushliteral( L, "killed"); 2494 lua_pushliteral( L, "killed");
2438 ret = 2; 2495 ret = 2;
2439 } 2496 }
2440 else 2497 else
2441 { 2498 {
2499 // debug_name is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed
2500 // so store it in the userdata uservalue at a key that can't possibly collide
2501 securize_debug_threadname( L, s);
2442 switch( s->status) 2502 switch( s->status)
2443 { 2503 {
2444 case DONE: 2504 case DONE:
@@ -2467,11 +2527,9 @@ LUAG_FUNC( thread_join)
2467 2527
2468 default: 2528 default:
2469 DEBUGSPEW_CODE( fprintf( stderr, "Status: %d\n", s->status)); 2529 DEBUGSPEW_CODE( fprintf( stderr, "Status: %d\n", s->status));
2470 ASSERT_L( FALSE ); ret= 0; 2530 ASSERT_L( FALSE); ret = 0;
2471 } 2531 }
2472 lua_close( L2); 2532 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>";
2475 } 2533 }
2476 s->L = 0; 2534 s->L = 0;
2477 2535
@@ -2652,10 +2710,7 @@ LUAG_FUNC( threads)
2652 lua_newtable( L); // {} 2710 lua_newtable( L); // {}
2653 while( s != TRACKING_END) 2711 while( s != TRACKING_END)
2654 { 2712 {
2655 if( s->debug_name) 2713 lua_pushstring( L, s->debug_name); // {} "name"
2656 lua_pushstring( L, s->debug_name); // {} "name"
2657 else
2658 lua_pushfstring( L, "Lane %p", s); // {} "name"
2659 push_thread_status( L, s); // {} "name" "status" 2714 push_thread_status( L, s); // {} "name" "status"
2660 lua_rawset( L, -3); // {} 2715 lua_rawset( L, -3); // {}
2661 s = s->tracking_next; 2716 s = s->tracking_next;
diff --git a/src/lanes.lua b/src/lanes.lua
index 9a0287d..1286099 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -196,7 +196,10 @@ end
196-- 196--
197-- .globals: table of globals to set for a new thread (passed by value) 197-- .globals: table of globals to set for a new thread (passed by value)
198-- 198--
199-- .required: table of packages to require 199-- .required: table of packages to require
200--
201-- .gc_cb: function called when the lane handle is collected
202--
200-- ... (more options may be introduced later) ... 203-- ... (more options may be introduced later) ...
201-- 204--
202-- Calling with a function parameter ('lane_func') ends the string/table 205-- Calling with a function parameter ('lane_func') ends the string/table
@@ -272,10 +275,11 @@ local function gen( ... )
272 end 275 end
273 end 276 end
274 277
275 local prio, cs, g_tbl, package_tbl, required 278 local prio, cs, g_tbl, package_tbl, required, gc_cb
276 279
277 for k,v in pairs(opt) do 280 for k,v in pairs(opt) do
278 if k=="priority" then prio= v 281 if k == "priority" then
282 prio = (type( v) == "number") and v or error( "Bad 'prio' option: expecting number, got " .. type( v), lev)
279 elseif k=="cancelstep" then 283 elseif k=="cancelstep" then
280 cs = (v==true) and 100 or 284 cs = (v==true) and 100 or
281 (v==false) and 0 or 285 (v==false) and 0 or
@@ -286,6 +290,8 @@ local function gen( ... )
286 package_tbl = (type( v) == "table") and v or error( "Bad package: " .. tostring( v), lev) 290 package_tbl = (type( v) == "table") and v or error( "Bad package: " .. tostring( v), lev)
287 elseif k=="required" then 291 elseif k=="required" then
288 required= (type( v) == "table") and v or error( "Bad 'required' option: expecting table, got " .. type( v), lev) 292 required= (type( v) == "table") and v or error( "Bad 'required' option: expecting table, got " .. type( v), lev)
293 elseif k == "gc_cb" then
294 gc_cb = (type( v) == "function") and v or error( "Bad 'gc_cb' option: expecting function, got " .. type( v), lev)
289 --.. 295 --..
290 elseif k==1 then error( "unkeyed option: ".. tostring(v), lev ) 296 elseif k==1 then error( "unkeyed option: ".. tostring(v), lev )
291 else error( "Bad option: ".. tostring(k), lev ) 297 else error( "Bad option: ".. tostring(k), lev )
@@ -296,7 +302,7 @@ local function gen( ... )
296 -- Lane generator 302 -- Lane generator
297 -- 303 --
298 return function(...) 304 return function(...)
299 return thread_new( func, libs, cs, prio, g_tbl, package_tbl, required, ...) -- args 305 return thread_new( func, libs, cs, prio, g_tbl, package_tbl, required, gc_cb, ...) -- args
300 end 306 end
301end 307end
302 308