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-11-29 20:33:46 +0100 |
---|---|---|
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-11-29 20:33:46 +0100 |
commit | ff74281fc94cda26b2d0a7fc2424f24fe2488718 (patch) | |
tree | 6e8aa44d300ccd5da451d02c08cee98090328b60 /src | |
parent | b4cd90c9e9a7898775d173df2267d1860dcbc699 (diff) | |
download | lanes-ff74281fc94cda26b2d0a7fc2424f24fe2488718.tar.gz lanes-ff74281fc94cda26b2d0a7fc2424f24fe2488718.tar.bz2 lanes-ff74281fc94cda26b2d0a7fc2424f24fe2488718.zip |
supposedly improved pthread support
* bumped version to 3.7.3
* set pthread thread cancel type to PTHREAD_CANCEL_ASYNCHRONOUS
* lane_h:cancel() accepts a 3rd timeout argument used when waiting for
actual thread termination (hitting the timeout raises an error)
* added PROPAGATE_ALLOCF macro to select state creation mode
(lua_newstate or luaL_newstate)
Diffstat (limited to 'src')
-rw-r--r-- | src/keeper.c | 9 | ||||
-rw-r--r-- | src/lanes.c | 68 | ||||
-rw-r--r-- | src/threading.c | 12 | ||||
-rw-r--r-- | src/threading.h | 12 | ||||
-rw-r--r-- | src/tools.c | 9 | ||||
-rw-r--r-- | src/tools.h | 11 |
6 files changed, 89 insertions, 32 deletions
diff --git a/src/keeper.c b/src/keeper.c index 8da3c08..99f510b 100644 --- a/src/keeper.c +++ b/src/keeper.c | |||
@@ -567,8 +567,7 @@ void close_keepers( void) | |||
567 | char const* init_keepers( lua_State* L) | 567 | char const* init_keepers( lua_State* L) |
568 | { | 568 | { |
569 | int i; | 569 | int i; |
570 | void* allocUD; | 570 | PROPAGATE_ALLOCF_PREP( L); |
571 | lua_Alloc allocF = lua_getallocf( L, &allocUD); | ||
572 | 571 | ||
573 | STACK_CHECK( L); | 572 | STACK_CHECK( L); |
574 | lua_getfield( L, 1, "nb_keepers"); | 573 | lua_getfield( L, 1, "nb_keepers"); |
@@ -580,10 +579,10 @@ char const* init_keepers( lua_State* L) | |||
580 | GKeepers = malloc( GNbKeepers * sizeof( struct s_Keeper)); | 579 | GKeepers = malloc( GNbKeepers * sizeof( struct s_Keeper)); |
581 | for( i = 0; i < GNbKeepers; ++ i) | 580 | for( i = 0; i < GNbKeepers; ++ i) |
582 | { | 581 | { |
583 | lua_State* K = lua_newstate( allocF, allocUD); | 582 | lua_State* K = PROPAGATE_ALLOCF_ALLOC(); |
584 | if( K == NULL) | 583 | if( K == NULL) |
585 | { | 584 | { |
586 | (void) luaL_error( L, "'lua_newstate()' failed while creating keeper state; out of memory"); | 585 | (void) luaL_error( L, "init_keepers() failed while creating keeper state; out of memory"); |
587 | } | 586 | } |
588 | STACK_CHECK( K); | 587 | STACK_CHECK( K); |
589 | 588 | ||
@@ -627,7 +626,7 @@ struct s_Keeper* keeper_acquire( void const* ptr) | |||
627 | * have to cast to unsigned long to avoid compilation warnings about loss of data when converting pointer-to-integer | 626 | * have to cast to unsigned long to avoid compilation warnings about loss of data when converting pointer-to-integer |
628 | */ | 627 | */ |
629 | unsigned int i = (unsigned int)(((unsigned long)(ptr) >> 3) % GNbKeepers); | 628 | unsigned int i = (unsigned int)(((unsigned long)(ptr) >> 3) % GNbKeepers); |
630 | struct s_Keeper* K = &GKeepers[i]; | 629 | struct s_Keeper *K= &GKeepers[i]; |
631 | 630 | ||
632 | MUTEX_LOCK( &K->lock_); | 631 | MUTEX_LOCK( &K->lock_); |
633 | //++ K->count; | 632 | //++ K->count; |
diff --git a/src/lanes.c b/src/lanes.c index 53ed5e4..9e32f9a 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.2"; | 55 | char const* VERSION = "3.7.3"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
@@ -1197,7 +1197,7 @@ typedef enum | |||
1197 | CR_Killed | 1197 | CR_Killed |
1198 | } cancel_result; | 1198 | } cancel_result; |
1199 | 1199 | ||
1200 | static cancel_result thread_cancel( struct s_lane *s, double secs, bool_t force) | 1200 | static cancel_result thread_cancel( lua_State* L, struct s_lane* s, double secs, bool_t force, double waitkill_timeout_) |
1201 | { | 1201 | { |
1202 | cancel_result result; | 1202 | cancel_result result; |
1203 | 1203 | ||
@@ -1239,6 +1239,15 @@ static cancel_result thread_cancel( struct s_lane *s, double secs, bool_t force) | |||
1239 | // PThread seems to have). | 1239 | // PThread seems to have). |
1240 | // | 1240 | // |
1241 | THREAD_KILL( &s->thread); | 1241 | THREAD_KILL( &s->thread); |
1242 | #if THREADAPI == THREADAPI_PTHREAD | ||
1243 | // pthread: make sure the thread is really stopped! | ||
1244 | // note that this may block forever if the lane doesn't call a cancellation point and pthread doesn't honor PTHREAD_CANCEL_ASYNCHRONOUS | ||
1245 | result = THREAD_WAIT( &s->thread, waitkill_timeout_, &s->done_signal, &s->done_lock, &s->status); | ||
1246 | if( result == CR_Timeout) | ||
1247 | { | ||
1248 | return luaL_error( L, "force-killed lane failed to terminate within %f second%s", waitkill_timeout_, waitkill_timeout_ > 1 ? "s" : ""); | ||
1249 | } | ||
1250 | #endif // THREADAPI == THREADAPI_PTHREAD | ||
1242 | s->mstatus = KILLED; // mark 'gc' to wait for it | 1251 | s->mstatus = KILLED; // mark 'gc' to wait for it |
1243 | // note that s->status value must remain to whatever it was at the time of the kill | 1252 | // note that s->status value must remain to whatever it was at the time of the kill |
1244 | // because we need to know if we can lua_close() the Lua State or not. | 1253 | // because we need to know if we can lua_close() the Lua State or not. |
@@ -1357,7 +1366,7 @@ static int selfdestruct_gc( lua_State* L) | |||
1357 | while( s != SELFDESTRUCT_END) | 1366 | while( s != SELFDESTRUCT_END) |
1358 | { | 1367 | { |
1359 | // attempt a regular unforced hard cancel with a small timeout | 1368 | // attempt a regular unforced hard cancel with a small timeout |
1360 | bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( s, 0.0001, FALSE); | 1369 | bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( L, s, 0.0001, FALSE, 0.0); |
1361 | // if we failed, and we know the thread is waiting on a linda | 1370 | // if we failed, and we know the thread is waiting on a linda |
1362 | if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL) | 1371 | if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL) |
1363 | { | 1372 | { |
@@ -1758,6 +1767,17 @@ static char const* get_errcode_name( int _code) | |||
1758 | } | 1767 | } |
1759 | #endif // USE_DEBUG_SPEW | 1768 | #endif // USE_DEBUG_SPEW |
1760 | 1769 | ||
1770 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR // implies THREADAPI == THREADAPI_PTHREAD | ||
1771 | static void thread_cleanup_handler( void* opaque) | ||
1772 | { | ||
1773 | struct s_lane* s= (struct s_lane*) opaque; | ||
1774 | MUTEX_LOCK( &s->done_lock); | ||
1775 | s->status = CANCELLED; | ||
1776 | SIGNAL_ONE( &s->done_signal); // wake up master (while 's->done_lock' is on) | ||
1777 | MUTEX_UNLOCK( &s->done_lock); | ||
1778 | } | ||
1779 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
1780 | |||
1761 | //--- | 1781 | //--- |
1762 | static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | 1782 | static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) |
1763 | { | 1783 | { |
@@ -1770,7 +1790,8 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1770 | tracking_add( s); | 1790 | tracking_add( s); |
1771 | } | 1791 | } |
1772 | #endif // HAVE_LANE_TRACKING | 1792 | #endif // HAVE_LANE_TRACKING |
1773 | 1793 | THREAD_MAKE_ASYNCH_CANCELLABLE(); | |
1794 | THREAD_CLEANUP_PUSH( thread_cleanup_handler, s); | ||
1774 | s->status= RUNNING; // PENDING -> RUNNING | 1795 | s->status= RUNNING; // PENDING -> RUNNING |
1775 | 1796 | ||
1776 | // Tie "set_finalizer()" to the state | 1797 | // Tie "set_finalizer()" to the state |
@@ -1888,7 +1909,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1888 | : ERROR_ST; | 1909 | : ERROR_ST; |
1889 | 1910 | ||
1890 | // Posix no PTHREAD_TIMEDJOIN: | 1911 | // Posix no PTHREAD_TIMEDJOIN: |
1891 | // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change | 1912 | // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change |
1892 | // | 1913 | // |
1893 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1914 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1894 | MUTEX_LOCK( &s->done_lock); | 1915 | MUTEX_LOCK( &s->done_lock); |
@@ -1901,6 +1922,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1901 | MUTEX_UNLOCK( &s->done_lock); | 1922 | MUTEX_UNLOCK( &s->done_lock); |
1902 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1923 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1903 | } | 1924 | } |
1925 | THREAD_CLEANUP_POP( FALSE); | ||
1904 | return 0; // ignored | 1926 | return 0; // ignored |
1905 | } | 1927 | } |
1906 | 1928 | ||
@@ -2248,6 +2270,7 @@ LUAG_FUNC( thread_gc) | |||
2248 | return 0; | 2270 | return 0; |
2249 | } | 2271 | } |
2250 | 2272 | ||
2273 | // lane_h:cancel( [timeout,] force[, forcekill_timeout]) | ||
2251 | LUAG_FUNC( thread_cancel) | 2274 | LUAG_FUNC( thread_cancel) |
2252 | { | 2275 | { |
2253 | if( lua_gettop( L) < 1 || lua_type( L, 1) != LUA_TUSERDATA) | 2276 | if( lua_gettop( L) < 1 || lua_type( L, 1) != LUA_TUSERDATA) |
@@ -2258,9 +2281,8 @@ LUAG_FUNC( thread_cancel) | |||
2258 | { | 2281 | { |
2259 | struct s_lane* s = lua_toLane( L, 1); | 2282 | struct s_lane* s = lua_toLane( L, 1); |
2260 | double secs = 0.0; | 2283 | double secs = 0.0; |
2261 | uint_t force_i = 2; | 2284 | int force_i = 2; |
2262 | cancel_result result; | 2285 | int forcekill_timeout_i = 3; |
2263 | bool_t force; | ||
2264 | 2286 | ||
2265 | if( lua_isnumber( L, 2)) | 2287 | if( lua_isnumber( L, 2)) |
2266 | { | 2288 | { |
@@ -2270,30 +2292,34 @@ LUAG_FUNC( thread_cancel) | |||
2270 | return luaL_error( L, "can't force a soft cancel"); | 2292 | return luaL_error( L, "can't force a soft cancel"); |
2271 | } | 2293 | } |
2272 | ++ force_i; | 2294 | ++ force_i; |
2295 | ++ forcekill_timeout_i; | ||
2273 | } | 2296 | } |
2274 | else if( lua_isnil( L, 2)) | 2297 | else if( lua_isnil( L, 2)) |
2275 | { | 2298 | { |
2276 | ++ force_i; | 2299 | ++ force_i; |
2300 | ++ forcekill_timeout_i; | ||
2277 | } | 2301 | } |
2278 | 2302 | ||
2279 | force = lua_toboolean( L, force_i); // FALSE if nothing there | ||
2280 | |||
2281 | result = thread_cancel( s, secs, force); | ||
2282 | switch( result) | ||
2283 | { | 2303 | { |
2304 | bool_t force = lua_toboolean( L, force_i); // FALSE if nothing there | ||
2305 | double forcekill_timeout = luaL_optnumber( L, forcekill_timeout_i, 0.0); | ||
2306 | |||
2307 | switch( thread_cancel( L, s, secs, force, forcekill_timeout)) | ||
2308 | { | ||
2284 | case CR_Timeout: | 2309 | case CR_Timeout: |
2285 | lua_pushboolean( L, 0); | 2310 | lua_pushboolean( L, 0); |
2286 | lua_pushstring( L, "timeout"); | 2311 | lua_pushstring( L, "timeout"); |
2287 | return 2; | 2312 | return 2; |
2288 | 2313 | ||
2289 | case CR_Cancelled: | 2314 | case CR_Cancelled: |
2290 | lua_pushboolean( L, 1); | 2315 | lua_pushboolean( L, 1); |
2291 | return 1; | 2316 | return 1; |
2292 | 2317 | ||
2293 | case CR_Killed: | 2318 | case CR_Killed: |
2294 | lua_pushboolean( L, 0); | 2319 | lua_pushboolean( L, 0); |
2295 | lua_pushstring( L, "killed"); | 2320 | lua_pushstring( L, "killed"); |
2296 | return 2; | 2321 | return 2; |
2322 | } | ||
2297 | } | 2323 | } |
2298 | } | 2324 | } |
2299 | // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value" | 2325 | // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value" |
diff --git a/src/threading.c b/src/threading.c index 63c39ae..ad5b473 100644 --- a/src/threading.c +++ b/src/threading.c | |||
@@ -330,6 +330,8 @@ bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs) | |||
330 | *ref= NULL; | 330 | *ref= NULL; |
331 | } | 331 | } |
332 | 332 | ||
333 | void THREAD_MAKE_ASYNCH_CANCELLABLE() {} // nothing to do for windows threads, we can cancel them anytime we want | ||
334 | |||
333 | #if !defined __GNUC__ | 335 | #if !defined __GNUC__ |
334 | //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx | 336 | //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx |
335 | #define MS_VC_EXCEPTION 0x406D1388 | 337 | #define MS_VC_EXCEPTION 0x406D1388 |
@@ -896,11 +898,19 @@ bool_t THREAD_WAIT( THREAD_T *ref, double secs , SIGNAL_T *signal_ref, MUTEX_T * | |||
896 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 898 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
897 | return done; | 899 | return done; |
898 | } | 900 | } |
899 | // | 901 | // |
900 | void THREAD_KILL( THREAD_T *ref ) { | 902 | void THREAD_KILL( THREAD_T *ref ) { |
901 | pthread_cancel( *ref ); | 903 | pthread_cancel( *ref ); |
902 | } | 904 | } |
903 | 905 | ||
906 | void THREAD_MAKE_ASYNCH_CANCELLABLE() | ||
907 | { | ||
908 | // that's the default, but just in case... | ||
909 | pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); | ||
910 | // we want cancellation to take effect immediately if possible, instead of waiting for a cancellation point (which is the default) | ||
911 | pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL); | ||
912 | } | ||
913 | |||
904 | void THREAD_SETNAME( char const* _name) | 914 | void THREAD_SETNAME( char const* _name) |
905 | { | 915 | { |
906 | // exact API to set the thread name is platform-dependant | 916 | // exact API to set the thread name is platform-dependant |
diff --git a/src/threading.h b/src/threading.h index aa34248..4b28ce8 100644 --- a/src/threading.h +++ b/src/threading.h | |||
@@ -194,10 +194,16 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout ); | |||
194 | # define THREAD_PRIO_MIN (-3) | 194 | # define THREAD_PRIO_MIN (-3) |
195 | # define THREAD_PRIO_MAX (+3) | 195 | # define THREAD_PRIO_MAX (+3) |
196 | 196 | ||
197 | #define THREAD_CLEANUP_PUSH( cb_, val_) | ||
198 | #define THREAD_CLEANUP_POP( execute_) | ||
199 | |||
197 | #else // THREADAPI == THREADAPI_PTHREAD | 200 | #else // THREADAPI == THREADAPI_PTHREAD |
198 | /* Platforms that have a timed 'pthread_join()' can get away with a simpler | 201 | /* Platforms that have a timed 'pthread_join()' can get away with a simpler |
199 | * implementation. Others will use a condition variable. | 202 | * implementation. Others will use a condition variable. |
200 | */ | 203 | */ |
204 | #if defined __WINPTHREADS_VERSION | ||
205 | #define USE_PTHREAD_TIMEDJOIN | ||
206 | #endif // __WINPTHREADS_VERSION | ||
201 | # ifdef USE_PTHREAD_TIMEDJOIN | 207 | # ifdef USE_PTHREAD_TIMEDJOIN |
202 | # ifdef PLATFORM_OSX | 208 | # ifdef PLATFORM_OSX |
203 | # error "No 'pthread_timedjoin()' on this system" | 209 | # error "No 'pthread_timedjoin()' on this system" |
@@ -229,6 +235,9 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout ); | |||
229 | # define THREAD_PRIO_MIN (-2) | 235 | # define THREAD_PRIO_MIN (-2) |
230 | # define THREAD_PRIO_MAX (+2) | 236 | # define THREAD_PRIO_MAX (+2) |
231 | # endif | 237 | # endif |
238 | |||
239 | #define THREAD_CLEANUP_PUSH( cb_, val_) pthread_cleanup_push( cb_, val_) | ||
240 | #define THREAD_CLEANUP_POP( execute_) pthread_cleanup_pop( execute_) | ||
232 | #endif // THREADAPI == THREADAPI_WINDOWS | 241 | #endif // THREADAPI == THREADAPI_WINDOWS |
233 | 242 | ||
234 | /* | 243 | /* |
@@ -253,7 +262,8 @@ bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs, SIGNAL_T *signal_ref, MUTEX | |||
253 | #define THREAD_WAIT THREAD_WAIT_IMPL | 262 | #define THREAD_WAIT THREAD_WAIT_IMPL |
254 | #endif // // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 263 | #endif // // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
255 | 264 | ||
256 | void THREAD_KILL( THREAD_T *ref ); | 265 | void THREAD_KILL( THREAD_T* ref); |
257 | void THREAD_SETNAME( char const* _name); | 266 | void THREAD_SETNAME( char const* _name); |
267 | void THREAD_MAKE_ASYNCH_CANCELLABLE(); | ||
258 | 268 | ||
259 | #endif // __threading_h__ | 269 | #endif // __threading_h__ |
diff --git a/src/tools.c b/src/tools.c index 754605b..b5f2f4b 100644 --- a/src/tools.c +++ b/src/tools.c | |||
@@ -570,13 +570,14 @@ void populate_func_lookup_table( lua_State* L, int _i, char const* name_) | |||
570 | lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char const* libs) | 570 | lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char const* libs) |
571 | { | 571 | { |
572 | // reuse alloc function from the originating state | 572 | // reuse alloc function from the originating state |
573 | void* allocUD; | 573 | #if PROPAGATE_ALLOCF |
574 | lua_Alloc allocF = lua_getallocf( _from, &allocUD); | 574 | PROPAGATE_ALLOCF_PREP( _from); |
575 | lua_State* L = lua_newstate( allocF, allocUD); | 575 | #endif // PROPAGATE_ALLOCF |
576 | lua_State* L = PROPAGATE_ALLOCF_ALLOC(); | ||
576 | 577 | ||
577 | if( L == NULL) | 578 | if( L == NULL) |
578 | { | 579 | { |
579 | (void) luaL_error( _from, "'lua_newstate()' failed; out of memory"); | 580 | (void) luaL_error( _from, "luaG_newstate() failed while creating state; out of memory"); |
580 | } | 581 | } |
581 | 582 | ||
582 | // we'll need this everytime we transfer some C function from/to this state | 583 | // we'll need this everytime we transfer some C function from/to this state |
diff --git a/src/tools.h b/src/tools.h index e984ec2..19dca70 100644 --- a/src/tools.h +++ b/src/tools.h | |||
@@ -41,6 +41,17 @@ void luaL_requiref (lua_State* L, const char* modname, lua_CFunction openf, int | |||
41 | #define luaG_registerlibfuncs( L, _funcs) luaL_setfuncs( L, _funcs, 0) | 41 | #define luaG_registerlibfuncs( L, _funcs) luaL_setfuncs( L, _funcs, 0) |
42 | #endif // LUA_VERSION_NUM == 502 | 42 | #endif // LUA_VERSION_NUM == 502 |
43 | 43 | ||
44 | // For some reason, LuaJIT 64bits doesn't support lua_newstate() | ||
45 | // If you build specifically for this situation, change value to 0 | ||
46 | #define PROPAGATE_ALLOCF 1 | ||
47 | #if PROPAGATE_ALLOCF | ||
48 | #define PROPAGATE_ALLOCF_PREP( L) void* allocUD; lua_Alloc allocF = lua_getallocf( L, &allocUD) | ||
49 | #define PROPAGATE_ALLOCF_ALLOC() lua_newstate( allocF, allocUD) | ||
50 | #else // PROPAGATE_ALLOCF | ||
51 | #define PROPAGATE_ALLOCF_PREP( L) | ||
52 | #define PROPAGATE_ALLOCF_ALLOC() luaL_newstate() | ||
53 | #endif // PROPAGATE_ALLOCF | ||
54 | |||
44 | #define USE_DEBUG_SPEW 0 | 55 | #define USE_DEBUG_SPEW 0 |
45 | #if USE_DEBUG_SPEW | 56 | #if USE_DEBUG_SPEW |
46 | extern char const* debugspew_indent; | 57 | extern char const* debugspew_indent; |