293 lines
10 KiB
Diff
293 lines
10 KiB
Diff
|
commit 6a033150c36bea6d704b7537c219e9b13b4387ec
|
||
|
Author: Rémi Bernon <rbernon@codeweavers.com>
|
||
|
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.h>]], [[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 <sys/resource.h>
|
||
|
+#include <sys/time.h>]], [[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 <sched.h>
|
||
|
#endif
|
||
|
+#ifdef HAVE_SYS_TIME_H
|
||
|
+#include <sys/time.h>
|
||
|
+#endif
|
||
|
+#ifdef HAVE_SYS_RESOURCE_H
|
||
|
+#include <sys/resource.h>
|
||
|
+#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 );
|