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

Rémi Bernon rbernon at codeweavers.com
Wed Jul 3 04:41:18 CDT 2019


This does not really check for errors in order to avoid introducing
breaking changes.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 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 <sched.h>
+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 <sys/resource.h>
+#include <sys/time.h>
+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.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/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 <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
@@ -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, &param ) != 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, &param ) == 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




More information about the wine-devel mailing list