Skip to content

Commit

Permalink
add thread priorities
Browse files Browse the repository at this point in the history
  • Loading branch information
marzent committed Jan 28, 2024
1 parent ddec39d commit 106856d
Showing 1 changed file with 246 additions and 0 deletions.
246 changes: 246 additions & 0 deletions wine-tkg-git/wine-tkg-userpatches/thread-prios.mypatch
Original file line number Diff line number Diff line change
@@ -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 <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
+
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 <sched.h>
#endif
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#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 );

0 comments on commit 106856d

Please sign in to comment.