From ff74281fc94cda26b2d0a7fc2424f24fe2488718 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Fri, 29 Nov 2013 20:33:46 +0100 Subject: 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) --- src/keeper.c | 9 ++++---- src/lanes.c | 68 +++++++++++++++++++++++++++++++++++++++------------------ src/threading.c | 12 +++++++++- src/threading.h | 12 +++++++++- src/tools.c | 9 ++++---- src/tools.h | 11 ++++++++++ 6 files changed, 89 insertions(+), 32 deletions(-) (limited to 'src') 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) char const* init_keepers( lua_State* L) { int i; - void* allocUD; - lua_Alloc allocF = lua_getallocf( L, &allocUD); + PROPAGATE_ALLOCF_PREP( L); STACK_CHECK( L); lua_getfield( L, 1, "nb_keepers"); @@ -580,10 +579,10 @@ char const* init_keepers( lua_State* L) GKeepers = malloc( GNbKeepers * sizeof( struct s_Keeper)); for( i = 0; i < GNbKeepers; ++ i) { - lua_State* K = lua_newstate( allocF, allocUD); + lua_State* K = PROPAGATE_ALLOCF_ALLOC(); if( K == NULL) { - (void) luaL_error( L, "'lua_newstate()' failed while creating keeper state; out of memory"); + (void) luaL_error( L, "init_keepers() failed while creating keeper state; out of memory"); } STACK_CHECK( K); @@ -627,7 +626,7 @@ struct s_Keeper* keeper_acquire( void const* ptr) * have to cast to unsigned long to avoid compilation warnings about loss of data when converting pointer-to-integer */ unsigned int i = (unsigned int)(((unsigned long)(ptr) >> 3) % GNbKeepers); - struct s_Keeper* K = &GKeepers[i]; + struct s_Keeper *K= &GKeepers[i]; MUTEX_LOCK( &K->lock_); //++ 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 @@ * ... */ -char const* VERSION = "3.7.2"; +char const* VERSION = "3.7.3"; /* =============================================================================== @@ -1197,7 +1197,7 @@ typedef enum CR_Killed } cancel_result; -static cancel_result thread_cancel( struct s_lane *s, double secs, bool_t force) +static cancel_result thread_cancel( lua_State* L, struct s_lane* s, double secs, bool_t force, double waitkill_timeout_) { cancel_result result; @@ -1239,6 +1239,15 @@ static cancel_result thread_cancel( struct s_lane *s, double secs, bool_t force) // PThread seems to have). // THREAD_KILL( &s->thread); +#if THREADAPI == THREADAPI_PTHREAD + // pthread: make sure the thread is really stopped! + // note that this may block forever if the lane doesn't call a cancellation point and pthread doesn't honor PTHREAD_CANCEL_ASYNCHRONOUS + result = THREAD_WAIT( &s->thread, waitkill_timeout_, &s->done_signal, &s->done_lock, &s->status); + if( result == CR_Timeout) + { + return luaL_error( L, "force-killed lane failed to terminate within %f second%s", waitkill_timeout_, waitkill_timeout_ > 1 ? "s" : ""); + } +#endif // THREADAPI == THREADAPI_PTHREAD s->mstatus = KILLED; // mark 'gc' to wait for it // note that s->status value must remain to whatever it was at the time of the kill // 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) while( s != SELFDESTRUCT_END) { // attempt a regular unforced hard cancel with a small timeout - bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( s, 0.0001, FALSE); + bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( L, s, 0.0001, FALSE, 0.0); // if we failed, and we know the thread is waiting on a linda if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL) { @@ -1758,6 +1767,17 @@ static char const* get_errcode_name( int _code) } #endif // USE_DEBUG_SPEW +#if THREADWAIT_METHOD == THREADWAIT_CONDVAR // implies THREADAPI == THREADAPI_PTHREAD +static void thread_cleanup_handler( void* opaque) +{ + struct s_lane* s= (struct s_lane*) opaque; + MUTEX_LOCK( &s->done_lock); + s->status = CANCELLED; + SIGNAL_ONE( &s->done_signal); // wake up master (while 's->done_lock' is on) + MUTEX_UNLOCK( &s->done_lock); +} +#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR + //--- static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) { @@ -1770,7 +1790,8 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) tracking_add( s); } #endif // HAVE_LANE_TRACKING - + THREAD_MAKE_ASYNCH_CANCELLABLE(); + THREAD_CLEANUP_PUSH( thread_cleanup_handler, s); s->status= RUNNING; // PENDING -> RUNNING // Tie "set_finalizer()" to the state @@ -1888,7 +1909,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) : ERROR_ST; // Posix no PTHREAD_TIMEDJOIN: - // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change + // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change // #if THREADWAIT_METHOD == THREADWAIT_CONDVAR MUTEX_LOCK( &s->done_lock); @@ -1901,6 +1922,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) MUTEX_UNLOCK( &s->done_lock); #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR } + THREAD_CLEANUP_POP( FALSE); return 0; // ignored } @@ -2248,6 +2270,7 @@ LUAG_FUNC( thread_gc) return 0; } +// lane_h:cancel( [timeout,] force[, forcekill_timeout]) LUAG_FUNC( thread_cancel) { if( lua_gettop( L) < 1 || lua_type( L, 1) != LUA_TUSERDATA) @@ -2258,9 +2281,8 @@ LUAG_FUNC( thread_cancel) { struct s_lane* s = lua_toLane( L, 1); double secs = 0.0; - uint_t force_i = 2; - cancel_result result; - bool_t force; + int force_i = 2; + int forcekill_timeout_i = 3; if( lua_isnumber( L, 2)) { @@ -2270,30 +2292,34 @@ LUAG_FUNC( thread_cancel) return luaL_error( L, "can't force a soft cancel"); } ++ force_i; + ++ forcekill_timeout_i; } else if( lua_isnil( L, 2)) { ++ force_i; + ++ forcekill_timeout_i; } - force = lua_toboolean( L, force_i); // FALSE if nothing there - - result = thread_cancel( s, secs, force); - switch( result) { + bool_t force = lua_toboolean( L, force_i); // FALSE if nothing there + double forcekill_timeout = luaL_optnumber( L, forcekill_timeout_i, 0.0); + + switch( thread_cancel( L, s, secs, force, forcekill_timeout)) + { case CR_Timeout: - lua_pushboolean( L, 0); - lua_pushstring( L, "timeout"); - return 2; + lua_pushboolean( L, 0); + lua_pushstring( L, "timeout"); + return 2; case CR_Cancelled: - lua_pushboolean( L, 1); - return 1; - + lua_pushboolean( L, 1); + return 1; + case CR_Killed: - lua_pushboolean( L, 0); - lua_pushstring( L, "killed"); - return 2; + lua_pushboolean( L, 0); + lua_pushstring( L, "killed"); + return 2; + } } } // 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) *ref= NULL; } + void THREAD_MAKE_ASYNCH_CANCELLABLE() {} // nothing to do for windows threads, we can cancel them anytime we want + #if !defined __GNUC__ //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx #define MS_VC_EXCEPTION 0x406D1388 @@ -896,11 +898,19 @@ bool_t THREAD_WAIT( THREAD_T *ref, double secs , SIGNAL_T *signal_ref, MUTEX_T * #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR return done; } - // + // void THREAD_KILL( THREAD_T *ref ) { pthread_cancel( *ref ); } + void THREAD_MAKE_ASYNCH_CANCELLABLE() + { + // that's the default, but just in case... + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + // we want cancellation to take effect immediately if possible, instead of waiting for a cancellation point (which is the default) + pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + } + void THREAD_SETNAME( char const* _name) { // 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 ); # define THREAD_PRIO_MIN (-3) # define THREAD_PRIO_MAX (+3) +#define THREAD_CLEANUP_PUSH( cb_, val_) +#define THREAD_CLEANUP_POP( execute_) + #else // THREADAPI == THREADAPI_PTHREAD /* Platforms that have a timed 'pthread_join()' can get away with a simpler * implementation. Others will use a condition variable. */ +#if defined __WINPTHREADS_VERSION +#define USE_PTHREAD_TIMEDJOIN +#endif // __WINPTHREADS_VERSION # ifdef USE_PTHREAD_TIMEDJOIN # ifdef PLATFORM_OSX # error "No 'pthread_timedjoin()' on this system" @@ -229,6 +235,9 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout ); # define THREAD_PRIO_MIN (-2) # define THREAD_PRIO_MAX (+2) # endif + +#define THREAD_CLEANUP_PUSH( cb_, val_) pthread_cleanup_push( cb_, val_) +#define THREAD_CLEANUP_POP( execute_) pthread_cleanup_pop( execute_) #endif // THREADAPI == THREADAPI_WINDOWS /* @@ -253,7 +262,8 @@ bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs, SIGNAL_T *signal_ref, MUTEX #define THREAD_WAIT THREAD_WAIT_IMPL #endif // // THREADWAIT_METHOD == THREADWAIT_CONDVAR -void THREAD_KILL( THREAD_T *ref ); +void THREAD_KILL( THREAD_T* ref); void THREAD_SETNAME( char const* _name); +void THREAD_MAKE_ASYNCH_CANCELLABLE(); #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_) lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char const* libs) { // reuse alloc function from the originating state - void* allocUD; - lua_Alloc allocF = lua_getallocf( _from, &allocUD); - lua_State* L = lua_newstate( allocF, allocUD); +#if PROPAGATE_ALLOCF + PROPAGATE_ALLOCF_PREP( _from); +#endif // PROPAGATE_ALLOCF + lua_State* L = PROPAGATE_ALLOCF_ALLOC(); if( L == NULL) { - (void) luaL_error( _from, "'lua_newstate()' failed; out of memory"); + (void) luaL_error( _from, "luaG_newstate() failed while creating state; out of memory"); } // 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 #define luaG_registerlibfuncs( L, _funcs) luaL_setfuncs( L, _funcs, 0) #endif // LUA_VERSION_NUM == 502 +// For some reason, LuaJIT 64bits doesn't support lua_newstate() +// If you build specifically for this situation, change value to 0 +#define PROPAGATE_ALLOCF 1 +#if PROPAGATE_ALLOCF +#define PROPAGATE_ALLOCF_PREP( L) void* allocUD; lua_Alloc allocF = lua_getallocf( L, &allocUD) +#define PROPAGATE_ALLOCF_ALLOC() lua_newstate( allocF, allocUD) +#else // PROPAGATE_ALLOCF +#define PROPAGATE_ALLOCF_PREP( L) +#define PROPAGATE_ALLOCF_ALLOC() luaL_newstate() +#endif // PROPAGATE_ALLOCF + #define USE_DEBUG_SPEW 0 #if USE_DEBUG_SPEW extern char const* debugspew_indent; -- cgit v1.2.3-55-g6feb