From 106856dd83dd36de1829c84e52a542788b47461a Mon Sep 17 00:00:00 2001 From: Marc-Aurel Zent Date: Sun, 28 Jan 2024 10:06:26 +0100 Subject: [PATCH] add thread priorities --- .../wine-tkg-userpatches/thread-prios.mypatch | 246 ++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 wine-tkg-git/wine-tkg-userpatches/thread-prios.mypatch diff --git a/wine-tkg-git/wine-tkg-userpatches/thread-prios.mypatch b/wine-tkg-git/wine-tkg-userpatches/thread-prios.mypatch new file mode 100644 index 000000000..4d78fe8a7 --- /dev/null +++ b/wine-tkg-git/wine-tkg-userpatches/thread-prios.mypatch @@ -0,0 +1,246 @@ +diff --git a/configure.ac b/configure.ac +index 475743bc121..e3e6bd75f5d 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2117,6 +2117,16 @@ then + AC_DEFINE(HAVE_SCHED_SETAFFINITY, 1, [Define to 1 if you have the `sched_setaffinity' function.]) + fi + ++AC_CACHE_CHECK([for setpriority],wine_cv_have_setpriority, ++ AC_LINK_IFELSE([AC_LANG_PROGRAM( ++[[#define _GNU_SOURCE ++#include ++#include ]], [[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 +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 803d8079213..40c55d58f76 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -2151,6 +2151,9 @@ DECLSPEC_EXPORT void __wine_main( int argc, char *argv[], char *envp[] ) + #ifdef RLIMIT_AS + set_max_limit( RLIMIT_AS ); + #endif ++#ifdef RLIMIT_NICE ++ set_max_limit( RLIMIT_NICE ); ++#endif + + virtual_init(); + init_environment(); +diff --git a/server/main.c b/server/main.c +index 1248b92f24d..e014ec535ff 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -234,6 +234,7 @@ int main( int argc, char *argv[] ) + init_signals(); + init_memory(); + init_directories( load_intl_file() ); ++ init_threading(); + init_registry(); + main_loop(); + return 0; +diff --git a/server/object.h b/server/object.h +index dfdd691601f..66012fbc4af 100644 +--- a/server/object.h ++++ b/server/object.h +@@ -277,6 +277,10 @@ extern struct object *get_directory_obj( struct process *process, obj_handle_t h + extern int directory_link_name( struct object *obj, struct object_name *name, struct object *parent ); + extern void init_directories( struct fd *intl_fd ); + ++/* thread functions */ ++ ++extern void init_threading(void); ++ + /* symbolic link functions */ + + extern struct object *create_root_symlink( struct object *root, const struct unicode_str *name, +diff --git a/server/thread.c b/server/thread.c +index 56f57cefd8f..14911a3dace 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -37,6 +37,9 @@ + #define _WITH_CPU_SET_T + #include + #endif ++#ifdef HAVE_SYS_RESOURCE_H ++#include ++#endif + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -215,6 +218,34 @@ static const struct fd_ops thread_fd_ops = + }; + + static struct list thread_list = LIST_INIT(thread_list); ++#ifdef __linux__ ++static int nice_limit; ++#endif ++ ++void init_threading(void) ++{ ++#ifdef __linux__ ++#ifdef RLIMIT_NICE ++ struct rlimit rlimit; ++#endif ++#ifdef HAVE_SETPRIORITY ++ /* if wineserver has cap_sys_nice we are unlimited, but leave -20 to the user */ ++ if (!setpriority( PRIO_PROCESS, getpid(), -20 )) nice_limit = -19; ++ setpriority( PRIO_PROCESS, getpid(), 0 ); ++#endif ++#ifdef RLIMIT_NICE ++ if (!nice_limit && !getrlimit( RLIMIT_NICE, &rlimit )) ++ { ++ rlimit.rlim_cur = rlimit.rlim_max; ++ setrlimit( RLIMIT_NICE, &rlimit ); ++ if (rlimit.rlim_max <= 40) nice_limit = 20 - rlimit.rlim_max; ++ else if (rlimit.rlim_max == -1) nice_limit = -20; ++ if (nice_limit >= 0 && debug_level) fprintf(stderr, "wine: RLIMIT_NICE is <= 20, unable to use setpriority safely\n"); ++ } ++#endif ++ if (nice_limit < 0 && debug_level) fprintf(stderr, "wine: Using setpriority to control niceness in the [%d,%d] range\n", nice_limit, -nice_limit ); ++#endif ++} + + /* initialize the structure for a newly allocated thread */ + static inline void init_thread_structure( struct thread *thread ) +@@ -603,31 +634,84 @@ affinity_t get_thread_affinity( struct thread *thread ) + return mask; + } + ++static int get_base_priority( int priority_class, int priority ) ++{ ++ /* offsets taken from https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities */ ++ static const int class_offsets[] = { 4, 8, 13, 24, 6, 10 }; ++ if (priority == THREAD_PRIORITY_IDLE) return (priority_class == PROCESS_PRIOCLASS_REALTIME ? 16 : 1); ++ if (priority == THREAD_PRIORITY_TIME_CRITICAL) return (priority_class == PROCESS_PRIOCLASS_REALTIME ? 31 : 15); ++ if (priority_class >= ARRAY_SIZE(class_offsets)) return 8; ++ return class_offsets[priority_class - 1] + priority; ++} ++ ++#ifdef __linux__ ++/* maps an NT application band [1,15] base priority to [-nice_limit, nice_limit] */ ++static int get_unix_niceness( int base_priority ) ++{ ++ int min = -nice_limit, max = nice_limit, range = max - min; ++ return min + (base_priority - 1) * range / 14; ++} ++#endif ++ + #define THREAD_PRIORITY_REALTIME_HIGHEST 6 + #define THREAD_PRIORITY_REALTIME_LOWEST -7 + ++static void apply_thread_priority( struct thread *thread, int priority_class, int priority ) ++{ ++ int base_priority = get_base_priority( priority_class, priority ); ++#ifdef __linux__ ++ int niceness; ++ ++ if (thread->unix_tid == -1) return; ++ ++ /* FIXME: handle REALTIME class using SCHED_RR if possible, for now map it to highest non-realtime band */ ++ if (priority_class == PROCESS_PRIOCLASS_REALTIME) base_priority = 15; ++#ifdef HAVE_SETPRIORITY ++ if (nice_limit < 0) ++ { ++ niceness = get_unix_niceness( base_priority ); ++ if (setpriority( PRIO_PROCESS, thread->unix_tid, niceness ) != 0) ++ fprintf( stderr, "wine: setpriority %d for pid %d failed: %d\n", niceness, thread->unix_tid, errno ); ++ return; ++ } ++#endif ++#endif ++} ++ ++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; ++ ++ apply_thread_priority( thread, priority_class, priority ); ++ 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; +- 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) + { +@@ -1413,7 +1500,10 @@ DECL_HANDLER(init_first_thread) + if (!process->parent_id) + process->affinity = current->affinity = get_thread_affinity( current ); + else if (!process->cpu_override.cpu_count) ++ { ++ set_thread_priority( current, current->process->priority, current->priority ); + set_thread_affinity( current, current->affinity ); ++ } + + debug_level = max( debug_level, req->debug_level ); + +@@ -1444,7 +1534,10 @@ DECL_HANDLER(init_thread) + + init_thread_context( current ); + generate_debug_event( current, DbgCreateThreadStateChange, &req->entry ); + if (!process->cpu_override.cpu_count) ++ { ++ 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); +diff --git a/server/thread.h b/server/thread.h +index 8dcf966a90a..b0237c3a80e 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -119,6 +119,7 @@ extern void thread_cancel_apc( struct thread *thread, struct object *owner, enum + 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 );