From: Marc-Aurel Zent mzent@codeweavers.com
--- dlls/ntdll/tests/info.c | 8 ++++---- dlls/ntdll/unix/thread.c | 16 +++++++++++++++- server/protocol.def | 14 ++++++++------ server/thread.c | 28 ++++++++++++++++++++++------ server/thread.h | 2 ++ 5 files changed, 51 insertions(+), 17 deletions(-)
diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index b92d421891f..44c5bd6cd1e 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -3355,7 +3355,7 @@ static void test_priority(void) * reach in NORMAL_PRIORITY_CLASS without boost. */ nt_thread_priority = 12; status = pNtSetInformationThread( GetCurrentThread(), ThreadPriority, &nt_thread_priority, sizeof(ULONG) ); - todo_wine ok( status == STATUS_SUCCESS, "NtSetInformationThread(ThreadPriority) failed: %08lx\n", status ); + ok( status == STATUS_SUCCESS, "NtSetInformationThread(ThreadPriority) failed: %08lx\n", status ); /* Effective thread priority should be now 12, BasePriority should be * unchanged. */ status = pNtQueryInformationThread( GetCurrentThread(), ThreadBasicInformation, &tbi, sizeof(tbi), NULL ); @@ -3380,14 +3380,14 @@ static void test_priority(void) * and lower fails. */ nt_thread_priority = 42; status = pNtSetInformationThread( GetCurrentThread(), ThreadPriority, &nt_thread_priority, sizeof(ULONG) ); - todo_wine ok( status == STATUS_INVALID_PARAMETER, "got %08lx, expected STATUS_INVALID_PARAMETER.\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "got %08lx, expected STATUS_INVALID_PARAMETER.\n", status ); nt_thread_priority = 0; /* 0 also fails in addition to negative values. */ status = pNtSetInformationThread( GetCurrentThread(), ThreadPriority, &nt_thread_priority, sizeof(ULONG) ); - todo_wine ok( status == STATUS_INVALID_PARAMETER, "got %08lx, expected STATUS_INVALID_PARAMETER.\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "got %08lx, expected 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( GetCurrentThread(), ThreadPriority, &nt_thread_priority, sizeof(ULONG) ); - todo_wine ok( status == STATUS_PRIVILEGE_NOT_HELD, "got %08lx, expected STATUS_PRIVILEGE_NOT_HELD.\n", status ); + ok( status == STATUS_PRIVILEGE_NOT_HELD, "got %08lx, expected STATUS_PRIVILEGE_NOT_HELD.\n", status );
/* Restore thread priority and boosting behaviour back to normal */ SetThreadPriorityBoost( GetCurrentThread(), FALSE ); diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index b31f8969c21..bda23fcd9a1 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 *priority = data; + if (length != sizeof(DWORD)) return STATUS_INVALID_PARAMETER; + SERVER_START_REQ( set_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->priority = *priority; + req->mask = SET_THREAD_INFO_PRIORITY; + status = wine_server_call( req ); + } + SERVER_END_REQ; + return status; + } + case ThreadBasePriority: { const DWORD *base_priority = 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: diff --git a/server/protocol.def b/server/protocol.def index 36056c00e24..fddc5337757 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; /* current thread priority */ int base_priority;/* base priority level (relative to process 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 8797af32648..30a295a40cc 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,21 @@ 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 priority_from_class_and_level( int priority_class, int priority_level ) { /* offsets taken from https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priori... */ @@ -812,18 +828,18 @@ 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, priority_from_class_and_level( priority_class, base_priority ) ); - - return STATUS_SUCCESS; + return set_thread_priority( thread, priority_from_class_and_level( priority_class, base_priority ) ); }
/* set all information about a thread */ static void set_thread_info( struct thread *thread, const struct set_thread_info_request *req ) { + if (req->mask & SET_THREAD_INFO_PRIORITY) + { + 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 ); diff --git a/server/thread.h b/server/thread.h index 33d150df9cb..7fdae3a629f 100644 --- a/server/thread.h +++ b/server/thread.h @@ -81,6 +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; /* current thread priority */ int base_priority; /* base priority level (relative to process base priority class) */ int suspend; /* suspend count */ int dbg_hidden; /* hidden from debugger */ @@ -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 );