From 49f8b961cc230280e4cf86cc8d50c8a6d8687a65 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Wed, 4 Dec 2013 08:40:23 +0100 Subject: new API lanes.set_thread_priority() --- src/lanes.c | 31 ++++++++++++---- src/lanes.lua | 1 + src/threading.c | 108 +++++++++++++++++++++++++++++++++++--------------------- src/threading.h | 84 +++++++++++++++++++++---------------------- 4 files changed, 133 insertions(+), 91 deletions(-) (limited to 'src') diff --git a/src/lanes.c b/src/lanes.c index ec7fd9e..467c920 100644 --- a/src/lanes.c +++ b/src/lanes.c @@ -35,10 +35,10 @@ * * Defines: * -DLINUX_SCHED_RR: all threads are lifted to SCHED_RR category, to - * allow negative priorities (-2,-1) be used. Even without this, + * allow negative priorities [-3,-1] be used. Even without this, * using priorities will require 'sudo' privileges on Linux. * - * -DUSE_PTHREAD_TIMEDJOIN: use 'pthread_timedjoin_np()' for waiting + * -DUSE_PTHREAD_TIMEDJOIN: use 'pthread_timedjoin_np()' for waiting * for threads with a timeout. This changes the thread cleanup * mechanism slightly (cleans up at the join, not once the thread * has finished). May or may not be a good idea to use it. @@ -52,7 +52,7 @@ * ... */ -char const* VERSION = "3.7.3"; +char const* VERSION = "3.7.4"; /* =============================================================================== @@ -1734,6 +1734,20 @@ LUAG_FUNC( set_debug_threadname) return 0; } +LUAG_FUNC( set_thread_priority) +{ + int const prio = luaL_checkint( L, 1); + // public Lanes API accepts a generic range -3/+3 + // that will be remapped into the platform-specific scheduler priority scheme + // On some platforms, -3 is equivalent to -2 and +3 to +2 + if( prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) + { + return luaL_error( L, "priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio); + } + THREAD_SET_PRIORITY( prio); + return 0; +} + #if USE_DEBUG_SPEW // can't use direct LUA_x errcode indexing because the sequence is not the same between Lua 5.1 and 5.2 :-( // LUA_ERRERR doesn't have the same value @@ -2693,6 +2707,7 @@ static const struct luaL_Reg lanes_functions [] = { {"linda", LG_linda}, {"now_secs", LG_now_secs}, {"wakeup_conv", LG_wakeup_conv}, + {"set_thread_priority", LG_set_thread_priority}, {"nameof", luaG_nameof}, {"set_singlethreaded", LG_set_singlethreaded}, {NULL, NULL} @@ -2754,16 +2769,18 @@ static void init_once_LOCKED( lua_State* L) // be enabled also for Linux. // #ifdef PLATFORM_LINUX - sudo= geteuid()==0; // we are root? + sudo = (geteuid() == 0); // we are root? // If lower priorities (-2..-1) are wanted, we need to lift the main // thread to SCHED_RR and 50 (medium) level. Otherwise, we're always below // the launched threads (even -2). // #ifdef LINUX_SCHED_RR - if (sudo) { - struct sched_param sp= {0}; sp.sched_priority= _PRIO_0; - PT_CALL( pthread_setschedparam( pthread_self(), SCHED_RR, &sp) ); + if( sudo) + { + struct sched_param sp; + sp.sched_priority = _PRIO_0; + PT_CALL( pthread_setschedparam( pthread_self(), SCHED_RR, &sp)); } #endif // LINUX_SCHED_RR #endif // PLATFORM_LINUX diff --git a/src/lanes.lua b/src/lanes.lua index 1b81944..6e2a736 100644 --- a/src/lanes.lua +++ b/src/lanes.lua @@ -683,6 +683,7 @@ end lanes.cancel_error = core.cancel_error lanes.nameof = core.nameof lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false + lanes.set_thread_priority = core.set_thread_priority lanes.timer = timer lanes.timer_lane = timer_lane lanes.timers = timers diff --git a/src/threading.c b/src/threading.c index 18eef87..db9f734 100644 --- a/src/threading.c +++ b/src/threading.c @@ -12,6 +12,7 @@ =============================================================================== Copyright (C) 2007-10 Asko Kauppi +Copyright (C) 2009-13, Benoit Germain Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -269,40 +270,52 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) { } #endif // Windows NT4 - /* MSDN: "If you would like to use the CRT in ThreadProc, use the - _beginthreadex function instead (of CreateThread)." - MSDN: "you can create at most 2028 threads" - */ - void - THREAD_CREATE( THREAD_T *ref, - THREAD_RETURN_T (__stdcall *func)( void * ), - // Note: Visual C++ requires '__stdcall' where it is - void *data, int prio /* -3..+3 */ ) { - - HANDLE h= (HANDLE)_beginthreadex( NULL, // security - _THREAD_STACK_SIZE, - func, - data, - 0, // flags (0/CREATE_SUSPENDED) - NULL // thread id (not used) - ); - - if (h == INVALID_HANDLE_VALUE) FAIL( "CreateThread", GetLastError() ); - - if (prio!= 0) { - int win_prio= (prio == +3) ? THREAD_PRIORITY_TIME_CRITICAL : - (prio == +2) ? THREAD_PRIORITY_HIGHEST : - (prio == +1) ? THREAD_PRIORITY_ABOVE_NORMAL : - (prio == -1) ? THREAD_PRIORITY_BELOW_NORMAL : - (prio == -2) ? THREAD_PRIORITY_LOWEST : - THREAD_PRIORITY_IDLE; // -3 - - if (!SetThreadPriority( h, win_prio )) - FAIL( "SetThreadPriority", GetLastError() ); - } - *ref= h; - } - // +static int const gs_prio_remap[] = +{ + THREAD_PRIORITY_IDLE, + THREAD_PRIORITY_LOWEST, + THREAD_PRIORITY_BELOW_NORMAL, + THREAD_PRIORITY_NORMAL, + THREAD_PRIORITY_ABOVE_NORMAL, + THREAD_PRIORITY_HIGHEST, + THREAD_PRIORITY_TIME_CRITICAL +}; + +/* MSDN: "If you would like to use the CRT in ThreadProc, use the +_beginthreadex function instead (of CreateThread)." +MSDN: "you can create at most 2028 threads" +*/ +// Note: Visual C++ requires '__stdcall' where it is +void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (__stdcall *func)( void*), void* data, int prio /* -3..+3 */) +{ + HANDLE h = (HANDLE) _beginthreadex( NULL, // security + _THREAD_STACK_SIZE, + func, + data, + 0, // flags (0/CREATE_SUSPENDED) + NULL // thread id (not used) + ); + + if( h == INVALID_HANDLE_VALUE) + FAIL( "CreateThread", GetLastError()); + + if (!SetThreadPriority( h, gs_prio_remap[prio + 3])) + FAIL( "SetThreadPriority", GetLastError()); + + *ref = h; +} + + +void THREAD_SET_PRIORITY( int prio) +{ + // prio range [-3,+3] was checked by the caller + if (!SetThreadPriority( GetCurrentThread(), gs_prio_remap[prio + 3])) + { + FAIL( "THREAD_SET_PRIORITY", GetLastError()); + } +} + + bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs) { DWORD ms = (secs<0.0) ? INFINITE : (DWORD)((secs*1000.0)+0.5); @@ -710,20 +723,19 @@ static int const gs_prio_remap[] = # endif #if defined _PRIO_0 -# define _PRIO_AN (_PRIO_0 + ((_PRIO_HI-_PRIO_0)/2) ) -# define _PRIO_BN (_PRIO_LO + ((_PRIO_0-_PRIO_LO)/2) ) +# define _PRIO_AN (_PRIO_0 + ((_PRIO_HI-_PRIO_0)/2)) +# define _PRIO_BN (_PRIO_LO + ((_PRIO_0-_PRIO_LO)/2)) _PRIO_LO, _PRIO_LO, _PRIO_BN, _PRIO_0, _PRIO_AN, _PRIO_HI, _PRIO_HI #endif // _PRIO_0 }; -// -void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void * ), void *data, int prio /* -2..+2 */) +void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, int prio /* -3..+3 */) { pthread_attr_t a; bool_t const normal = #if defined(PLATFORM_LINUX) && defined(LINUX_SCHED_RR) - !sudo; // with sudo, even normal thread must use SCHED_RR + !sudo; // with sudo, even normal thread must use SCHED_RR #else (prio == 0); #endif @@ -762,7 +774,6 @@ void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void * ), void *data // PT_CALL( pthread_attr_setinheritsched( &a, PTHREAD_EXPLICIT_SCHED)); - #ifdef _PRIO_SCOPE PT_CALL( pthread_attr_setscope( &a, _PRIO_SCOPE)); #endif // _PRIO_SCOPE @@ -828,13 +839,28 @@ void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void * ), void *data PT_CALL( pthread_attr_destroy( &a)); } + +void THREAD_SET_PRIORITY( int prio) +{ +#if defined PLATFORM_LINUX && defined LINUX_SCHED_RR + if( sudo) // only root-privileged process can change priorities +#endif // defined PLATFORM_LINUX && defined LINUX_SCHED_RR + { + struct sched_param sp; + // prio range [-3,+3] was checked by the caller + sp.sched_priority = gs_prio_remap[ prio + 3]; + PT_CALL( pthread_setschedparam( pthread_self(), _PRIO_MODE, &sp)); + } +} + + /* * Wait for a thread to finish. * * 'mu_ref' is a lock we should use for the waiting; initially unlocked. * Same lock as passed to THREAD_EXIT. * - * Returns TRUE for succesful wait, FALSE for timed out + * Returns TRUE for successful wait, FALSE for timed out */ bool_t THREAD_WAIT( THREAD_T *ref, double secs , SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref) { diff --git a/src/threading.h b/src/threading.h index d87bba6..0698355 100644 --- a/src/threading.h +++ b/src/threading.h @@ -184,26 +184,25 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout ); #if THREADAPI == THREADAPI_WINDOWS - typedef HANDLE THREAD_T; -# define THREAD_ISNULL( _h) (_h == 0) - // - void THREAD_CREATE( THREAD_T *ref, - THREAD_RETURN_T (__stdcall *func)( void * ), - void *data, int prio /* -3..+3 */ ); + typedef HANDLE THREAD_T; +# define THREAD_ISNULL( _h) (_h == 0) + void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (__stdcall *func)( void*), void* data, int prio /* -3..+3 */); -# define THREAD_PRIO_MIN (-3) -# define THREAD_PRIO_MAX (+3) +# define THREAD_PRIO_MIN (-3) +# define THREAD_PRIO_MAX (+3) -#define THREAD_CLEANUP_PUSH( cb_, val_) -#define THREAD_CLEANUP_POP( execute_) +# 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 + + /* 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" @@ -213,33 +212,31 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout ); # endif # endif - typedef pthread_t THREAD_T; -# define THREAD_ISNULL( _h) 0 // pthread_t may be a structure: never 'null' by itself - - void THREAD_CREATE( THREAD_T *ref, - THREAD_RETURN_T (*func)( void * ), - void *data, int prio /* -2..+2 */ ); - -# if defined(PLATFORM_LINUX) - extern volatile bool_t sudo; -# ifdef LINUX_SCHED_RR -# define THREAD_PRIO_MIN (sudo ? -3 : 0) -# else -# define THREAD_PRIO_MIN (0) -# endif -# define THREAD_PRIO_MAX (sudo ? +3 : 0) -# else -# define THREAD_PRIO_MIN (-3) -# define THREAD_PRIO_MAX (+3) -# endif - -#if THREADWAIT_METHOD == THREADWAIT_CONDVAR -#define THREAD_CLEANUP_PUSH( cb_, val_) pthread_cleanup_push( cb_, val_) -#define THREAD_CLEANUP_POP( execute_) pthread_cleanup_pop( execute_) -#else -#define THREAD_CLEANUP_PUSH( cb_, val_) { -#define THREAD_CLEANUP_POP( execute_) } -#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR + typedef pthread_t THREAD_T; +# define THREAD_ISNULL( _h) 0 // pthread_t may be a structure: never 'null' by itself + + void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, int prio /* -3..+3 */); + +# if defined(PLATFORM_LINUX) + extern volatile bool_t sudo; +# ifdef LINUX_SCHED_RR +# define THREAD_PRIO_MIN (sudo ? -3 : 0) +# else +# define THREAD_PRIO_MIN (0) +# endif +# define THREAD_PRIO_MAX (sudo ? +3 : 0) +# else +# define THREAD_PRIO_MIN (-3) +# define THREAD_PRIO_MAX (+3) +# endif + +# if THREADWAIT_METHOD == THREADWAIT_CONDVAR +# define THREAD_CLEANUP_PUSH( cb_, val_) pthread_cleanup_push( cb_, val_) +# define THREAD_CLEANUP_POP( execute_) pthread_cleanup_pop( execute_) +# else +# define THREAD_CLEANUP_PUSH( cb_, val_) { +# define THREAD_CLEANUP_POP( execute_) } +# endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR #endif // THREADAPI == THREADAPI_WINDOWS /* @@ -267,5 +264,6 @@ bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs, SIGNAL_T *signal_ref, MUTEX void THREAD_KILL( THREAD_T* ref); void THREAD_SETNAME( char const* _name); void THREAD_MAKE_ASYNCH_CANCELLABLE(); +void THREAD_SET_PRIORITY( int prio); #endif // __threading_h__ -- cgit v1.2.3-55-g6feb