[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, ¶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
+ }
+
+ 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