From: Rémi Bernon Subject: [PATCH 1/2] wineserver: Implement thread priorities on Unix with sched_setscheduler / setpriority Message-Id: <20190703094119.17048-1-rbernon@codeweavers.com> Date: Wed, 3 Jul 2019 11:41:18 +0200 This does not really check for errors in order to avoid introducing breaking changes. Signed-off-by: Rémi Bernon --- configure | 69 ++++++++++++++++++++ configure.ac | 19 ++++++ include/config.h.in | 6 ++ server/thread.c | 149 ++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 230 insertions(+), 13 deletions(-) diff --git a/configure b/configure index 3b6d744c391..e89be467e2e 100755 --- a/configure +++ b/configure @@ -18065,6 +18065,75 @@ $as_echo "#define HAVE_SCHED_SETAFFINITY 1" >>confdefs.h fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sched_setscheduler" >&5 +$as_echo_n "checking for sched_setscheduler... " >&6; } +if ${wine_cv_have_sched_setscheduler+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _GNU_SOURCE +#include +int +main () +{ +sched_setscheduler(0, 0, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + wine_cv_have_sched_setscheduler=yes +else + wine_cv_have_sched_setscheduler=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $wine_cv_have_sched_setscheduler" >&5 +$as_echo "$wine_cv_have_sched_setscheduler" >&6; } +if test "$wine_cv_have_sched_setscheduler" = "yes" +then + +$as_echo "#define HAVE_SCHED_SETSCHEDULER 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for setpriority" >&5 +$as_echo_n "checking for setpriority... " >&6; } +if ${wine_cv_have_setpriority+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _GNU_SOURCE +#include +#include +int +main () +{ +setpriority(0, 0, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + wine_cv_have_setpriority=yes +else + wine_cv_have_setpriority=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $wine_cv_have_setpriority" >&5 +$as_echo "$wine_cv_have_setpriority" >&6; } +if test "$wine_cv_have_setpriority" = "yes" +then + +$as_echo "#define HAVE_SETPRIORITY 1" >>confdefs.h + +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fallocate" >&5 $as_echo_n "checking for fallocate... " >&6; } if ${wine_cv_have_fallocate+:} false; then : diff --git a/configure.ac b/configure.ac index 709b43b9478..4a3ff36e6b7 100644 --- a/configure.ac +++ b/configure.ac @@ -2233,6 +2233,25 @@ then AC_DEFINE(HAVE_SCHED_SETAFFINITY, 1, [Define to 1 if you have the `sched_setaffinity' function.]) fi +AC_CACHE_CHECK([for sched_setscheduler],wine_cv_have_sched_setscheduler, + AC_LINK_IFELSE([AC_LANG_PROGRAM( +[[#define _GNU_SOURCE +#include ]], [[sched_setscheduler(0, 0, 0);]])],[wine_cv_have_sched_setscheduler=yes],[wine_cv_have_sched_setscheduler=no])) +if test "$wine_cv_have_sched_setscheduler" = "yes" +then + AC_DEFINE(HAVE_SCHED_SETSCHEDULER, 1, [Define to 1 if you have the `sched_setscheduler' function.]) +fi + +AC_CACHE_CHECK([for setpriority],wine_cv_have_setpriority, + AC_LINK_IFELSE([AC_LANG_PROGRAM( +[[#define _GNU_SOURCE +#include +#include ]], [[setpriority(0, 0, 0);]])],[wine_cv_have_setpriority=yes],[wine_cv_have_setpriority=no])) +if test "$wine_cv_have_setpriority" = "yes" +then + AC_DEFINE(HAVE_SETPRIORITY, 1, [Define to 1 if you have the `setpriority' function.]) +fi + AC_CACHE_CHECK([for fallocate],wine_cv_have_fallocate, AC_LINK_IFELSE([AC_LANG_PROGRAM( [[#define _GNU_SOURCE diff --git a/include/config.h.in b/include/config.h.in index ce5c1570541..1acc02b7173 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -788,6 +788,9 @@ /* Define to 1 if you have the `sched_setaffinity' function. */ #undef HAVE_SCHED_SETAFFINITY +/* Define to 1 if you have the `sched_setscheduler' function. */ +#undef HAVE_SCHED_SETSCHEDULER + /* Define to 1 if you have the `sched_yield' function. */ #undef HAVE_SCHED_YIELD @@ -812,6 +815,9 @@ /* Define to 1 if you have the `select' function. */ #undef HAVE_SELECT +/* Define to 1 if you have the `setpriority' function. */ +#undef HAVE_SETPRIORITY + /* Define to 1 if you have the `setproctitle' function. */ #undef HAVE_SETPROCTITLE diff --git a/server/thread.c b/server/thread.c index 7057c9bbd0c..5b5f3cde617 100644 --- a/server/thread.c +++ b/server/thread.c @@ -38,6 +38,12 @@ #ifdef HAVE_SCHED_H #include #endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif #include "ntstatus.h" #define WIN32_NO_STATUS @@ -517,28 +523,144 @@ affinity_t get_thread_affinity( struct thread *thread ) return mask; } +#if defined(HAVE_SCHED_SETSCHEDULER) || defined(HAVE_SETPRIORITY) +static int get_unix_priority( int priority_class, int priority ) +{ + switch (priority_class) { + case PROCESS_PRIOCLASS_IDLE: + switch (priority) { + case THREAD_PRIORITY_IDLE: return 15; + case THREAD_PRIORITY_LOWEST: return 10; + case THREAD_PRIORITY_BELOW_NORMAL: return 8; + case THREAD_PRIORITY_NORMAL: return 6; + case THREAD_PRIORITY_ABOVE_NORMAL: return 4; + case THREAD_PRIORITY_HIGHEST: return 2; + case THREAD_PRIORITY_TIME_CRITICAL: return -15; + } + case PROCESS_PRIOCLASS_BELOW_NORMAL: + switch (priority) { + case THREAD_PRIORITY_IDLE: return 15; + case THREAD_PRIORITY_LOWEST: return 8; + case THREAD_PRIORITY_BELOW_NORMAL: return 6; + case THREAD_PRIORITY_NORMAL: return 4; + case THREAD_PRIORITY_ABOVE_NORMAL: return 2; + case THREAD_PRIORITY_HIGHEST: return 0; + case THREAD_PRIORITY_TIME_CRITICAL: return -15; + } + case PROCESS_PRIOCLASS_NORMAL: + switch (priority) { + case THREAD_PRIORITY_IDLE: return 15; + case THREAD_PRIORITY_LOWEST: return 4; + case THREAD_PRIORITY_BELOW_NORMAL: return 2; + case THREAD_PRIORITY_NORMAL: return 0; + case THREAD_PRIORITY_ABOVE_NORMAL: return -2; + case THREAD_PRIORITY_HIGHEST: return -4; + case THREAD_PRIORITY_TIME_CRITICAL: return -15; + } + case PROCESS_PRIOCLASS_ABOVE_NORMAL: + switch (priority) { + case THREAD_PRIORITY_IDLE: return 15; + case THREAD_PRIORITY_LOWEST: return 0; + case THREAD_PRIORITY_BELOW_NORMAL: return -2; + case THREAD_PRIORITY_NORMAL: return -4; + case THREAD_PRIORITY_ABOVE_NORMAL: return -6; + case THREAD_PRIORITY_HIGHEST: return -8; + case THREAD_PRIORITY_TIME_CRITICAL: return -15; + } + case PROCESS_PRIOCLASS_HIGH: + switch (priority) { + case THREAD_PRIORITY_IDLE: return 15; + case THREAD_PRIORITY_LOWEST: return -2; + case THREAD_PRIORITY_BELOW_NORMAL: return -4; + case THREAD_PRIORITY_NORMAL: return -6; + case THREAD_PRIORITY_ABOVE_NORMAL: return -8; + case THREAD_PRIORITY_HIGHEST: return -10; + case THREAD_PRIORITY_TIME_CRITICAL: return -15; + } + case PROCESS_PRIOCLASS_REALTIME: + switch (priority) { + case THREAD_PRIORITY_IDLE: return 1; + case -7: + case -6: + case -5: + case -4: + case -3: + case THREAD_PRIORITY_LOWEST: + case THREAD_PRIORITY_BELOW_NORMAL: + case THREAD_PRIORITY_NORMAL: + case THREAD_PRIORITY_ABOVE_NORMAL: + case THREAD_PRIORITY_HIGHEST: + case 3: + case 4: + case 5: + case 6: + return priority + 9; + case THREAD_PRIORITY_TIME_CRITICAL: + return 16; + } + } + return 0; +} +#endif + #define THREAD_PRIORITY_REALTIME_HIGHEST 6 #define THREAD_PRIORITY_REALTIME_LOWEST -7 +int set_thread_priority( struct thread* thread, int priority ) +{ + int max = THREAD_PRIORITY_HIGHEST; + int min = THREAD_PRIORITY_LOWEST; + if (thread->process->priority == PROCESS_PRIOCLASS_REALTIME) + { + max = THREAD_PRIORITY_REALTIME_HIGHEST; + min = THREAD_PRIORITY_REALTIME_LOWEST; + } + + if ((priority < min || priority > max) && + priority != THREAD_PRIORITY_IDLE && + priority != THREAD_PRIORITY_TIME_CRITICAL) + { + errno = EINVAL; + return -1; + } + + thread->priority = priority; + if (thread->unix_tid == -1) + return 0; + + if (thread->process->priority == PROCESS_PRIOCLASS_REALTIME) + { +#ifdef HAVE_SCHED_SETSCHEDULER + struct sched_param param; + if (sched_getparam( thread->unix_tid, ¶m ) != 0) + goto error; + + param.sched_priority = get_unix_priority( thread->process->priority, priority ); + if (sched_setscheduler( thread->unix_tid, SCHED_RR|SCHED_RESET_ON_FORK, ¶m ) == 0) + return 0; +#endif + } + else + { +#ifdef HAVE_SETPRIORITY + if (setpriority( PRIO_PROCESS, thread->unix_tid, + get_unix_priority( thread->process->priority, priority ) ) == 0) + return 0; +#endif + } + +error: + return 0; +} + /* set all information about a thread */ static void set_thread_info( struct thread *thread, const struct set_thread_info_request *req ) { if (req->mask & SET_THREAD_INFO_PRIORITY) { - int max = THREAD_PRIORITY_HIGHEST; - int min = THREAD_PRIORITY_LOWEST; - if (thread->process->priority == PROCESS_PRIOCLASS_REALTIME) - { - max = THREAD_PRIORITY_REALTIME_HIGHEST; - min = THREAD_PRIORITY_REALTIME_LOWEST; - } - if ((req->priority >= min && req->priority <= max) || - req->priority == THREAD_PRIORITY_IDLE || - req->priority == THREAD_PRIORITY_TIME_CRITICAL) - thread->priority = req->priority; - else - set_error( STATUS_INVALID_PARAMETER ); + if (set_thread_priority( thread, req->priority )) + file_set_error(); } if (req->mask & SET_THREAD_INFO_AFFINITY) { @@ -1402,6 +1524,7 @@ DECL_HANDLER(init_thread) process->unix_pid = -1; /* can happen with linuxthreads */ init_thread_context( current ); generate_debug_event( current, CREATE_THREAD_DEBUG_EVENT, &req->entry ); + set_thread_priority( current, current->priority ); set_thread_affinity( current, current->affinity ); } debug_level = max( debug_level, req->debug_level ); -- 2.20.1