commit 6a033150c36bea6d704b7537c219e9b13b4387ec Author: RĂ©mi Bernon Date: Tue Dec 1 22:49:26 2020 +0100 Subject: [PATCH 1/3] server: Implement thread priorities on Linux. This does not report permission errors in order to avoid any breaking change, only the parameter checks that were already there are returning errors. Only call setpriority on Linux, as unix_tid is a Mach port on Mac OS. --- a/configure.ac +++ b/configure.ac @@ -2086,6 +2086,25 @@ 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 + dnl **** Check for types **** AC_C_INLINE --- a/server/process.c +++ b/server/process.c @@ -1638,6 +1638,24 @@ release_object( process ); } +static void set_process_priority( struct process *process, int priority ) +{ + struct thread *thread; + + if (!process->running_threads) + { + set_error( STATUS_PROCESS_IS_TERMINATING ); + return; + } + + LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry ) + { + set_thread_priority( thread, priority, thread->priority ); + } + + process->priority = priority; +} + static void set_process_affinity( struct process *process, affinity_t affinity ) { struct thread *thread; @@ -1663,7 +1681,7 @@ if ((process = get_process_from_handle( req->handle, PROCESS_SET_INFORMATION ))) { - if (req->mask & SET_PROCESS_INFO_PRIORITY) process->priority = req->priority; + if (req->mask & SET_PROCESS_INFO_PRIORITY) set_process_priority( process, req->priority ); if (req->mask & SET_PROCESS_INFO_AFFINITY) set_process_affinity( process, req->affinity ); release_object( process ); } --- a/server/thread.c +++ b/server/thread.c @@ -37,6 +37,12 @@ #define _WITH_CPU_SET_T #include #endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif #include "ntstatus.h" #define WIN32_NO_STATUS @@ -253,6 +259,7 @@ thread->state = RUNNING; thread->exit_code = 0; thread->priority = 0; + thread->priority_applied = 0; thread->suspend = 0; thread->dbg_hidden = 0; thread->desktop_users = 0; @@ -648,31 +655,151 @@ 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_class, int priority ) +{ + int max = THREAD_PRIORITY_HIGHEST; + int min = THREAD_PRIORITY_LOWEST; + if (priority_class == 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; + } + + if (thread->process->priority == priority_class && + thread->priority == priority && + thread->priority_applied) + return 0; + + thread->priority = priority; + thread->priority_applied = 0; + if (thread->unix_tid == -1) + return 0; + +#ifdef __linux__ + if (priority_class == PROCESS_PRIOCLASS_REALTIME) + { +#ifdef HAVE_SCHED_SETSCHEDULER + struct sched_param param; + if (sched_getparam( thread->unix_tid, ¶m ) != 0) + return 0; /* ignore errors for now */ + + param.sched_priority = get_unix_priority( priority_class, 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( priority_class, priority ) ) == 0) + return 0; +#endif + } +#endif + + return 0; /* ignore errors for now */ +} + /* 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; - set_scheduler_priority( thread ); - } - else - set_error( STATUS_INVALID_PARAMETER ); + if (set_thread_priority( thread, thread->process->priority, req->priority )) + file_set_error(); } if (req->mask & SET_THREAD_INFO_AFFINITY) { @@ -1541,6 +1668,7 @@ init_thread_context( current ); generate_debug_event( current, DbgCreateThreadStateChange, &req->entry ); + set_thread_priority( current, current->process->priority, current->priority ); set_thread_affinity( current, current->affinity ); reply->suspend = (current->suspend || current->process->suspend || current->context != NULL); --- a/server/thread.h +++ b/server/thread.h @@ -84,6 +84,7 @@ client_ptr_t entry_point; /* entry point (in client address space) */ affinity_t affinity; /* affinity mask */ int priority; /* priority level */ + int priority_applied; /* priority level successfully applied status */ int suspend; /* suspend count */ int dbg_hidden; /* hidden from debugger */ obj_handle_t desktop; /* desktop handle */ @@ -124,6 +125,7 @@ extern int thread_add_inflight_fd( struct thread *thread, int client, int server ); extern int thread_get_inflight_fd( struct thread *thread, int client ); extern struct token *thread_get_impersonation_token( struct thread *thread ); +extern int set_thread_priority( struct thread *thread, int priority_class, int priority ); extern int set_thread_affinity( struct thread *thread, affinity_t affinity ); extern int suspend_thread( struct thread *thread ); extern int resume_thread( struct thread *thread );