Merge branch 'thread-prios' into 'master'

Server-side thread priorities implementation (Part 1)

See merge request wine/wine!4551
This commit is contained in:
Marc-Aurel Zent 2024-11-18 22:22:24 +00:00
commit f16404d202
5 changed files with 99 additions and 13 deletions

View file

@ -2178,6 +2178,9 @@ DECLSPEC_EXPORT void __wine_main( int argc, char *argv[] )
#ifdef RLIMIT_AS
set_max_limit( RLIMIT_AS );
#endif
#ifdef RLIMIT_NICE
set_max_limit( RLIMIT_NICE );
#endif
virtual_init();
init_environment();

View file

@ -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;

View file

@ -287,6 +287,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,

View file

@ -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
@ -216,6 +219,50 @@ static const struct fd_ops thread_fd_ops =
static struct list thread_list = LIST_INIT(thread_list);
#if defined(__linux__) && defined(RLIMIT_NICE)
static int nice_limit;
void init_threading(void)
{
struct rlimit rlimit;
/* 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 );
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");
}
if (nice_limit < 0 && debug_level) fprintf(stderr, "wine: Using setpriority to control niceness in the [%d,%d] range\n", nice_limit, -nice_limit );
}
static void apply_thread_priority( struct thread *thread, int base_priority )
{
int min = -nice_limit, max = nice_limit, range = max - min, niceness;
/* FIXME: handle realtime priorities using SCHED_RR if possible */
if (base_priority > THREAD_BASE_PRIORITY_LOWRT) base_priority = THREAD_BASE_PRIORITY_LOWRT;
/* map an NT application band [1,15] base priority to [-nice_limit, nice_limit] */
niceness = (min + (base_priority - 1) * range / 14);
setpriority( PRIO_PROCESS, thread->unix_tid, niceness );
}
#else
void init_threading(void)
{
}
static void apply_thread_priority( struct thread *thread, int base_priority )
{
}
#endif
/* initialize the structure for a newly allocated thread */
static inline void init_thread_structure( struct thread *thread )
{
@ -605,28 +652,55 @@ 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;
}
#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 min = THREAD_PRIORITY_LOWEST, max = THREAD_PRIORITY_HIGHEST, base_priority;
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)
return STATUS_INVALID_PARAMETER;
if (thread->state == TERMINATED)
return STATUS_THREAD_IS_TERMINATING;
thread->priority = priority;
/* if unix_tid == -1, thread is gone or hasn't started yet, this will be called again from init_thread with a unix_tid */
if (thread->unix_tid == -1)
return STATUS_SUCCESS;
base_priority = get_base_priority( priority_class, priority );
apply_thread_priority( thread, base_priority );
return STATUS_SUCCESS;
}
/* 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 );
int status = set_thread_priority( thread, thread->process->priority, req->priority );
if (status) set_error( status );
}
if (req->mask & SET_THREAD_INFO_AFFINITY)
{
@ -1422,6 +1496,8 @@ DECL_HANDLER(init_first_thread)
else
set_thread_affinity( current, current->affinity );
set_thread_priority( current, process->priority, current->priority );
debug_level = max( debug_level, req->debug_level );
reply->pid = get_process_id( process );
@ -1451,6 +1527,7 @@ DECL_HANDLER(init_thread)
init_thread_context( current );
generate_debug_event( current, DbgCreateThreadStateChange, &req->entry );
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);

View file

@ -122,6 +122,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 );