This is the continuation of https://gitlab.winehq.org/wine/wine/-/merge_requests/7317.
There are a lot of smaller commits (some even no-op renames), because there are some inconsistencies in the codebase currently of what an NT thread priority vs a base thread priority should be and I hope that this clears that up a bit.
I tried making the commits as atomic as possible I hope this is fine... There are still a few tiny details missing like fixing up the `list_processes` request and `KeSetPriorityThread` and friends which are either stubs or not correctly working as well.
There are also probably a few tests that can be written here (after thread priority boosting has been disabled, cause that makes the behavior on windows very flaky and dynamic). In fact on windows there is some thread priority boost decay going on after the message has been processed (and probably other boosting mechanisms), which this MR does not properly capture, but are also probably not reasonable to implement.
This also reverts the commits of https://gitlab.winehq.org/wine/wine/-/merge_requests/1232, which are being effectively overwritten anyways.
The `get_thread_priority_info` request was added, because the reply in `get_thread_info` would be otherwise larger than 64 bytes, so it had to be split.
-- v6: ntdll/tests: Add tests for process and thread priority. server, ntdll: Fetch both process priority and base_priority. server: Add process base priority helper. kernelbase: Use ProcessPriorityClass info class in GetPriorityClass. kernelbase: Use correct priority in GetThreadPriority. server, ntdll: Fetch both thread priority and base_priority. server, ntdll: Move last thread information to get_thread_info flags. ntdll: Implement ThreadPriority class in NtSetInformationThread. server: Implement setting thread priority directly. server: Add set_thread_priority helper. server: Rename thread priority to base_priority. server: Rename base_priority to effective_priority in apply_thread_priority.
From: Marc-Aurel Zent mzent@codeweavers.com
--- include/winnt.h | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/include/winnt.h b/include/winnt.h index 722d2c3a542..d9f7b197da7 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -5358,6 +5358,11 @@ typedef enum tagSID_NAME_USE { #define THREAD_BASE_PRIORITY_MIN -2 #define THREAD_BASE_PRIORITY_IDLE -15
+#define LOW_PRIORITY 0 /* Lowest thread priority level */ +#define LOW_REALTIME_PRIORITY 16 /* Lowest realtime priority level */ +#define HIGH_PRIORITY 31 /* Highest thread priority level */ +#define MAXIMUM_PRIORITY 32 /* Number of thread priority levels */ + typedef struct _QUOTA_LIMITS { SIZE_T PagedPoolLimit; SIZE_T NonPagedPoolLimit;
From: Marc-Aurel Zent mzent@codeweavers.com
This matches publicly available windows headers now. --- dlls/kernel32/tests/loader.c | 2 +- dlls/kernelbase/process.c | 24 ++++++++++++------------ dlls/ntdll/tests/info.c | 4 ++-- include/winternl.h | 13 +++++++------ server/process.c | 2 +- server/thread.c | 6 +++--- 6 files changed, 26 insertions(+), 25 deletions(-)
diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c index 5eef938ca1f..9082e7212f8 100644 --- a/dlls/kernel32/tests/loader.c +++ b/dlls/kernel32/tests/loader.c @@ -4092,7 +4092,7 @@ static void test_ExitProcess(void) ret = pNtSetInformationProcess(pi.hProcess, ProcessAffinityMask, &affinity, sizeof(affinity)); ok(ret == STATUS_PROCESS_IS_TERMINATING, "expected STATUS_PROCESS_IS_TERMINATING, got %#lx\n", ret); ppc.Foreground = FALSE; - ppc.PriorityClass = PROCESS_PRIOCLASS_BELOW_NORMAL; + ppc.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL; ret = pNtSetInformationProcess(pi.hProcess, ProcessPriorityClass, &ppc, sizeof(ppc)); ok(ret == STATUS_SUCCESS, "expected STATUS_SUCCESS, got status %#lx\n", ret);
diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index df3447a9014..8d4c68e9a0d 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -829,12 +829,12 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetPriorityClass( HANDLE process )
switch (pbi.BasePriority) { - case PROCESS_PRIOCLASS_IDLE: return IDLE_PRIORITY_CLASS; - case PROCESS_PRIOCLASS_BELOW_NORMAL: return BELOW_NORMAL_PRIORITY_CLASS; - case PROCESS_PRIOCLASS_NORMAL: return NORMAL_PRIORITY_CLASS; - case PROCESS_PRIOCLASS_ABOVE_NORMAL: return ABOVE_NORMAL_PRIORITY_CLASS; - case PROCESS_PRIOCLASS_HIGH: return HIGH_PRIORITY_CLASS; - case PROCESS_PRIOCLASS_REALTIME: return REALTIME_PRIORITY_CLASS; + case PROCESS_PRIORITY_CLASS_IDLE: return IDLE_PRIORITY_CLASS; + case PROCESS_PRIORITY_CLASS_BELOW_NORMAL: return BELOW_NORMAL_PRIORITY_CLASS; + case PROCESS_PRIORITY_CLASS_NORMAL: return NORMAL_PRIORITY_CLASS; + case PROCESS_PRIORITY_CLASS_ABOVE_NORMAL: return ABOVE_NORMAL_PRIORITY_CLASS; + case PROCESS_PRIORITY_CLASS_HIGH: return HIGH_PRIORITY_CLASS; + case PROCESS_PRIORITY_CLASS_REALTIME: return REALTIME_PRIORITY_CLASS; default: return 0; } } @@ -1198,12 +1198,12 @@ BOOL WINAPI DECLSPEC_HOTPATCH SetPriorityClass( HANDLE process, DWORD class ) ppc.Foreground = FALSE; switch (class) { - case IDLE_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIOCLASS_IDLE; break; - case BELOW_NORMAL_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIOCLASS_BELOW_NORMAL; break; - case NORMAL_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIOCLASS_NORMAL; break; - case ABOVE_NORMAL_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIOCLASS_ABOVE_NORMAL; break; - case HIGH_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIOCLASS_HIGH; break; - case REALTIME_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIOCLASS_REALTIME; break; + case IDLE_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE; break; + case BELOW_NORMAL_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL; break; + case NORMAL_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL; break; + case ABOVE_NORMAL_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL; break; + case HIGH_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; break; + case REALTIME_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIORITY_CLASS_REALTIME; break; default: SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index 5003a5b50c2..ff7fbad49d4 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -2305,8 +2305,8 @@ static void test_query_process_priority(void)
status = NtQueryInformationProcess(GetCurrentProcess(), ProcessPriorityClass, &priority, sizeof(priority[0]), &ReturnLength); ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); - ok(priority[0].PriorityClass == PROCESS_PRIOCLASS_BELOW_NORMAL, - "Expected PROCESS_PRIOCLASS_BELOW_NORMAL, got %u\n", priority[0].PriorityClass); + ok(priority[0].PriorityClass == PROCESS_PRIORITY_CLASS_BELOW_NORMAL, + "Expected PROCESS_PRIORITY_CLASS_BELOW_NORMAL, got %u\n", priority[0].PriorityClass);
ret = SetPriorityClass(GetCurrentProcess(), orig_priority); ok(ret, "Failed to reset priority class: %lu\n", GetLastError()); diff --git a/include/winternl.h b/include/winternl.h index d6fe4171fd5..4c419bd6949 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -2652,12 +2652,13 @@ typedef struct _PROCESS_BASIC_INFORMATION64 { UINT64 InheritedFromUniqueProcessId; } PROCESS_BASIC_INFORMATION64;
-#define PROCESS_PRIOCLASS_IDLE 1 -#define PROCESS_PRIOCLASS_NORMAL 2 -#define PROCESS_PRIOCLASS_HIGH 3 -#define PROCESS_PRIOCLASS_REALTIME 4 -#define PROCESS_PRIOCLASS_BELOW_NORMAL 5 -#define PROCESS_PRIOCLASS_ABOVE_NORMAL 6 +#define PROCESS_PRIORITY_CLASS_UNKNOWN 0 +#define PROCESS_PRIORITY_CLASS_IDLE 1 +#define PROCESS_PRIORITY_CLASS_NORMAL 2 +#define PROCESS_PRIORITY_CLASS_HIGH 3 +#define PROCESS_PRIORITY_CLASS_REALTIME 4 +#define PROCESS_PRIORITY_CLASS_BELOW_NORMAL 5 +#define PROCESS_PRIORITY_CLASS_ABOVE_NORMAL 6
typedef struct _PROCESS_PRIORITY_CLASS { BOOLEAN Foreground; diff --git a/server/process.c b/server/process.c index c877bb6f7ae..c8c08c834d4 100644 --- a/server/process.c +++ b/server/process.c @@ -661,7 +661,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla process->unix_pid = -1; process->exit_code = STILL_ACTIVE; process->running_threads = 0; - process->priority = PROCESS_PRIOCLASS_NORMAL; + process->priority = PROCESS_PRIORITY_CLASS_NORMAL; process->suspend = 0; process->is_system = 0; process->debug_children = 1; diff --git a/server/thread.c b/server/thread.c index 5ad060108e3..51c89f029cb 100644 --- a/server/thread.c +++ b/server/thread.c @@ -775,8 +775,8 @@ static int get_base_priority( int priority_class, int priority ) /* offsets taken from https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priori... */ 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 == THREAD_PRIORITY_IDLE) return (priority_class == PROCESS_PRIORITY_CLASS_REALTIME ? 16 : 1); + if (priority == THREAD_PRIORITY_TIME_CRITICAL) return (priority_class == PROCESS_PRIORITY_CLASS_REALTIME ? 31 : 15); if (priority_class >= ARRAY_SIZE(class_offsets)) return 8; return class_offsets[priority_class - 1] + priority; } @@ -789,7 +789,7 @@ unsigned int set_thread_priority( struct thread *thread, int priority_class, int int max = THREAD_PRIORITY_HIGHEST; int min = THREAD_PRIORITY_LOWEST;
- if (priority_class == PROCESS_PRIOCLASS_REALTIME) + if (priority_class == PROCESS_PRIORITY_CLASS_REALTIME) { max = THREAD_PRIORITY_REALTIME_HIGHEST; min = THREAD_PRIORITY_REALTIME_LOWEST;
From: Marc-Aurel Zent mzent@codeweavers.com
--- server/process.c | 2 +- server/thread.c | 9 +++++---- server/thread.h | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/server/process.c b/server/process.c index c8c08c834d4..d970e01a4cf 100644 --- a/server/process.c +++ b/server/process.c @@ -1641,7 +1641,7 @@ void set_process_priority( struct process *process, int priority )
LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry ) { - set_thread_priority( thread, priority, thread->priority ); + set_thread_priority( thread, thread->priority ); } }
diff --git a/server/thread.c b/server/thread.c index 51c89f029cb..27abe882eb4 100644 --- a/server/thread.c +++ b/server/thread.c @@ -784,8 +784,9 @@ static int get_base_priority( int priority_class, int priority ) #define THREAD_PRIORITY_REALTIME_HIGHEST 6 #define THREAD_PRIORITY_REALTIME_LOWEST -7
-unsigned int set_thread_priority( struct thread *thread, int priority_class, int priority ) +unsigned int set_thread_priority( struct thread *thread, int priority ) { + int priority_class = thread->process->priority; int max = THREAD_PRIORITY_HIGHEST; int min = THREAD_PRIORITY_LOWEST;
@@ -814,7 +815,7 @@ static void set_thread_info( struct thread *thread, { if (req->mask & SET_THREAD_INFO_PRIORITY) { - unsigned int status = set_thread_priority( thread, thread->process->priority, req->priority ); + unsigned int status = set_thread_priority( thread, req->priority ); if (status) set_error( status ); } if (req->mask & SET_THREAD_INFO_AFFINITY) @@ -1611,7 +1612,7 @@ DECL_HANDLER(init_first_thread) else set_thread_affinity( current, current->affinity );
- set_thread_priority( current, process->priority, current->priority ); + set_thread_priority( current, current->priority );
debug_level = max( debug_level, req->debug_level );
@@ -1642,7 +1643,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_priority( current, 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 2b256be322f..7549243e88b 100644 --- a/server/thread.h +++ b/server/thread.h @@ -122,7 +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 unsigned int set_thread_priority( struct thread *thread, int priority_class, int priority ); +extern unsigned int set_thread_priority( struct thread *thread, 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 );
From: Marc-Aurel Zent mzent@codeweavers.com
This also uses the correct NT thread priority constants where possible. --- server/thread.c | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-)
diff --git a/server/thread.c b/server/thread.c index 27abe882eb4..513230e8c85 100644 --- a/server/thread.c +++ b/server/thread.c @@ -247,14 +247,14 @@ void init_threading(void) 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 ) +static void apply_thread_priority( struct thread *thread, int effective_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); + if (effective_priority >= LOW_REALTIME_PRIORITY) effective_priority = LOW_REALTIME_PRIORITY - 1; + /* map an NT application band [1,15] priority to [-nice_limit, nice_limit] */ + niceness = (min + (effective_priority - 1) * range / 14); setpriority( PRIO_PROCESS, thread->unix_tid, niceness ); }
@@ -275,13 +275,13 @@ void init_threading(void) } }
-static int get_mach_importance( int base_priority ) +static int get_mach_importance( int effective_priority ) { int min = -31, max = 32, range = max - min; - return min + (base_priority - 1) * range / 14; + return min + (effective_priority - 1) * range / 14; }
-static void apply_thread_priority( struct thread *thread, int base_priority ) +static void apply_thread_priority( struct thread *thread, int effective_priority ) { kern_return_t kr; mach_msg_type_name_t type; @@ -294,9 +294,9 @@ static void apply_thread_priority( struct thread *thread, int base_priority ) kr = mach_port_extract_right( process_port, thread->unix_tid, MACH_MSG_TYPE_COPY_SEND, &thread_port, &type ); if (kr != KERN_SUCCESS) return; - /* base priority 15 is for time-critical threads, so not compute-bound */ - thread_extended_policy.timeshare = base_priority > 14 ? 0 : 1; - thread_precedence_policy.importance = get_mach_importance( base_priority ); + /* effective priority 15 is for time-critical threads, so not compute-bound */ + thread_extended_policy.timeshare = effective_priority > 14 ? 0 : 1; + thread_precedence_policy.importance = get_mach_importance( effective_priority ); /* adapted from the QoS table at xnu/osfmk/kern/thread_policy.c */ switch (thread->priority) { @@ -325,7 +325,7 @@ static void apply_thread_priority( struct thread *thread, int base_priority ) break; } /* QOS_UNSPECIFIED is assigned the highest tier available, so it does not provide a limit */ - if (base_priority > THREAD_BASE_PRIORITY_LOWRT) + if (effective_priority >= LOW_REALTIME_PRIORITY) { throughput_qos = THROUGHPUT_QOS_TIER_UNSPECIFIED; latency_qos = LATENCY_QOS_TIER_UNSPECIFIED; @@ -339,7 +339,7 @@ static void apply_thread_priority( struct thread *thread, int base_priority ) THREAD_EXTENDED_POLICY_COUNT ); thread_policy_set( thread_port, THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thread_precedence_policy, THREAD_PRECEDENCE_POLICY_COUNT ); - if (base_priority > THREAD_BASE_PRIORITY_LOWRT) + if (effective_priority >= LOW_REALTIME_PRIORITY) { /* For realtime threads we are requesting from the scheduler to be moved * into the Mach realtime band (96-127) above the kernel. @@ -354,14 +354,14 @@ static void apply_thread_priority( struct thread *thread, int base_priority ) * importance), which is on XNU equivalent to setting SCHED_RR with the * pthread API. */ struct thread_time_constraint_policy thread_time_constraint_policy; - int realtime_priority = base_priority - THREAD_BASE_PRIORITY_LOWRT; + int realtime_priority = effective_priority - LOW_REALTIME_PRIORITY + 1; unsigned int max_constraint = mach_ticks_per_second / 2; unsigned int max_computation = max_constraint / 10; /* unfortunately we can't give a hint for the periodicity of calculations */ thread_time_constraint_policy.period = 0; thread_time_constraint_policy.constraint = max_constraint; thread_time_constraint_policy.computation = realtime_priority * max_computation / 16; - thread_time_constraint_policy.preemptible = thread->priority == THREAD_PRIORITY_TIME_CRITICAL ? 0 : 1; + thread_time_constraint_policy.preemptible = effective_priority == HIGH_PRIORITY ? 0 : 1; thread_policy_set( thread_port, THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t)&thread_time_constraint_policy, THREAD_TIME_CONSTRAINT_POLICY_COUNT ); @@ -375,7 +375,7 @@ void init_threading(void) { }
-static void apply_thread_priority( struct thread *thread, int base_priority ) +static void apply_thread_priority( struct thread *thread, int effective_priority ) { }
@@ -770,14 +770,17 @@ affinity_t get_thread_affinity( struct thread *thread ) return mask; }
-static int get_base_priority( int priority_class, int priority ) +static int thread_priority_from_class_and_level( int priority_class, int priority ) { /* offsets taken from https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priori... */ static const int class_offsets[] = { 4, 8, 13, 24, 6, 10 };
- if (priority == THREAD_PRIORITY_IDLE) return (priority_class == PROCESS_PRIORITY_CLASS_REALTIME ? 16 : 1); - if (priority == THREAD_PRIORITY_TIME_CRITICAL) return (priority_class == PROCESS_PRIORITY_CLASS_REALTIME ? 31 : 15); - if (priority_class >= ARRAY_SIZE(class_offsets)) return 8; + if (priority == THREAD_PRIORITY_IDLE) + return (priority_class == PROCESS_PRIORITY_CLASS_REALTIME ? LOW_REALTIME_PRIORITY : LOW_PRIORITY + 1); + if (priority == THREAD_PRIORITY_TIME_CRITICAL) + return (priority_class == PROCESS_PRIORITY_CLASS_REALTIME ? HIGH_PRIORITY : LOW_REALTIME_PRIORITY - 1); + if (priority_class >= ARRAY_SIZE(class_offsets)) + return LOW_REALTIME_PRIORITY / 2; return class_offsets[priority_class - 1] + priority; }
@@ -804,7 +807,7 @@ unsigned int set_thread_priority( struct thread *thread, int priority )
/* if thread is gone or hasn't started yet, this will be called again from init_thread with a unix_tid */ if (thread->state == RUNNING && thread->unix_tid != -1) - apply_thread_priority( thread, get_base_priority( priority_class, priority )); + apply_thread_priority( thread, thread_priority_from_class_and_level( priority_class, priority ));
return STATUS_SUCCESS; }
From: Marc-Aurel Zent mzent@codeweavers.com
--- dlls/ntdll/unix/thread.c | 6 +++--- server/process.c | 6 +++--- server/protocol.def | 14 +++++++------- server/thread.c | 34 +++++++++++++++++----------------- server/thread.h | 4 ++-- 5 files changed, 32 insertions(+), 32 deletions(-)
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index b64a7dd40af..8a4a53d93a1 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -2371,9 +2371,9 @@ NTSTATUS WINAPI NtSetInformationThread( HANDLE handle, THREADINFOCLASS class, if (length != sizeof(DWORD)) return STATUS_INVALID_PARAMETER; SERVER_START_REQ( set_thread_info ) { - req->handle = wine_server_obj_handle( handle ); - req->priority = *pprio; - req->mask = SET_THREAD_INFO_PRIORITY; + req->handle = wine_server_obj_handle( handle ); + req->base_priority = *pprio; + req->mask = SET_THREAD_INFO_BASE_PRIORITY; status = wine_server_call( req ); } SERVER_END_REQ; diff --git a/server/process.c b/server/process.c index d970e01a4cf..b8896a224ff 100644 --- a/server/process.c +++ b/server/process.c @@ -1641,7 +1641,7 @@ void set_process_priority( struct process *process, int priority )
LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry ) { - set_thread_priority( thread, thread->priority ); + set_thread_base_priority( thread, thread->base_priority ); } }
@@ -2013,8 +2013,8 @@ DECL_HANDLER(list_processes)
thread_info->start_time = thread->creation_time; thread_info->tid = thread->id; - thread_info->base_priority = thread->priority; - thread_info->current_priority = thread->priority; /* FIXME */ + thread_info->base_priority = thread->base_priority; + thread_info->current_priority = thread->base_priority; /* FIXME */ thread_info->unix_tid = thread->unix_tid; thread_info->entry_point = thread->entry_point; thread_info->teb = thread->teb; diff --git a/server/protocol.def b/server/protocol.def index 4f712b4e4e6..39e74724cb6 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1245,18 +1245,18 @@ struct obj_locator @REQ(set_thread_info) obj_handle_t handle; /* thread handle */ int mask; /* setting mask (see below) */ - int priority; /* priority class */ + int base_priority;/* base priority class */ affinity_t affinity; /* affinity mask */ client_ptr_t entry_point; /* thread entry point */ obj_handle_t token; /* impersonation token */ VARARG(desc,unicode_str); /* description string */ @END -#define SET_THREAD_INFO_PRIORITY 0x01 -#define SET_THREAD_INFO_AFFINITY 0x02 -#define SET_THREAD_INFO_TOKEN 0x04 -#define SET_THREAD_INFO_ENTRYPOINT 0x08 -#define SET_THREAD_INFO_DESCRIPTION 0x10 -#define SET_THREAD_INFO_DBG_HIDDEN 0x20 +#define SET_THREAD_INFO_BASE_PRIORITY 0x01 +#define SET_THREAD_INFO_AFFINITY 0x02 +#define SET_THREAD_INFO_TOKEN 0x04 +#define SET_THREAD_INFO_ENTRYPOINT 0x08 +#define SET_THREAD_INFO_DESCRIPTION 0x10 +#define SET_THREAD_INFO_DBG_HIDDEN 0x20
/* Suspend a thread */ diff --git a/server/thread.c b/server/thread.c index 513230e8c85..d1acace4980 100644 --- a/server/thread.c +++ b/server/thread.c @@ -298,7 +298,7 @@ static void apply_thread_priority( struct thread *thread, int effective_priority thread_extended_policy.timeshare = effective_priority > 14 ? 0 : 1; thread_precedence_policy.importance = get_mach_importance( effective_priority ); /* adapted from the QoS table at xnu/osfmk/kern/thread_policy.c */ - switch (thread->priority) + switch (thread->base_priority) { case THREAD_PRIORITY_IDLE: /* THREAD_QOS_MAINTENANCE */ case THREAD_PRIORITY_LOWEST: /* THREAD_QOS_BACKGROUND */ @@ -404,7 +404,7 @@ static inline void init_thread_structure( struct thread *thread ) thread->wait_fd = NULL; thread->state = RUNNING; thread->exit_code = 0; - thread->priority = 0; + thread->base_priority = 0; thread->suspend = 0; thread->dbg_hidden = 0; thread->desktop_users = 0; @@ -770,24 +770,24 @@ affinity_t get_thread_affinity( struct thread *thread ) return mask; }
-static int thread_priority_from_class_and_level( int priority_class, int priority ) +static int thread_priority_from_class_and_level( int priority_class, int base_priority ) { /* offsets taken from https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priori... */ static const int class_offsets[] = { 4, 8, 13, 24, 6, 10 };
- if (priority == THREAD_PRIORITY_IDLE) + if (base_priority == THREAD_PRIORITY_IDLE) return (priority_class == PROCESS_PRIORITY_CLASS_REALTIME ? LOW_REALTIME_PRIORITY : LOW_PRIORITY + 1); - if (priority == THREAD_PRIORITY_TIME_CRITICAL) + if (base_priority == THREAD_PRIORITY_TIME_CRITICAL) return (priority_class == PROCESS_PRIORITY_CLASS_REALTIME ? HIGH_PRIORITY : LOW_REALTIME_PRIORITY - 1); if (priority_class >= ARRAY_SIZE(class_offsets)) return LOW_REALTIME_PRIORITY / 2; - return class_offsets[priority_class - 1] + priority; + return class_offsets[priority_class - 1] + base_priority; }
#define THREAD_PRIORITY_REALTIME_HIGHEST 6 #define THREAD_PRIORITY_REALTIME_LOWEST -7
-unsigned int set_thread_priority( struct thread *thread, int priority ) +unsigned int set_thread_base_priority( struct thread *thread, int base_priority ) { int priority_class = thread->process->priority; int max = THREAD_PRIORITY_HIGHEST; @@ -798,16 +798,16 @@ unsigned int set_thread_priority( struct thread *thread, int priority ) max = THREAD_PRIORITY_REALTIME_HIGHEST; min = THREAD_PRIORITY_REALTIME_LOWEST; } - if ((priority < min || priority > max) && - priority != THREAD_PRIORITY_IDLE && - priority != THREAD_PRIORITY_TIME_CRITICAL) + if ((base_priority < min || base_priority > max) && + base_priority != THREAD_PRIORITY_IDLE && + base_priority != THREAD_PRIORITY_TIME_CRITICAL) return STATUS_INVALID_PARAMETER;
- thread->priority = priority; + thread->base_priority = base_priority;
/* if thread is gone or hasn't started yet, this will be called again from init_thread with a unix_tid */ if (thread->state == RUNNING && thread->unix_tid != -1) - apply_thread_priority( thread, thread_priority_from_class_and_level( priority_class, priority )); + apply_thread_priority( thread, thread_priority_from_class_and_level( priority_class, base_priority ));
return STATUS_SUCCESS; } @@ -816,9 +816,9 @@ unsigned int set_thread_priority( struct thread *thread, int priority ) static void set_thread_info( struct thread *thread, const struct set_thread_info_request *req ) { - if (req->mask & SET_THREAD_INFO_PRIORITY) + if (req->mask & SET_THREAD_INFO_BASE_PRIORITY) { - unsigned int status = set_thread_priority( thread, req->priority ); + unsigned int status = set_thread_base_priority( thread, req->base_priority ); if (status) set_error( status ); } if (req->mask & SET_THREAD_INFO_AFFINITY) @@ -1615,7 +1615,7 @@ DECL_HANDLER(init_first_thread) else set_thread_affinity( current, current->affinity );
- set_thread_priority( current, current->priority ); + set_thread_base_priority( current, current->base_priority );
debug_level = max( debug_level, req->debug_level );
@@ -1646,7 +1646,7 @@ DECL_HANDLER(init_thread)
init_thread_context( current ); generate_debug_event( current, DbgCreateThreadStateChange, &req->entry ); - set_thread_priority( current, current->priority ); + set_thread_base_priority( current, current->base_priority ); set_thread_affinity( current, current->affinity );
reply->suspend = (current->suspend || current->process->suspend || current->context != NULL); @@ -1695,7 +1695,7 @@ DECL_HANDLER(get_thread_info) reply->teb = thread->teb; reply->entry_point = thread->entry_point; reply->exit_code = (thread->state == TERMINATED) ? thread->exit_code : STATUS_PENDING; - reply->priority = thread->priority; + reply->priority = thread->base_priority; reply->affinity = thread->affinity; reply->last = thread->process->running_threads == 1; reply->suspend_count = thread->suspend; diff --git a/server/thread.h b/server/thread.h index 7549243e88b..419f5987ad4 100644 --- a/server/thread.h +++ b/server/thread.h @@ -81,7 +81,7 @@ struct thread client_ptr_t teb; /* TEB address (in client address space) */ client_ptr_t entry_point; /* entry point (in client address space) */ affinity_t affinity; /* affinity mask */ - int priority; /* priority level */ + int base_priority; /* priority level */ int suspend; /* suspend count */ int dbg_hidden; /* hidden from debugger */ obj_handle_t desktop; /* desktop handle */ @@ -122,7 +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 unsigned int set_thread_priority( struct thread *thread, int priority ); +extern unsigned int set_thread_base_priority( struct thread *thread, int base_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 );
From: Marc-Aurel Zent mzent@codeweavers.com
--- server/thread.c | 27 +++++++++++++++++++++------ server/thread.h | 4 +++- 2 files changed, 24 insertions(+), 7 deletions(-)
diff --git a/server/thread.c b/server/thread.c index d1acace4980..500e248a8a7 100644 --- a/server/thread.c +++ b/server/thread.c @@ -404,6 +404,7 @@ static inline void init_thread_structure( struct thread *thread ) thread->wait_fd = NULL; thread->state = RUNNING; thread->exit_code = 0; + thread->priority = 0; thread->base_priority = 0; thread->suspend = 0; thread->dbg_hidden = 0; @@ -770,6 +771,25 @@ affinity_t get_thread_affinity( struct thread *thread ) return mask; }
+unsigned int set_thread_priority( struct thread *thread, int priority ) +{ + int priority_class = thread->process->priority; + + if (priority < LOW_PRIORITY + 1 || priority > HIGH_PRIORITY) + return STATUS_INVALID_PARAMETER; + + if (priority_class != PROCESS_PRIORITY_CLASS_REALTIME && priority >= LOW_REALTIME_PRIORITY) + return STATUS_PRIVILEGE_NOT_HELD; + + thread->priority = priority; + + /* if thread is gone or hasn't started yet, this will be called again from init_thread with a unix_tid */ + if (thread->state == RUNNING && thread->unix_tid != -1) + apply_thread_priority( thread, priority ); + + return STATUS_SUCCESS; +} + static int thread_priority_from_class_and_level( int priority_class, int base_priority ) { /* offsets taken from https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priori... */ @@ -804,12 +824,7 @@ unsigned int set_thread_base_priority( struct thread *thread, int base_priority return STATUS_INVALID_PARAMETER;
thread->base_priority = base_priority; - - /* if thread is gone or hasn't started yet, this will be called again from init_thread with a unix_tid */ - if (thread->state == RUNNING && thread->unix_tid != -1) - apply_thread_priority( thread, thread_priority_from_class_and_level( priority_class, base_priority )); - - return STATUS_SUCCESS; + return set_thread_priority( thread, thread_priority_from_class_and_level( priority_class, base_priority ) ); }
/* set all information about a thread */ diff --git a/server/thread.h b/server/thread.h index 419f5987ad4..0857d50020c 100644 --- a/server/thread.h +++ b/server/thread.h @@ -81,7 +81,8 @@ struct thread client_ptr_t teb; /* TEB address (in client address space) */ client_ptr_t entry_point; /* entry point (in client address space) */ affinity_t affinity; /* affinity mask */ - int base_priority; /* priority level */ + int priority; /* priority level */ + int base_priority; /* base priority class */ int suspend; /* suspend count */ int dbg_hidden; /* hidden from debugger */ obj_handle_t desktop; /* desktop handle */ @@ -122,6 +123,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 unsigned int set_thread_priority( struct thread *thread, int priority ); extern unsigned int set_thread_base_priority( struct thread *thread, int base_priority ); extern int set_thread_affinity( struct thread *thread, affinity_t affinity ); extern int suspend_thread( struct thread *thread );
From: Marc-Aurel Zent mzent@codeweavers.com
--- server/protocol.def | 14 ++++++++------ server/thread.c | 5 +++++ 2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/server/protocol.def b/server/protocol.def index 39e74724cb6..4d2ea609bbc 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1245,18 +1245,20 @@ struct obj_locator @REQ(set_thread_info) obj_handle_t handle; /* thread handle */ int mask; /* setting mask (see below) */ + int priority; /* NT thread priority */ int base_priority;/* base priority class */ affinity_t affinity; /* affinity mask */ client_ptr_t entry_point; /* thread entry point */ obj_handle_t token; /* impersonation token */ VARARG(desc,unicode_str); /* description string */ @END -#define SET_THREAD_INFO_BASE_PRIORITY 0x01 -#define SET_THREAD_INFO_AFFINITY 0x02 -#define SET_THREAD_INFO_TOKEN 0x04 -#define SET_THREAD_INFO_ENTRYPOINT 0x08 -#define SET_THREAD_INFO_DESCRIPTION 0x10 -#define SET_THREAD_INFO_DBG_HIDDEN 0x20 +#define SET_THREAD_INFO_PRIORITY 0x01 +#define SET_THREAD_INFO_BASE_PRIORITY 0x02 +#define SET_THREAD_INFO_AFFINITY 0x04 +#define SET_THREAD_INFO_TOKEN 0x08 +#define SET_THREAD_INFO_ENTRYPOINT 0x10 +#define SET_THREAD_INFO_DESCRIPTION 0x20 +#define SET_THREAD_INFO_DBG_HIDDEN 0x40
/* Suspend a thread */ diff --git a/server/thread.c b/server/thread.c index 500e248a8a7..644e3e05198 100644 --- a/server/thread.c +++ b/server/thread.c @@ -831,6 +831,11 @@ unsigned int set_thread_base_priority( struct thread *thread, int base_priority static void set_thread_info( struct thread *thread, const struct set_thread_info_request *req ) { + if (req->mask & SET_THREAD_INFO_PRIORITY) + { + unsigned int status = set_thread_priority( thread, req->priority ); + if (status) set_error( status ); + } if (req->mask & SET_THREAD_INFO_BASE_PRIORITY) { unsigned int status = set_thread_base_priority( thread, req->base_priority );
From: Marc-Aurel Zent mzent@codeweavers.com
--- dlls/ntdll/unix/thread.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 8a4a53d93a1..b8e6ba65854 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -2365,6 +2365,21 @@ NTSTATUS WINAPI NtSetInformationThread( HANDLE handle, THREADINFOCLASS class, return status; }
+ case ThreadPriority: + { + const DWORD *pprio = data; + if (length != sizeof(DWORD)) return STATUS_INVALID_PARAMETER; + SERVER_START_REQ( set_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->priority = *pprio; + req->mask = SET_THREAD_INFO_PRIORITY; + status = wine_server_call( req ); + } + SERVER_END_REQ; + return status; + } + case ThreadBasePriority: { const DWORD *pprio = data; @@ -2542,7 +2557,6 @@ NTSTATUS WINAPI NtSetInformationThread( HANDLE handle, THREADINFOCLASS class,
case ThreadBasicInformation: case ThreadTimes: - case ThreadPriority: case ThreadDescriptorTableEntry: case ThreadEventPair_Reusable: case ThreadPerformanceCount:
From: Marc-Aurel Zent mzent@codeweavers.com
--- dlls/ntdll/unix/thread.c | 2 +- server/protocol.def | 2 +- server/thread.c | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index b8e6ba65854..9a852f90332 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -2153,7 +2153,7 @@ NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class, status = wine_server_call( req ); if (status == STATUS_SUCCESS) { - ULONG last = reply->last; + ULONG last = !!(reply->flags & GET_THREAD_INFO_FLAG_LAST); if (data) memcpy( data, &last, sizeof(last) ); if (ret_len) *ret_len = sizeof(last); } diff --git a/server/protocol.def b/server/protocol.def index 4d2ea609bbc..eaf4a0b9d52 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1220,7 +1220,6 @@ struct obj_locator affinity_t affinity; /* thread affinity mask */ int exit_code; /* thread exit code */ int priority; /* thread priority level */ - int last; /* last thread in process */ int suspend_count; /* thread suspend count */ unsigned int flags; /* GET_THREAD_INFO_FLAG_ flags */ data_size_t desc_len; /* description length in bytes */ @@ -1228,6 +1227,7 @@ struct obj_locator @END #define GET_THREAD_INFO_FLAG_DBG_HIDDEN 0x01 #define GET_THREAD_INFO_FLAG_TERMINATED 0x02 +#define GET_THREAD_INFO_FLAG_LAST 0x04
/* Retrieve information about thread times */ diff --git a/server/thread.c b/server/thread.c index 644e3e05198..898a650348c 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1717,7 +1717,6 @@ DECL_HANDLER(get_thread_info) reply->exit_code = (thread->state == TERMINATED) ? thread->exit_code : STATUS_PENDING; reply->priority = thread->base_priority; reply->affinity = thread->affinity; - reply->last = thread->process->running_threads == 1; reply->suspend_count = thread->suspend; reply->desc_len = thread->desc_len; reply->flags = 0; @@ -1725,6 +1724,8 @@ DECL_HANDLER(get_thread_info) reply->flags |= GET_THREAD_INFO_FLAG_DBG_HIDDEN; if (thread->state == TERMINATED) reply->flags |= GET_THREAD_INFO_FLAG_TERMINATED; + if (thread->process->running_threads == 1) + reply->flags |= GET_THREAD_INFO_FLAG_LAST;
if (thread->desc && get_reply_max_size()) {
From: Marc-Aurel Zent mzent@codeweavers.com
--- dlls/ntdll/unix/thread.c | 2 +- server/process.c | 2 +- server/protocol.def | 3 ++- server/thread.c | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 9a852f90332..d1ee7c3944a 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -2061,7 +2061,7 @@ NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class, info.ClientId.UniqueThread = ULongToHandle(reply->tid); info.AffinityMask = reply->affinity & affinity_mask; info.Priority = reply->priority; - info.BasePriority = reply->priority; /* FIXME */ + info.BasePriority = reply->base_priority; } } SERVER_END_REQ; diff --git a/server/process.c b/server/process.c index b8896a224ff..09cfa4406c6 100644 --- a/server/process.c +++ b/server/process.c @@ -2014,7 +2014,7 @@ DECL_HANDLER(list_processes) thread_info->start_time = thread->creation_time; thread_info->tid = thread->id; thread_info->base_priority = thread->base_priority; - thread_info->current_priority = thread->base_priority; /* FIXME */ + thread_info->current_priority = thread->priority; thread_info->unix_tid = thread->unix_tid; thread_info->entry_point = thread->entry_point; thread_info->teb = thread->teb; diff --git a/server/protocol.def b/server/protocol.def index eaf4a0b9d52..e672cd0d759 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1219,7 +1219,8 @@ struct obj_locator client_ptr_t entry_point; /* thread entry point */ affinity_t affinity; /* thread affinity mask */ int exit_code; /* thread exit code */ - int priority; /* thread priority level */ + int priority; /* NT thread priority */ + int base_priority; /* base priority class */ int suspend_count; /* thread suspend count */ unsigned int flags; /* GET_THREAD_INFO_FLAG_ flags */ data_size_t desc_len; /* description length in bytes */ diff --git a/server/thread.c b/server/thread.c index 898a650348c..6494d0f17ed 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1715,7 +1715,8 @@ DECL_HANDLER(get_thread_info) reply->teb = thread->teb; reply->entry_point = thread->entry_point; reply->exit_code = (thread->state == TERMINATED) ? thread->exit_code : STATUS_PENDING; - reply->priority = thread->base_priority; + reply->priority = thread->priority; + reply->base_priority = thread->base_priority; reply->affinity = thread->affinity; reply->suspend_count = thread->suspend; reply->desc_len = thread->desc_len;
From: Marc-Aurel Zent mzent@codeweavers.com
--- dlls/kernelbase/thread.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/kernelbase/thread.c b/dlls/kernelbase/thread.c index 82096fd45f3..82ade671d1a 100644 --- a/dlls/kernelbase/thread.c +++ b/dlls/kernelbase/thread.c @@ -274,7 +274,7 @@ INT WINAPI DECLSPEC_HOTPATCH GetThreadPriority( HANDLE thread ) if (!set_ntstatus( NtQueryInformationThread( thread, ThreadBasicInformation, &info, sizeof(info), NULL ))) return THREAD_PRIORITY_ERROR_RETURN; - return info.Priority; + return info.BasePriority; }
From: Marc-Aurel Zent mzent@codeweavers.com
--- dlls/kernelbase/process.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 8d4c68e9a0d..462989c283f 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -821,13 +821,13 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetHandleInformation( HANDLE handle, DWORD *flags */ DWORD WINAPI DECLSPEC_HOTPATCH GetPriorityClass( HANDLE process ) { - PROCESS_BASIC_INFORMATION pbi; + PROCESS_PRIORITY_CLASS priority;
- if (!set_ntstatus( NtQueryInformationProcess( process, ProcessBasicInformation, - &pbi, sizeof(pbi), NULL ))) + if (!set_ntstatus( NtQueryInformationProcess( process, ProcessPriorityClass, + &priority, sizeof(priority), NULL ))) return 0;
- switch (pbi.BasePriority) + switch (priority.PriorityClass) { case PROCESS_PRIORITY_CLASS_IDLE: return IDLE_PRIORITY_CLASS; case PROCESS_PRIORITY_CLASS_BELOW_NORMAL: return BELOW_NORMAL_PRIORITY_CLASS;
From: Marc-Aurel Zent mzent@codeweavers.com
--- server/process.c | 23 +++++++++++++++++++++++ server/process.h | 1 + server/thread.c | 7 +------ 3 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/server/process.c b/server/process.c index 09cfa4406c6..8998be1bbe8 100644 --- a/server/process.c +++ b/server/process.c @@ -1633,6 +1633,29 @@ DECL_HANDLER(get_process_vm_counters) release_object( process ); }
+unsigned short get_process_base_priority( int priority_class ) +{ + /* offsets taken from https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priori... */ + switch (priority_class) + { + case PROCESS_PRIORITY_CLASS_IDLE: + return 4; + case PROCESS_PRIORITY_CLASS_BELOW_NORMAL: + return 6; + case PROCESS_PRIORITY_CLASS_NORMAL: + return 8; + case PROCESS_PRIORITY_CLASS_ABOVE_NORMAL: + return 10; + case PROCESS_PRIORITY_CLASS_HIGH: + return 13; + case PROCESS_PRIORITY_CLASS_REALTIME: + return 24; + case PROCESS_PRIORITY_CLASS_UNKNOWN: + default: + return 8; + } +} + void set_process_priority( struct process *process, int priority ) { struct thread *thread; diff --git a/server/process.h b/server/process.h index 9238d638f15..4d62745f209 100644 --- a/server/process.h +++ b/server/process.h @@ -116,6 +116,7 @@ extern void kill_process( struct process *process, int violent_death ); extern void kill_console_processes( struct thread *renderer, int exit_code ); extern void detach_debugged_processes( struct debug_obj *debug_obj, int exit_code ); extern void enum_processes( int (*cb)(struct process*, void*), void *user); +extern unsigned short get_process_base_priority( int priority_class ); extern void set_process_priority( struct process *process, int priority );
/* console functions */ diff --git a/server/thread.c b/server/thread.c index 6494d0f17ed..879f8fea120 100644 --- a/server/thread.c +++ b/server/thread.c @@ -792,16 +792,11 @@ unsigned int set_thread_priority( struct thread *thread, int priority )
static int thread_priority_from_class_and_level( int priority_class, int base_priority ) { - /* offsets taken from https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priori... */ - static const int class_offsets[] = { 4, 8, 13, 24, 6, 10 }; - if (base_priority == THREAD_PRIORITY_IDLE) return (priority_class == PROCESS_PRIORITY_CLASS_REALTIME ? LOW_REALTIME_PRIORITY : LOW_PRIORITY + 1); if (base_priority == THREAD_PRIORITY_TIME_CRITICAL) return (priority_class == PROCESS_PRIORITY_CLASS_REALTIME ? HIGH_PRIORITY : LOW_REALTIME_PRIORITY - 1); - if (priority_class >= ARRAY_SIZE(class_offsets)) - return LOW_REALTIME_PRIORITY / 2; - return class_offsets[priority_class - 1] + base_priority; + return get_process_base_priority( priority_class ) + base_priority; }
#define THREAD_PRIORITY_REALTIME_HIGHEST 6
From: Marc-Aurel Zent mzent@codeweavers.com
--- dlls/ntdll/unix/process.c | 4 ++-- server/process.c | 1 + server/protocol.def | 1 + 3 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index 533940280e6..4d5d23c47bb 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -1159,7 +1159,7 @@ NTSTATUS WINAPI NtQueryInformationProcess( HANDLE handle, PROCESSINFOCLASS class pbi.ExitStatus = reply->exit_code; pbi.PebBaseAddress = wine_server_get_ptr( reply->peb ); pbi.AffinityMask = reply->affinity & affinity_mask; - pbi.BasePriority = reply->priority; + pbi.BasePriority = reply->base_priority; pbi.UniqueProcessId = reply->pid; pbi.InheritedFromUniqueProcessId = reply->ppid; if (is_old_wow64()) @@ -1653,7 +1653,7 @@ NTSTATUS WINAPI NtWow64QueryInformationProcess64( HANDLE handle, PROCESSINFOCLAS pbi.ExitStatus = reply->exit_code; pbi.PebBaseAddress = (ULONG)wine_server_get_ptr( reply->peb ); pbi.AffinityMask = reply->affinity & affinity_mask; - pbi.BasePriority = reply->priority; + pbi.BasePriority = reply->base_priority; pbi.UniqueProcessId = reply->pid; pbi.InheritedFromUniqueProcessId = reply->ppid; } diff --git a/server/process.c b/server/process.c index 8998be1bbe8..a905d3421df 100644 --- a/server/process.c +++ b/server/process.c @@ -1512,6 +1512,7 @@ DECL_HANDLER(get_process_info) reply->ppid = process->parent_id; reply->exit_code = process->exit_code; reply->priority = process->priority; + reply->base_priority = get_process_base_priority( process->priority ); reply->affinity = process->affinity; reply->peb = process->peb; reply->start_time = process->start_time; diff --git a/server/protocol.def b/server/protocol.def index e672cd0d759..0e21cf36306 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1156,6 +1156,7 @@ struct obj_locator unsigned int session_id; /* process session id */ int exit_code; /* process exit code */ int priority; /* priority class */ + unsigned short base_priority; /* process base priority */ unsigned short machine; /* process architecture */ VARARG(image,pe_image_info); /* image info for main exe */ @END
From: Marc-Aurel Zent mzent@codeweavers.com
--- dlls/ntdll/tests/info.c | 175 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+)
diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index ff7fbad49d4..86e11c11784 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -32,6 +32,7 @@ #include "wine/test.h"
static NTSTATUS (WINAPI * pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); +static NTSTATUS (WINAPI * pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); static NTSTATUS (WINAPI * pNtSetSystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG); static NTSTATUS (WINAPI * pRtlGetNativeSystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); static NTSTATUS (WINAPI * pNtQuerySystemInformationEx)(SYSTEM_INFORMATION_CLASS, void*, ULONG, void*, ULONG, ULONG*); @@ -90,6 +91,7 @@ static void InitFunctionPtrs(void) HMODULE hkernel32 = GetModuleHandleA("kernel32");
NTDLL_GET_PROC(NtQuerySystemInformation); + NTDLL_GET_PROC(NtQueryInformationProcess); NTDLL_GET_PROC(NtQuerySystemInformationEx); NTDLL_GET_PROC(NtSetSystemInformation); NTDLL_GET_PROC(RtlGetNativeSystemInformation); @@ -3263,6 +3265,178 @@ static void test_affinity(void) "Unexpected thread affinity\n" ); }
+static void test_priority(void) +{ + NTSTATUS status; + DWORD proc_priority; + int thread_base_priority, expected_nt_priority; + ULONG nt_thread_priority; + THREAD_BASIC_INFORMATION tbi; + PROCESS_PRIORITY_CLASS ppc; + HANDLE process_handle = GetCurrentProcess(); + HANDLE thread_handle = GetCurrentThread(); + BOOL ret; + + typedef struct _PROCESS_BASIC_INFORMATION_PRIVATE { + NTSTATUS ExitStatus; + PEB *PebBaseAddress; + ULONG_PTR AffinityMask; + LONG BasePriority; + ULONG_PTR UniqueProcessId; + ULONG_PTR InheritedFromUniqueProcessId; + } PROCESS_BASIC_INFORMATION_PRIVATE; + + PROCESS_BASIC_INFORMATION_PRIVATE pbi; + + /* Change process priority class to HIGH_PRIORITY_CLASS and test */ + ret = SetPriorityClass(process_handle, HIGH_PRIORITY_CLASS); + ok(ret, "SetPriorityClass to HIGH_PRIORITY_CLASS failed: %lu\n", GetLastError()); + proc_priority = GetPriorityClass(process_handle); + ok(proc_priority == HIGH_PRIORITY_CLASS, + "Expected HIGH_PRIORITY_CLASS, got %lu\n", proc_priority); + status = pNtQueryInformationProcess( process_handle, ProcessPriorityClass, &ppc, + sizeof(ppc), NULL ); + ok(status == STATUS_SUCCESS, + "NtQueryInformationProcess failed: %08lx\n", status); + ok(ppc.PriorityClass == PROCESS_PRIORITY_CLASS_HIGH, + "Expected PROCESS_PRIORITY_CLASS_HIGH, got %lu\n", proc_priority); + + /* Restore process priority back to normal */ + ret = SetPriorityClass(process_handle, NORMAL_PRIORITY_CLASS); + ok(ret, "Restore SetPriorityClass failed: %lu\n", GetLastError()); + proc_priority = GetPriorityClass(process_handle); + ok(proc_priority == NORMAL_PRIORITY_CLASS, + "Expected NORMAL_PRIORITY_CLASS after restore, got %lu\n", proc_priority); + status = pNtQueryInformationProcess( process_handle, ProcessPriorityClass, &ppc, + sizeof(ppc), NULL ); + ok(status == STATUS_SUCCESS, + "NtQueryInformationProcess failed: %08lx\n", status); + ok(ppc.PriorityClass == PROCESS_PRIORITY_CLASS_NORMAL, + "Expected PROCESS_PRIORITY_CLASS_NORMAL, got %lu\n", proc_priority); + + /* Before checking any thread priorities, disable priority boosting + * in order to make the tests reliable. */ + SetThreadPriorityBoost(thread_handle, TRUE); + + /* Test thread priority: + * Compare the value from GetThreadPriority (thread priority level) + * with the BasePriority from NtQueryInformationThread. */ + thread_base_priority = GetThreadPriority(thread_handle); + ok(thread_base_priority != THREAD_PRIORITY_ERROR_RETURN, + "GetThreadPriority returned error\n"); + + status = pNtQueryInformationThread(thread_handle, ThreadBasicInformation, + &tbi, sizeof(tbi), NULL); + ok(status == STATUS_SUCCESS, + "NtQueryInformationThread failed: %08lx\n", status); + ok(thread_base_priority == (int)tbi.BasePriority, + "Thread priority mismatch: Win32 API returned %d, NT BasePriority is %d\n", + thread_base_priority, (int)tbi.BasePriority); + + /* Change the thread priority to THREAD_PRIORITY_HIGHEST and compare with + * underlying NT priority, which should be now the NORMAL_PRIORITY_CLASS + * base value (8) + THREAD_PRIORITY_HIGHEST (+2) = 10 (without boost). */ + ret = SetThreadPriority(thread_handle, THREAD_PRIORITY_HIGHEST); + ok(ret, "SetThreadPriority(THREAD_PRIORITY_HIGHEST) failed: %lu\n", GetLastError()); + thread_base_priority = GetThreadPriority(thread_handle); + ok(thread_base_priority == THREAD_PRIORITY_HIGHEST, + "Expected THREAD_PRIORITY_HIGHEST (%d), got %d\n", + THREAD_PRIORITY_HIGHEST, thread_base_priority); + + status = pNtQueryInformationThread(thread_handle, ThreadBasicInformation, + &tbi, sizeof(tbi), NULL); + ok(status == STATUS_SUCCESS, + "NtQueryInformationThread failed after setting priority: %08lx\n", status); + ok(thread_base_priority == (int)tbi.BasePriority, + "After setting, API priority (%d) does not match NT BasePriority (%d)\n", + thread_base_priority, (int)tbi.BasePriority); + status = pNtQueryInformationProcess(process_handle, (PROCESSINFOCLASS)ProcessBasicInformation, &pbi, + sizeof(pbi), NULL); + expected_nt_priority = pbi.BasePriority + THREAD_PRIORITY_HIGHEST; + ok(expected_nt_priority == (int)tbi.Priority, + "After setting, effective NT priority (%d) does not match expected priority %d.\n", + (int)tbi.Priority, expected_nt_priority); + + /* Test setting the thread priority to THREAD_PRIORITY_LOWEST now, but using + * pNtSetInformationThread, also testing NT priority directly afterwards. */ + nt_thread_priority = THREAD_PRIORITY_LOWEST; + status = pNtSetInformationThread(thread_handle, ThreadBasePriority, + &nt_thread_priority, sizeof(ULONG)); + ok(status == STATUS_SUCCESS, + "NtSetInformationThread(ThreadBasePriority) failed: %08lx\n", status); + /* Effective thread priority should be now NORMAL_PRIORITY_CLASS base + * value (8) + THREAD_PRIORITY_LOWEST (-2) = 6. */ + status = pNtQueryInformationThread(thread_handle, ThreadBasicInformation, + &tbi, sizeof(tbi), NULL); + ok(status == STATUS_SUCCESS, + "NtQueryInformationThread failed after setting priority: %08lx\n", status); + ok(nt_thread_priority == tbi.BasePriority, + "After setting, BasePriority (%ld) does not match set BasePriority (%d)\n", + nt_thread_priority, (int)tbi.BasePriority); + expected_nt_priority = pbi.BasePriority + THREAD_PRIORITY_LOWEST; + ok(expected_nt_priority == (int)tbi.Priority, + "After setting, effective NT priority (%d) does not match expected priority %d.\n", + (int)tbi.Priority, expected_nt_priority); + /* Now set NT thread priority directly to 12, a value normally impossible to + * reach in NORMAL_PRIORITY_CLASS without boost. */ + nt_thread_priority = 12; + status = pNtSetInformationThread(thread_handle, ThreadPriority, + &nt_thread_priority, sizeof(ULONG)); + ok(status == STATUS_SUCCESS, + "NtSetInformationThread(ThreadPriority) failed: %08lx\n", status); + /* Effective thread priority should be now 12, BasePriority should be + * unchanged. */ + status = pNtQueryInformationThread(thread_handle, ThreadBasicInformation, + &tbi, sizeof(tbi), NULL); + ok(status == STATUS_SUCCESS, + "NtQueryInformationThread failed after setting priority: %08lx\n", status); + ok(THREAD_PRIORITY_LOWEST == tbi.BasePriority, + "After setting, BasePriority (%d) does not match set BasePriority THREAD_PRIORITY_LOWEST.\n", + (int)tbi.BasePriority); + ok(nt_thread_priority == (int)tbi.Priority, + "After setting, effective NT priority (%d) does not match expected priority %lu.\n", + (int)tbi.Priority, nt_thread_priority); + /* Changing process priority recalculates all priorities again and + * overwrites our custom priority of 12. */ + ret = SetPriorityClass(process_handle, BELOW_NORMAL_PRIORITY_CLASS); + ok(ret, "SetPriorityClass to BELOW_NORMAL_PRIORITY_CLASS failed: %lu\n", GetLastError()); + /* Effective thread priority should be now BELOW_NORMAL_PRIORITY_CLASS base + * value (6) + THREAD_PRIORITY_LOWEST (-2) = 4. */ + status = pNtQueryInformationThread(thread_handle, ThreadBasicInformation, + &tbi, sizeof(tbi), NULL); + ok(status == STATUS_SUCCESS, + "NtQueryInformationThread failed after setting priority: %08lx\n", status); + status = pNtQueryInformationProcess(process_handle, (PROCESSINFOCLASS)ProcessBasicInformation, &pbi, + sizeof(pbi), NULL); + expected_nt_priority = pbi.BasePriority + THREAD_PRIORITY_LOWEST; + ok(expected_nt_priority == (int)tbi.Priority, + "After setting, effective NT priority (%d) does not match expected priority %d.\n", + (int)tbi.Priority, expected_nt_priority); + /* Setting an out of range priority above HIGH_PRIORITY (31) or LOW_PRIORITY (0) + * and lower fails. */ + nt_thread_priority = 42; + status = pNtSetInformationThread(thread_handle, ThreadPriority, + &nt_thread_priority, sizeof(ULONG)); + ok(status == STATUS_INVALID_PARAMETER, + "NtSetInformationThread(ThreadPriority) unexpectedly returned %08lx instead of STATUS_INVALID_PARAMETER.\n", status); + nt_thread_priority = 0; /* 0 also fails in addition to negative values. */ + status = pNtSetInformationThread(thread_handle, ThreadPriority, + &nt_thread_priority, sizeof(ULONG)); + ok(status == STATUS_INVALID_PARAMETER, + "NtSetInformationThread(ThreadPriority) unexpectedly returned %08lx instead of STATUS_INVALID_PARAMETER.\n", status); + /* Moving a thread into the realtime band is normally not possible in a non-realtime process. */ + nt_thread_priority = 24; + status = pNtSetInformationThread(thread_handle, ThreadPriority, + &nt_thread_priority, sizeof(ULONG)); + ok(status == STATUS_PRIVILEGE_NOT_HELD, + "NtSetInformationThread(ThreadPriority) unexpectedly returned %08lx instead of STATUS_PRIVILEGE_NOT_HELD.\n", status); + + /* Restore thread priority and boosting behaviour back to normal */ + SetThreadPriorityBoost(thread_handle, FALSE); + ret = SetThreadPriority(thread_handle, THREAD_PRIORITY_NORMAL); + ok(ret, "Restore SetThreadPriority(THREAD_PRIORITY_NORMAL) failed: %lu\n", GetLastError()); +} + static DWORD WINAPI hide_from_debugger_thread(void *arg) { HANDLE stop_event = arg; @@ -4176,6 +4350,7 @@ START_TEST(info) test_ThreadIsTerminated();
test_affinity(); + test_priority(); test_debug_object();
/* belongs to its own file */
I added a few extra edge cases to test for and this implementation now behaves exactly like windows AFAICT, at least when ignoring thread boosting for now. Of note is that the current wine HEAD fails some of these tests in an interesting way like ``` After setting, effective NT priority (2) does not match expected priority 4. ``` or ``` After setting, effective NT priority (-2) does not match expected priority 6. ``` where the left hand side of the comparison is wrong because of the mix up between thread base priority and priority and the right hand side is also wrong because process base priority and priority are also mixed up (luckily still not equal though, so the test doesn't succeed by accident).
On Fri Mar 7 23:21:57 2025 +0000, Marc-Aurel Zent wrote:
Yeah I originally also read that piece of documentation first (and the current nomenclature used in Wine makes sense with it), but the more I looked into how things actually work and not only ntdll constants, but also all structures fields and functions (some even documented like [KeSetPriorityThread](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-ke...) or [KeSetBasePriorityThread](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntdd...)) consistently use the terms in exactly the opposite way. Naming them like that makes everything line up perfectly AFAICT, so I think the rename is worth it here. I also wouldn't mind renaming the old thread priority to `priority_level` like you suggested per se, which would be the same amount of work... I believe the way it is in this MR now though is nicer and more consistent.
After digging a bit deeper, the way it actually works is ``` Thread Priority = Process Base Priority + Thread Base Priority ``` using NT terminology, so I think it makes sense to keep mirroring that server-side.
Also process BasePriority is something that can be set AFAICT as well, making the rabbit hole go even deeper here (but this MR is already too large, so I will postpone that for the next part).
If I had known when creating part one of this series what I know now, I would have probably also named all priorities, be they thread or process to `priority` or `base_priority` depending on what they actually represent.