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

Rémi Bernon rbernon at codeweavers.com
Mon Jul 8 08:46:51 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.

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     | 152 ++++++++++++++++++++++++++++++++++++++++----
 server/thread.h     |   1 +
 5 files changed, 184 insertions(+), 14 deletions(-)

diff --git a/configure.ac b/configure.ac
index a7c45ace738..4eca35b06f5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2234,6 +2234,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 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/process.c b/server/process.c
index b67bd88da47..bf216a76f78 100644
--- a/server/process.c
+++ b/server/process.c
@@ -1464,6 +1464,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;
@@ -1489,7 +1507,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 d5742a1bf96..1b8819bf93f 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
@@ -516,28 +522,147 @@ 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;
+
+    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
+    }
+
+    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)
     {
@@ -1368,6 +1493,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 40f0eecbf1b..5e58a1bc683 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -126,6 +126,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.20.1




More information about the wine-devel mailing list