mirror of
https://gitlab.winehq.org/wine/wine.git
synced 2024-11-21 17:09:06 -07:00
Merge branch 'thread-prios' into 'master'
Server-side thread priorities implementation (Part 1) See merge request wine/wine!4551
This commit is contained in:
commit
f16404d202
5 changed files with 99 additions and 13 deletions
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
103
server/thread.c
103
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
|
||||
|
@ -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);
|
||||
|
|
|
@ -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 );
|
||||
|
|
Loading…
Reference in a new issue