[PATCH v3 1/2] wineserver: Implement thread priorities with sched_setscheduler / setpriority.

Rémi Bernon rbernon at codeweavers.com
Wed Oct 9 04:01:17 CDT 2019


This does not report permission errors in order to avoid any breaking
change, only the parameter checks that were already there are returning
errors.

We only call setpriority on Linux, as unix_tid is a Mach port on Mac OS.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 configure.ac        |  19 ++++++
 include/config.h.in |   6 ++
 server/process.c    |  20 +++++-
 server/thread.c     | 154 ++++++++++++++++++++++++++++++++++++++++----
 server/thread.h     |   1 +
 5 files changed, 186 insertions(+), 14 deletions(-)

diff --git a/configure.ac b/configure.ac
index b9339b90aa7..47c1f7c5315 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2285,6 +2285,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.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
+
 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 9c7a49ae9a2..929b11a8754 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -785,6 +785,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
 
@@ -809,6 +812,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/process.c b/server/process.c
index 16bb5d57e72..0c544b46096 100644
--- a/server/process.c
+++ b/server/process.c
@@ -1465,6 +1465,24 @@ DECL_HANDLER(get_process_vm_counters)
     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;
@@ -1490,7 +1508,7 @@ DECL_HANDLER(set_process_info)
 
     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 );
     }
diff --git a/server/thread.c b/server/thread.c
index e753c8d0dda..36bd41e977f 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -38,6 +38,12 @@
 #ifdef HAVE_SCHED_H
 #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
@@ -515,28 +521,149 @@ 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_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)
+        return 0;
+
+    thread->priority = priority;
+    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, &param ) != 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, &param ) == 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;
-        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)
     {
@@ -1367,6 +1494,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->process->priority, current->priority );
         set_thread_affinity( current, current->affinity );
     }
     debug_level = max( debug_level, req->debug_level );
diff --git a/server/thread.h b/server/thread.h
index e10120dcf6e..b81061aea11 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -125,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 thread_snapshot *thread_snap( int *count );
 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 is_cpu_supported( enum cpu_type cpu );
 extern unsigned int get_supported_cpu_mask(void);
-- 
2.23.0




More information about the wine-devel mailing list