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 (or even constant names being wrong), 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 boosting and `KeSetPriorityThread` and friends, which are either stubs or not correctly working as well and have been moved to the next part.
I wrote a pretty extensive test for all this too, since pretty much none of how this works is documented (or even misrepresented quite often). For testing, thread priority boosting has been disabled in order to make the behavior on Windows not flaky and dynamic. In fact, on Windows, there is some thread priority boost decay going on after the message has been processed and other mechanisms.
The `last` field of `get_thread_info` reply was moved to a flag in order to make space for the thread base priority, since it is already at the limit of 64 bytes.
-- v9: 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. server: Infer process priority class in set_thread_priority.
From: Marc-Aurel Zent mzent@codeweavers.com
--- include/ddk/wdm.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h index 2ea6a81d432..88a6fcb943c 100644 --- a/include/ddk/wdm.h +++ b/include/ddk/wdm.h @@ -40,7 +40,12 @@ #define ADDRESS_AND_SIZE_TO_SPAN_PAGES(va, length) \ ((BYTE_OFFSET(va) + ((SIZE_T)(length)) + (PAGE_SIZE - 1)) >> PAGE_SHIFT)
-typedef LONG KPRIORITY; +#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 LONG KPRIORITY; /* Thread priority */
typedef ULONG_PTR KSPIN_LOCK, *PKSPIN_LOCK;
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 c877bb6f7ae..6af58cc2cc6 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 9f6f5f57a52..b0573b609fd 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) @@ -1617,7 +1618,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 );
@@ -1648,7 +1649,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 | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-)
diff --git a/server/thread.c b/server/thread.c index b0573b609fd..11e469a2df4 100644 --- a/server/thread.c +++ b/server/thread.c @@ -51,6 +51,7 @@ #define WIN32_NO_STATUS #include "windef.h" #include "winternl.h" +#include "ddk/wdm.h"
#include "file.h" #include "handle.h" @@ -247,14 +248,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 +276,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 +295,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 +326,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 +340,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 +355,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 +376,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 +771,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_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; + if (priority == THREAD_PRIORITY_IDLE) + return (priority_class == PROCESS_PRIOCLASS_REALTIME ? LOW_REALTIME_PRIORITY : LOW_PRIORITY + 1); + if (priority == THREAD_PRIORITY_TIME_CRITICAL) + return (priority_class == PROCESS_PRIOCLASS_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 +808,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 d3ddcf13963..c2e1ed14f24 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -2377,9 +2377,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 6af58cc2cc6..1980e2ca777 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 871c3909e8c..e4790480113 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1247,18 +1247,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 11e469a2df4..d5fa28124b8 100644 --- a/server/thread.c +++ b/server/thread.c @@ -299,7 +299,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 */ @@ -405,7 +405,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; @@ -771,24 +771,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_PRIOCLASS_REALTIME ? LOW_REALTIME_PRIORITY : LOW_PRIORITY + 1); - if (priority == THREAD_PRIORITY_TIME_CRITICAL) + if (base_priority == THREAD_PRIORITY_TIME_CRITICAL) return (priority_class == PROCESS_PRIOCLASS_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; @@ -799,16 +799,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; } @@ -817,9 +817,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) @@ -1622,7 +1622,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 );
@@ -1653,7 +1653,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); @@ -1702,7 +1702,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 d5fa28124b8..e65bcb3485e 100644 --- a/server/thread.c +++ b/server/thread.c @@ -405,6 +405,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; @@ -771,6 +772,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_PRIOCLASS_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... */ @@ -805,12 +825,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 e4790480113..60744d1c6df 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1247,18 +1247,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 e65bcb3485e..74e1d2b020c 100644 --- a/server/thread.c +++ b/server/thread.c @@ -832,6 +832,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 c2e1ed14f24..ccb70d6f10b 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -2371,6 +2371,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; @@ -2548,7 +2563,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 ccb70d6f10b..c5ff03b648a 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 60744d1c6df..3c7bee633e2 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1222,7 +1222,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 */ @@ -1230,6 +1229,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 74e1d2b020c..ea5f7483fc1 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1724,7 +1724,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; @@ -1732,6 +1731,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 c5ff03b648a..2edeb8a571e 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 1980e2ca777..12218e82e3d 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 3c7bee633e2..55cffbacca1 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1221,7 +1221,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 ea5f7483fc1..8e28aefe47a 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1722,7 +1722,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 df3447a9014..a47e2dcc01a 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_PRIOCLASS_IDLE: return IDLE_PRIORITY_CLASS; case PROCESS_PRIOCLASS_BELOW_NORMAL: return BELOW_NORMAL_PRIORITY_CLASS;
From: Marc-Aurel Zent mzent@codeweavers.com
--- server/process.c | 22 ++++++++++++++++++++++ server/process.h | 1 + server/thread.c | 7 +------ 3 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/server/process.c b/server/process.c index 12218e82e3d..fc21ac70012 100644 --- a/server/process.c +++ b/server/process.c @@ -1633,6 +1633,28 @@ 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_PRIOCLASS_IDLE: + return 4; + case PROCESS_PRIOCLASS_BELOW_NORMAL: + return 6; + case PROCESS_PRIOCLASS_NORMAL: + return 8; + case PROCESS_PRIOCLASS_ABOVE_NORMAL: + return 10; + case PROCESS_PRIOCLASS_HIGH: + return 13; + case PROCESS_PRIOCLASS_REALTIME: + return 24; + 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 8e28aefe47a..88ff39676d9 100644 --- a/server/thread.c +++ b/server/thread.c @@ -793,16 +793,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_PRIOCLASS_REALTIME ? LOW_REALTIME_PRIORITY : LOW_PRIORITY + 1); if (base_priority == THREAD_PRIORITY_TIME_CRITICAL) return (priority_class == PROCESS_PRIOCLASS_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 fc21ac70012..ab81b389c8c 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 55cffbacca1..91b91958d6f 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1158,6 +1158,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 d24639b6b68..d14ecb81deb 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_PRIOCLASS_HIGH, + "Expected PROCESS_PRIOCLASS_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_PRIOCLASS_NORMAL, + "Expected PROCESS_PRIOCLASS_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; @@ -4197,6 +4371,7 @@ START_TEST(info) test_ThreadIsTerminated();
test_affinity(); + test_priority(); test_debug_object();
/* belongs to its own file */
On Wed Mar 19 12:40:57 2025 +0000, Marc-Aurel Zent wrote:
changed this line in [version 9 of the diff](/wine/wine/-/merge_requests/7516/diffs?diff_id=165186&start_sha=592de7748235f9e03d54c97ef9d6abfc716529e2#4ba90b2e069cab26f43d90c35b1ce15880d09d93_2660_2654)
Alright, I dropped it in the latest version.
Superseded by both https://gitlab.winehq.org/wine/wine/-/merge_requests/7631 and https://gitlab.winehq.org/wine/wine/-/merge_requests/7632
This merge request was closed by Marc-Aurel Zent.