From d73f4cee37b0a43edadf9709289798ee4bfccc0e Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Fri, 28 Jul 2023 14:48:16 +0200 Subject: tweaks to linux thread priority management --- CHANGES | 6 +++ src/lanes.c | 77 +++++++++++++++---------------- src/threading.c | 137 ++++++++++++++++++++++++++++++-------------------------- src/threading.h | 4 ++ 4 files changed, 122 insertions(+), 102 deletions(-) diff --git a/CHANGES b/CHANGES index 568f6db..3b333b2 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,11 @@ CHANGES: +CHANGE 155: BGe 28-Jul-23 + * tweaks to linux thread priority management: do nothing if not super-user. if super-user, do nothing if nothing is provided (instead of trying to force a prio when LINUX_SCHED_RR is defined). + +CHANGE 154: eligovision 1-Mar-22 + * Fix 3-parametrized __lanesclone + CHANGE 153: BGe 17-Feb-22 * NEVER use allocator obtained from lua_getallocf to allocate stuff manually when compiling for LuaJIT diff --git a/src/lanes.c b/src/lanes.c index cd1d4f1..9f6a4d6 100644 --- a/src/lanes.c +++ b/src/lanes.c @@ -1052,21 +1052,22 @@ LUAG_FUNC( lane_new) Lane** ud; char const* libs_str = lua_tostring( L, 2); - int const priority = (int) luaL_optinteger( L, 3, 0); - uint_t globals_idx = lua_isnoneornil( L, 4) ? 0 : 4; - uint_t package_idx = lua_isnoneornil( L, 5) ? 0 : 5; - uint_t required_idx = lua_isnoneornil( L, 6) ? 0 : 6; - uint_t gc_cb_idx = lua_isnoneornil( L, 7) ? 0 : 7; + bool_t const have_priority = !lua_isnoneornil( L, 3); + int const priority = have_priority ? (int) lua_tointeger( L, 3) : THREAD_PRIO_DEFAULT; + uint_t const globals_idx = lua_isnoneornil( L, 4) ? 0 : 4; + uint_t const package_idx = lua_isnoneornil( L, 5) ? 0 : 5; + uint_t const required_idx = lua_isnoneornil( L, 6) ? 0 : 6; + uint_t const gc_cb_idx = lua_isnoneornil( L, 7) ? 0 : 7; #define FIXED_ARGS 7 int const nargs = lua_gettop(L) - FIXED_ARGS; - Universe* U = universe_get( L); + Universe* const U = universe_get( L); ASSERT_L( nargs >= 0); // 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( priority < THREAD_PRIO_MIN || priority > THREAD_PRIO_MAX) + if( have_priority && (priority < THREAD_PRIO_MIN || priority > THREAD_PRIO_MAX)) { return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, priority); } @@ -1076,12 +1077,12 @@ LUAG_FUNC( lane_new) DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); // populate with selected libraries at the same time - L2 = luaG_newstate( U, L, libs_str); // L // L2 + L2 = luaG_newstate( U, L, libs_str); // L // L2 STACK_GROW( L2, nargs + 3); // STACK_CHECK( L2, 0); - STACK_GROW( L, 3); // func libs priority globals package required gc_cb [... args ...] + STACK_GROW( L, 3); // func libs priority globals package required gc_cb [... args ...] STACK_CHECK( L, 0); // give a default "Lua" name to the thread to see VM name in Decoda debugger @@ -1110,8 +1111,8 @@ LUAG_FUNC( lane_new) return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required_idx)); } - lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil - while( lua_next( L, required_idx) != 0) // func libs priority globals package required gc_cb [... args ...] n "modname" + lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil + while( lua_next( L, required_idx) != 0) // func libs priority globals package required gc_cb [... args ...] n "modname" { if( lua_type( L, -1) != LUA_TSTRING || lua_type( L, -2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) { @@ -1125,29 +1126,29 @@ LUAG_FUNC( lane_new) DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END, name)); // require the module in the target lane - lua_getglobal( L2, "require"); // require()? + lua_getglobal( L2, "require"); // require()? if( lua_isnil( L2, -1)) { - lua_pop( L2, 1); // + lua_pop( L2, 1); // luaL_error( L, "cannot pre-require modules without loading 'package' library first"); } else { - lua_pushlstring( L2, name, len); // require() name - if( lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode + lua_pushlstring( L2, name, len); // require() name + if( lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode { // propagate error to main state if any - luaG_inter_move( U, L2, L, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] n "modname" error + luaG_inter_move( U, L2, L, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] n "modname" error return lua_error( L); } // after requiring the module, register the functions it exported in our name<->function database populate_func_lookup_table( L2, -1, name); - lua_pop( L2, 1); // + lua_pop( L2, 1); // } } - lua_pop( L, 1); // func libs priority globals package required gc_cb [... args ...] n + lua_pop( L, 1); // func libs priority globals package required gc_cb [... args ...] n ++ nbRequired; - } // func libs priority globals package required gc_cb [... args ...] + } // func libs priority globals package required gc_cb [... args ...] DEBUGSPEW_CODE( -- U->debugspew_indent_depth); } STACK_MID( L, 0); @@ -1165,17 +1166,17 @@ LUAG_FUNC( lane_new) } DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); - lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil + lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil // Lua 5.2 wants us to push the globals table on the stack - lua_pushglobaltable( L2); // _G - while( lua_next( L, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v + lua_pushglobaltable( L2); // _G + while( lua_next( L, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v { - luaG_inter_copy( U, L, L2, 2, eLM_LaneBody); // _G k v + luaG_inter_copy( U, L, L2, 2, eLM_LaneBody); // _G k v // assign it in L2's globals table - lua_rawset( L2, -3); // _G - lua_pop( L, 1); // func libs priority globals package required gc_cb [... args ...] k - } // func libs priority globals package required gc_cb [... args ...] - lua_pop( L2, 1); // + lua_rawset( L2, -3); // _G + lua_pop( L, 1); // func libs priority globals package required gc_cb [... args ...] k + } // func libs priority globals package required gc_cb [... args ...] + lua_pop( L2, 1); // DEBUGSPEW_CODE( -- U->debugspew_indent_depth); } @@ -1188,8 +1189,8 @@ LUAG_FUNC( lane_new) int res; DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END)); DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); - lua_pushvalue( L, 1); // func libs priority globals package required gc_cb [... args ...] func - res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] // func + lua_pushvalue( L, 1); // func libs priority globals package required gc_cb [... args ...] func + res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] // func DEBUGSPEW_CODE( -- U->debugspew_indent_depth); if( res != 0) { @@ -1199,7 +1200,7 @@ LUAG_FUNC( lane_new) else if( lua_type( L, 1) == LUA_TSTRING) { // compile the string - if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) // func + if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) // func { return luaL_error( L, "error when parsing lane function code"); } @@ -1214,7 +1215,7 @@ LUAG_FUNC( lane_new) int res; DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END)); DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); - res = luaG_inter_move( U, L, L2, nargs, eLM_LaneBody); // func libs priority globals package required gc_cb // func [... args ...] + res = luaG_inter_move( U, L, L2, nargs, eLM_LaneBody); // func libs priority globals package required gc_cb // func [... args ...] DEBUGSPEW_CODE( -- U->debugspew_indent_depth); if( res != 0) { @@ -1267,23 +1268,23 @@ LUAG_FUNC( lane_new) // Set metatable for the userdata // - lua_pushvalue( L, lua_upvalueindex( 1)); // func libs priority globals package required gc_cb lane mt - lua_setmetatable( L, -2); // func libs priority globals package required gc_cb lane + lua_pushvalue( L, lua_upvalueindex( 1)); // func libs priority globals package required gc_cb lane mt + lua_setmetatable( L, -2); // func libs priority globals package required gc_cb lane STACK_MID( L, 1); // Create uservalue for the userdata // (this is where lane body return values will be stored when the handle is indexed by a numeric key) - lua_newtable( L); // func libs cancelstep priority globals package required gc_cb lane uv + lua_newtable( L); // func libs cancelstep priority globals package required gc_cb lane uv // Store the gc_cb callback in the uservalue if( gc_cb_idx > 0) { - push_unique_key( L, GCCB_KEY); // func libs priority globals package required gc_cb lane uv k - lua_pushvalue( L, gc_cb_idx); // func libs priority globals package required gc_cb lane uv k gc_cb - lua_rawset( L, -3); // func libs priority globals package required gc_cb lane uv + push_unique_key( L, GCCB_KEY); // func libs priority globals package required gc_cb lane uv k + lua_pushvalue( L, gc_cb_idx); // func libs priority globals package required gc_cb lane uv k gc_cb + lua_rawset( L, -3); // func libs priority globals package required gc_cb lane uv } - lua_setiuservalue( L, -2, 1); // func libs priority globals package required gc_cb lane + lua_setiuservalue( L, -2, 1); // func libs priority globals package required gc_cb lane // Store 's' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive). REGISTRY_SET( L2, CANCEL_TEST_KEY, lua_pushlightuserdata( L2, s)); // func [... args ...] diff --git a/src/threading.c b/src/threading.c index 183dc87..2b68503 100644 --- a/src/threading.c +++ b/src/threading.c @@ -323,9 +323,12 @@ void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (__stdcall *func)( void*), vo FAIL( "CreateThread", GetLastError()); } - if (!SetThreadPriority( h, gs_prio_remap[prio + 3])) + if (prio != THREAD_PRIO_DEFAULT) { - FAIL( "SetThreadPriority", GetLastError()); + if (!SetThreadPriority( h, gs_prio_remap[prio + 3])) + { + FAIL( "SetThreadPriority", GetLastError()); + } } *ref = h; @@ -681,7 +684,7 @@ static int const gs_prio_remap[] = // least OS specific settings. Hopefully, Linuxes and OS X versions // are uniform enough, among each other... // -# if defined PLATFORM_OSX +# if defined PLATFORM_OSX // AK 10-Apr-07 (OS X PowerPC 10.4.9): // // With SCHED_RR, 26 seems to be the "normal" priority, where setting @@ -694,85 +697,92 @@ static int const gs_prio_remap[] = // priority limits. This could imply, user mode applications won't // be able to use values outside of that range. // -# define _PRIO_MODE SCHED_OTHER +# define _PRIO_MODE SCHED_OTHER - // OS X 10.4.9 (PowerPC) gives ENOTSUP for process scope - //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS + // OS X 10.4.9 (PowerPC) gives ENOTSUP for process scope + //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS -# define _PRIO_HI 32 // seems to work (_carefully_ picked!) -# define _PRIO_0 26 // detected -# define _PRIO_LO 1 // seems to work (tested) +# define _PRIO_HI 32 // seems to work (_carefully_ picked!) +# define _PRIO_0 26 // detected +# define _PRIO_LO 1 // seems to work (tested) -# elif defined PLATFORM_LINUX - // (based on Ubuntu Linux 2.6.15 kernel) - // - // SCHED_OTHER is the default policy, but does not allow for priorities. - // SCHED_RR allows priorities, all of which (1..99) are higher than - // a thread with SCHED_OTHER policy. - // - // - // - // - // - // Manuals suggest checking #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING, - // but even Ubuntu does not seem to define it. - // -# define _PRIO_MODE SCHED_RR +# elif defined PLATFORM_LINUX + // (based on Ubuntu Linux 2.6.15 kernel) + // + // SCHED_OTHER is the default policy, but does not allow for priorities. + // SCHED_RR allows priorities, all of which (1..99) are higher than + // a thread with SCHED_OTHER policy. + // + // + // + // + // + // Manuals suggest checking #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING, + // but even Ubuntu does not seem to define it. + // +# define _PRIO_MODE SCHED_RR - // NTLP 2.5: only system scope allowed (being the basic reason why - // root privileges are required..) - //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS + // NTLP 2.5: only system scope allowed (being the basic reason why + // root privileges are required..) + //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS -# define _PRIO_HI 99 -# define _PRIO_0 50 -# define _PRIO_LO 1 +# define _PRIO_HI 99 +# define _PRIO_0 50 +# define _PRIO_LO 1 -# elif defined(PLATFORM_BSD) - // - // - // - // "When control over the thread scheduling is desired, then FreeBSD - // with the libpthread implementation is by far the best choice .." - // -# define _PRIO_MODE SCHED_OTHER -# define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS -# define _PRIO_HI 31 -# define _PRIO_0 15 -# define _PRIO_LO 1 +# elif defined(PLATFORM_BSD) + // + // + // + // "When control over the thread scheduling is desired, then FreeBSD + // with the libpthread implementation is by far the best choice .." + // +# define _PRIO_MODE SCHED_OTHER +# define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS +# define _PRIO_HI 31 +# define _PRIO_0 15 +# define _PRIO_LO 1 -# elif defined(PLATFORM_CYGWIN) - // - // TBD: Find right values for Cygwin - // -# elif defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) - // any other value not supported by win32-pthread as of version 2.9.1 -# define _PRIO_MODE SCHED_OTHER +# elif defined(PLATFORM_CYGWIN) + // + // TBD: Find right values for Cygwin + // +# elif defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) + // any other value not supported by win32-pthread as of version 2.9.1 +# define _PRIO_MODE SCHED_OTHER - // PTHREAD_SCOPE_PROCESS not supported by win32-pthread as of version 2.9.1 - //#define _PRIO_SCOPE PTHREAD_SCOPE_SYSTEM // but do we need this at all to start with? - THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_TIME_CRITICAL + // PTHREAD_SCOPE_PROCESS not supported by win32-pthread as of version 2.9.1 + //#define _PRIO_SCOPE PTHREAD_SCOPE_SYSTEM // but do we need this at all to start with? + THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_TIME_CRITICAL -# else -# error "Unknown OS: not implemented!" -# endif +# else +# error "Unknown OS: not implemented!" +# 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 }; +static int select_prio(int prio /* -3..+3 */) +{ + if (prio == THREAD_PRIO_DEFAULT) + prio = 0; + // prio range [-3,+3] was checked by the caller + return gs_prio_remap[prio + 3]; +} + 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 = + bool_t const change_priority = #if defined(PLATFORM_LINUX) && defined(LINUX_SCHED_RR) - !sudo; // with sudo, even normal thread must use SCHED_RR -#else - (prio == 0); + sudo && // with sudo, even normal thread must use SCHED_RR #endif + (prio != THREAD_PRIO_DEFAULT); PT_CALL( pthread_attr_init( &a)); @@ -800,7 +810,7 @@ void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, PT_CALL( pthread_attr_setstacksize( &a, _THREAD_STACK_SIZE)); #endif - if( !normal) + if (change_priority) { struct sched_param sp; // "The specified scheduling parameters are only used if the scheduling @@ -816,8 +826,7 @@ void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, PT_CALL( pthread_attr_setschedpolicy( &a, _PRIO_MODE)); - // prio range [-3,+3] was checked by the caller - sp.sched_priority = gs_prio_remap[ prio + 3]; + sp.sched_priority = select_prio(prio); PT_CALL( pthread_attr_setschedparam( &a, &sp)); } diff --git a/src/threading.h b/src/threading.h index 1224e08..3925076 100644 --- a/src/threading.h +++ b/src/threading.h @@ -32,8 +32,10 @@ enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; #define THREADAPI_PTHREAD 2 #if( defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)) && !defined( HAVE_WIN32_PTHREAD) +//#pragma message ( "THREADAPI_WINDOWS" ) #define THREADAPI THREADAPI_WINDOWS #else // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) +//#pragma message ( "THREADAPI_PTHREAD" ) #define THREADAPI THREADAPI_PTHREAD #endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) @@ -169,6 +171,8 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout ); /*---=== Threading ===--- */ +#define THREAD_PRIO_DEFAULT (-999) + #if THREADAPI == THREADAPI_WINDOWS typedef HANDLE THREAD_T; -- cgit v1.2.3-55-g6feb