From e84ec36a620a4922ddcb9cdce9ddabc2573ee1da Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 18 Jun 2020 12:37:50 +0200 Subject: [PATCH] ntdll: Move the thread get/set information functions to the Unix library. Signed-off-by: Alexandre Julliard --- dlls/ntdll/loader.c | 5 +- dlls/ntdll/ntdll_misc.h | 1 - dlls/ntdll/process.c | 7 - dlls/ntdll/thread.c | 436 +------------------------------ dlls/ntdll/unix/loader.c | 4 +- dlls/ntdll/unix/signal_arm.c | 2 +- dlls/ntdll/unix/signal_arm64.c | 2 +- dlls/ntdll/unix/signal_i386.c | 2 +- dlls/ntdll/unix/signal_x86_64.c | 2 +- dlls/ntdll/unix/thread.c | 440 +++++++++++++++++++++++++++++++- dlls/ntdll/unix/unix_private.h | 5 +- dlls/ntdll/unix/virtual.c | 73 +++++- dlls/ntdll/unixlib.h | 8 +- 13 files changed, 514 insertions(+), 473 deletions(-) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index bdd343324da..87a65e4fc11 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -3629,8 +3629,6 @@ void WINAPI LdrShutdownThread(void) RtlAcquirePebLock(); RemoveEntryList( &NtCurrentTeb()->TlsLinks ); - RtlReleasePebLock(); - if ((pointers = NtCurrentTeb()->ThreadLocalStoragePointer)) { for (i = 0; i < tls_module_count; i++) RtlFreeHeap( GetProcessHeap(), 0, pointers[i] ); @@ -3638,6 +3636,9 @@ void WINAPI LdrShutdownThread(void) } RtlFreeHeap( GetProcessHeap(), 0, NtCurrentTeb()->FlsSlots ); RtlFreeHeap( GetProcessHeap(), 0, NtCurrentTeb()->TlsExpansionSlots ); + NtCurrentTeb()->TlsExpansionSlots = NULL; + RtlReleasePebLock(); + RtlLeaveCriticalSection( &loader_section ); } diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 0c2941f0856..2f7c0b0f1e8 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -55,7 +55,6 @@ struct drive_info }; extern NTSTATUS close_handle( HANDLE ) DECLSPEC_HIDDEN; -extern ULONG_PTR get_system_affinity_mask(void) DECLSPEC_HIDDEN; /* exceptions */ extern LONG call_vectored_handlers( EXCEPTION_RECORD *rec, CONTEXT *context ) DECLSPEC_HIDDEN; diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c index c1790481ef2..b1cc307d2ae 100644 --- a/dlls/ntdll/process.c +++ b/dlls/ntdll/process.c @@ -85,13 +85,6 @@ HANDLE CDECL __wine_make_process_system(void) return ret; } -ULONG_PTR get_system_affinity_mask(void) -{ - ULONG num_cpus = NtCurrentTeb()->Peb->NumberOfProcessors; - if (num_cpus >= sizeof(ULONG_PTR) * 8) return ~(ULONG_PTR)0; - return ((ULONG_PTR)1 << num_cpus) - 1; -} - /****************************************************************************** * NtQueryInformationProcess [NTDLL.@] * ZwQueryInformationProcess [NTDLL.@] diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c index 5a6809638ae..70546849d9a 100644 --- a/dlls/ntdll/thread.c +++ b/dlls/ntdll/thread.c @@ -45,7 +45,6 @@ #include "ddk/wdm.h" #include "wine/exception.h" -WINE_DEFAULT_DEBUG_CHANNEL(thread); WINE_DECLARE_DEBUG_CHANNEL(relay); struct _KUSER_SHARED_DATA *user_shared_data = (void *)0x7ffe0000; @@ -576,239 +575,7 @@ NTSTATUS WINAPI NtSetLdtEntries( ULONG sel1, LDT_ENTRY entry1, ULONG sel2, LDT_E NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class, void *data, ULONG length, ULONG *ret_len ) { - NTSTATUS status; - - switch(class) - { - case ThreadBasicInformation: - { - THREAD_BASIC_INFORMATION info; - const ULONG_PTR affinity_mask = get_system_affinity_mask(); - - SERVER_START_REQ( get_thread_info ) - { - req->handle = wine_server_obj_handle( handle ); - req->tid_in = 0; - if (!(status = wine_server_call( req ))) - { - info.ExitStatus = reply->exit_code; - info.TebBaseAddress = wine_server_get_ptr( reply->teb ); - info.ClientId.UniqueProcess = ULongToHandle(reply->pid); - info.ClientId.UniqueThread = ULongToHandle(reply->tid); - info.AffinityMask = reply->affinity & affinity_mask; - info.Priority = reply->priority; - info.BasePriority = reply->priority; /* FIXME */ - } - } - SERVER_END_REQ; - if (status == STATUS_SUCCESS) - { - if (data) memcpy( data, &info, min( length, sizeof(info) )); - if (ret_len) *ret_len = min( length, sizeof(info) ); - } - } - return status; - case ThreadAffinityMask: - { - const ULONG_PTR affinity_mask = get_system_affinity_mask(); - ULONG_PTR affinity = 0; - - SERVER_START_REQ( get_thread_info ) - { - req->handle = wine_server_obj_handle( handle ); - req->tid_in = 0; - if (!(status = wine_server_call( req ))) - affinity = reply->affinity & affinity_mask; - } - SERVER_END_REQ; - if (status == STATUS_SUCCESS) - { - if (data) memcpy( data, &affinity, min( length, sizeof(affinity) )); - if (ret_len) *ret_len = min( length, sizeof(affinity) ); - } - } - return status; - case ThreadTimes: - { - KERNEL_USER_TIMES kusrt; - - SERVER_START_REQ( get_thread_times ) - { - req->handle = wine_server_obj_handle( handle ); - status = wine_server_call( req ); - if (status == STATUS_SUCCESS) - { - kusrt.CreateTime.QuadPart = reply->creation_time; - kusrt.ExitTime.QuadPart = reply->exit_time; - } - } - SERVER_END_REQ; - if (status == STATUS_SUCCESS) - { - /* We call times(2) for kernel time or user time */ - /* We can only (portably) do this for the current thread */ - if (handle == GetCurrentThread()) - { - struct tms time_buf; - long clocks_per_sec = sysconf(_SC_CLK_TCK); - - times(&time_buf); - kusrt.KernelTime.QuadPart = (ULONGLONG)time_buf.tms_stime * 10000000 / clocks_per_sec; - kusrt.UserTime.QuadPart = (ULONGLONG)time_buf.tms_utime * 10000000 / clocks_per_sec; - } - else - { - static BOOL reported = FALSE; - - kusrt.KernelTime.QuadPart = 0; - kusrt.UserTime.QuadPart = 0; - if (reported) - TRACE("Cannot get kerneltime or usertime of other threads\n"); - else - { - FIXME("Cannot get kerneltime or usertime of other threads\n"); - reported = TRUE; - } - } - if (data) memcpy( data, &kusrt, min( length, sizeof(kusrt) )); - if (ret_len) *ret_len = min( length, sizeof(kusrt) ); - } - } - return status; - - case ThreadDescriptorTableEntry: - return unix_funcs->get_thread_ldt_entry( handle, data, length, ret_len ); - - case ThreadAmILastThread: - { - SERVER_START_REQ(get_thread_info) - { - req->handle = wine_server_obj_handle( handle ); - req->tid_in = 0; - status = wine_server_call( req ); - if (status == STATUS_SUCCESS) - { - BOOLEAN last = reply->last; - if (data) memcpy( data, &last, min( length, sizeof(last) )); - if (ret_len) *ret_len = min( length, sizeof(last) ); - } - } - SERVER_END_REQ; - return status; - } - case ThreadQuerySetWin32StartAddress: - { - SERVER_START_REQ( get_thread_info ) - { - req->handle = wine_server_obj_handle( handle ); - req->tid_in = 0; - status = wine_server_call( req ); - if (status == STATUS_SUCCESS) - { - PRTL_THREAD_START_ROUTINE entry = wine_server_get_ptr( reply->entry_point ); - if (data) memcpy( data, &entry, min( length, sizeof(entry) ) ); - if (ret_len) *ret_len = min( length, sizeof(entry) ); - } - } - SERVER_END_REQ; - return status; - } - case ThreadGroupInformation: - { - const ULONG_PTR affinity_mask = get_system_affinity_mask(); - GROUP_AFFINITY affinity; - - memset(&affinity, 0, sizeof(affinity)); - affinity.Group = 0; /* Wine only supports max 64 processors */ - - SERVER_START_REQ( get_thread_info ) - { - req->handle = wine_server_obj_handle( handle ); - req->tid_in = 0; - if (!(status = wine_server_call( req ))) - affinity.Mask = reply->affinity & affinity_mask; - } - SERVER_END_REQ; - if (status == STATUS_SUCCESS) - { - if (data) memcpy( data, &affinity, min( length, sizeof(affinity) )); - if (ret_len) *ret_len = min( length, sizeof(affinity) ); - } - } - return status; - case ThreadIsIoPending: - FIXME( "ThreadIsIoPending info class not supported yet\n" ); - if (length != sizeof(BOOL)) return STATUS_INFO_LENGTH_MISMATCH; - if (!data) return STATUS_ACCESS_DENIED; - - *(BOOL*)data = FALSE; - if (ret_len) *ret_len = sizeof(BOOL); - return STATUS_SUCCESS; - case ThreadSuspendCount: - { - ULONG count = 0; - - if (length != sizeof(ULONG)) return STATUS_INFO_LENGTH_MISMATCH; - if (!data) return STATUS_ACCESS_VIOLATION; - - SERVER_START_REQ( get_thread_info ) - { - req->handle = wine_server_obj_handle( handle ); - req->tid_in = 0; - if (!(status = wine_server_call( req ))) - count = reply->suspend_count; - } - SERVER_END_REQ; - - if (!status) - *(ULONG *)data = count; - - return status; - } - case ThreadDescription: - { - THREAD_DESCRIPTION_INFORMATION *info = data; - data_size_t len, desc_len = 0; - WCHAR *ptr; - - len = length >= sizeof(*info) ? length - sizeof(*info) : 0; - ptr = info ? (WCHAR *)(info + 1) : NULL; - - SERVER_START_REQ( get_thread_info ) - { - req->handle = wine_server_obj_handle( handle ); - if (ptr) wine_server_set_reply( req, ptr, len ); - status = wine_server_call( req ); - desc_len = reply->desc_len; - } - SERVER_END_REQ; - - if (!info) - status = STATUS_BUFFER_TOO_SMALL; - else if (status == STATUS_SUCCESS) - { - info->Description.Length = info->Description.MaximumLength = desc_len; - info->Description.Buffer = ptr; - } - - if (ret_len && (status == STATUS_SUCCESS || status == STATUS_BUFFER_TOO_SMALL)) - *ret_len = sizeof(*info) + desc_len; - } - return status; - case ThreadPriority: - case ThreadBasePriority: - case ThreadImpersonationToken: - case ThreadEnableAlignmentFaultFixup: - case ThreadEventPair_Reusable: - case ThreadZeroTlsCell: - case ThreadPerformanceCount: - case ThreadIdealProcessor: - case ThreadPriorityBoost: - case ThreadSetTlsArrayAddress: - default: - FIXME( "info class %d not supported yet\n", class ); - return STATUS_NOT_IMPLEMENTED; - } + return unix_funcs->NtQueryInformationThread( handle, class, data, length, ret_len ); } @@ -819,173 +586,7 @@ NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class, NTSTATUS WINAPI NtSetInformationThread( HANDLE handle, THREADINFOCLASS class, LPCVOID data, ULONG length ) { - NTSTATUS status; - switch(class) - { - case ThreadZeroTlsCell: - if (handle == GetCurrentThread()) - { - LIST_ENTRY *entry; - DWORD index; - - if (length != sizeof(DWORD)) return STATUS_INVALID_PARAMETER; - index = *(const DWORD *)data; - if (index < TLS_MINIMUM_AVAILABLE) - { - RtlAcquirePebLock(); - for (entry = tls_links.Flink; entry != &tls_links; entry = entry->Flink) - { - TEB *teb = CONTAINING_RECORD(entry, TEB, TlsLinks); - teb->TlsSlots[index] = 0; - } - RtlReleasePebLock(); - } - else - { - index -= TLS_MINIMUM_AVAILABLE; - if (index >= 8 * sizeof(NtCurrentTeb()->Peb->TlsExpansionBitmapBits)) - return STATUS_INVALID_PARAMETER; - RtlAcquirePebLock(); - for (entry = tls_links.Flink; entry != &tls_links; entry = entry->Flink) - { - TEB *teb = CONTAINING_RECORD(entry, TEB, TlsLinks); - if (teb->TlsExpansionSlots) teb->TlsExpansionSlots[index] = 0; - } - RtlReleasePebLock(); - } - return STATUS_SUCCESS; - } - FIXME( "ZeroTlsCell not supported on other threads\n" ); - return STATUS_NOT_IMPLEMENTED; - - case ThreadImpersonationToken: - { - const HANDLE *phToken = data; - if (length != sizeof(HANDLE)) return STATUS_INVALID_PARAMETER; - TRACE("Setting ThreadImpersonationToken handle to %p\n", *phToken ); - SERVER_START_REQ( set_thread_info ) - { - req->handle = wine_server_obj_handle( handle ); - req->token = wine_server_obj_handle( *phToken ); - req->mask = SET_THREAD_INFO_TOKEN; - status = wine_server_call( req ); - } - SERVER_END_REQ; - } - return status; - case ThreadBasePriority: - { - 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 ThreadAffinityMask: - { - const ULONG_PTR affinity_mask = get_system_affinity_mask(); - ULONG_PTR req_aff; - - if (length != sizeof(ULONG_PTR)) return STATUS_INVALID_PARAMETER; - req_aff = *(const ULONG_PTR *)data & affinity_mask; - if (!req_aff) return STATUS_INVALID_PARAMETER; - - SERVER_START_REQ( set_thread_info ) - { - req->handle = wine_server_obj_handle( handle ); - req->affinity = req_aff; - req->mask = SET_THREAD_INFO_AFFINITY; - status = wine_server_call( req ); - } - SERVER_END_REQ; - } - return status; - case ThreadHideFromDebugger: - /* pretend the call succeeded to satisfy some code protectors */ - return STATUS_SUCCESS; - case ThreadQuerySetWin32StartAddress: - { - const PRTL_THREAD_START_ROUTINE *entry = data; - if (length != sizeof(PRTL_THREAD_START_ROUTINE)) return STATUS_INVALID_PARAMETER; - SERVER_START_REQ( set_thread_info ) - { - req->handle = wine_server_obj_handle( handle ); - req->mask = SET_THREAD_INFO_ENTRYPOINT; - req->entry_point = wine_server_client_ptr( *entry ); - status = wine_server_call( req ); - } - SERVER_END_REQ; - } - return status; - case ThreadGroupInformation: - { - const ULONG_PTR affinity_mask = get_system_affinity_mask(); - const GROUP_AFFINITY *req_aff; - - if (length != sizeof(*req_aff)) return STATUS_INVALID_PARAMETER; - if (!data) return STATUS_ACCESS_VIOLATION; - req_aff = data; - - /* On Windows the request fails if the reserved fields are set */ - if (req_aff->Reserved[0] || req_aff->Reserved[1] || req_aff->Reserved[2]) - return STATUS_INVALID_PARAMETER; - - /* Wine only supports max 64 processors */ - if (req_aff->Group) return STATUS_INVALID_PARAMETER; - if (req_aff->Mask & ~affinity_mask) return STATUS_INVALID_PARAMETER; - if (!req_aff->Mask) return STATUS_INVALID_PARAMETER; - SERVER_START_REQ( set_thread_info ) - { - req->handle = wine_server_obj_handle( handle ); - req->affinity = req_aff->Mask; - req->mask = SET_THREAD_INFO_AFFINITY; - status = wine_server_call( req ); - } - SERVER_END_REQ; - } - return status; - case ThreadDescription: - { - const THREAD_DESCRIPTION_INFORMATION *info = data; - - if (length != sizeof(*info)) return STATUS_INFO_LENGTH_MISMATCH; - if (!info) return STATUS_ACCESS_VIOLATION; - - if (info->Description.Length != info->Description.MaximumLength) return STATUS_INVALID_PARAMETER; - if (info->Description.Length && !info->Description.Buffer) return STATUS_ACCESS_VIOLATION; - - SERVER_START_REQ( set_thread_info ) - { - req->handle = wine_server_obj_handle( handle ); - req->mask = SET_THREAD_INFO_DESCRIPTION; - wine_server_add_data( req, info->Description.Buffer, info->Description.Length ); - status = wine_server_call( req ); - } - SERVER_END_REQ; - } - return status; - case ThreadBasicInformation: - case ThreadTimes: - case ThreadPriority: - case ThreadDescriptorTableEntry: - case ThreadEnableAlignmentFaultFixup: - case ThreadEventPair_Reusable: - case ThreadPerformanceCount: - case ThreadAmILastThread: - case ThreadIdealProcessor: - case ThreadPriorityBoost: - case ThreadSetTlsArrayAddress: - case ThreadIsIoPending: - default: - FIXME( "info class %d not supported yet\n", class ); - return STATUS_NOT_IMPLEMENTED; - } + return unix_funcs->NtSetInformationThread( handle, class, data, length ); } /****************************************************************************** @@ -996,36 +597,5 @@ NTSTATUS WINAPI NtSetInformationThread( HANDLE handle, THREADINFOCLASS class, */ ULONG WINAPI NtGetCurrentProcessorNumber(void) { - ULONG processor; - -#if defined(__linux__) && defined(__NR_getcpu) - int res = syscall(__NR_getcpu, &processor, NULL, NULL); - if (res != -1) return processor; -#endif - - if (NtCurrentTeb()->Peb->NumberOfProcessors > 1) - { - ULONG_PTR thread_mask, processor_mask; - NTSTATUS status; - - status = NtQueryInformationThread(GetCurrentThread(), ThreadAffinityMask, - &thread_mask, sizeof(thread_mask), NULL); - if (status == STATUS_SUCCESS) - { - for (processor = 0; processor < NtCurrentTeb()->Peb->NumberOfProcessors; processor++) - { - processor_mask = (1 << processor); - if (thread_mask & processor_mask) - { - if (thread_mask != processor_mask) - FIXME("need multicore support (%d processors)\n", - NtCurrentTeb()->Peb->NumberOfProcessors); - return processor; - } - } - } - } - - /* fallback to the first processor */ - return 0; + return unix_funcs->NtGetCurrentProcessorNumber(); } diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 8124ee8f511..091951163d7 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -868,6 +868,7 @@ static struct unix_funcs unix_funcs = NtFreeVirtualMemory, NtFsControlFile, NtGetContextThread, + NtGetCurrentProcessorNumber, NtGetWriteWatch, NtIsProcessInJob, NtLockVirtualMemory, @@ -892,6 +893,7 @@ static struct unix_funcs unix_funcs = NtQueryInformationFile, NtQueryInformationJobObject, NtQueryInformationProcess, + NtQueryInformationThread, NtQueryIoCompletion, NtQueryMutant, NtQueryPerformanceCounter, @@ -919,6 +921,7 @@ static struct unix_funcs unix_funcs = NtSetInformationFile, NtSetInformationJobObject, NtSetInformationProcess, + NtSetInformationThread, NtSetIoCompletion, NtSetLdtEntries, NtSetSystemTime, @@ -975,7 +978,6 @@ static struct unix_funcs unix_funcs = init_threading, exit_thread, exit_process, - get_thread_ldt_entry, exec_process, wine_server_call, server_send_fd, diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index faa2952a483..e65b8a93d18 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -781,7 +781,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) /********************************************************************** * get_thread_ldt_entry */ -NTSTATUS CDECL get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len ) +NTSTATUS get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len ) { return STATUS_NOT_IMPLEMENTED; } diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index e2c52a1a4f6..7f8fb708ccd 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -695,7 +695,7 @@ static void usr2_handler( int signal, siginfo_t *siginfo, void *sigcontext ) /********************************************************************** * get_thread_ldt_entry */ -NTSTATUS CDECL get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len ) +NTSTATUS get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len ) { return STATUS_NOT_IMPLEMENTED; } diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index adf622ca673..2f5d397ed55 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -2039,7 +2039,7 @@ static void ldt_set_fs( WORD sel, TEB *teb ) /********************************************************************** * get_thread_ldt_entry */ -NTSTATUS CDECL get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len ) +NTSTATUS get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len ) { THREAD_DESCRIPTOR_INFORMATION *info = data; NTSTATUS status = STATUS_SUCCESS; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 1bf8f5e8b8e..88ee18461a2 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1245,7 +1245,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *ucontext ) /********************************************************************** * get_thread_ldt_entry */ -NTSTATUS CDECL get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len ) +NTSTATUS get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len ) { return STATUS_NOT_IMPLEMENTED; } diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index c6ca9af56ac..35191d1d61e 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -89,7 +89,6 @@ TEB * CDECL init_threading( int *nb_threads_ptr, struct ldt_copy **ldt_copy, SIZ { TEB *teb; SIZE_T info_size; - struct ntdll_thread_data *thread_data; #ifdef __i386__ extern struct ldt_copy __wine_ldt_copy; *ldt_copy = &__wine_ldt_copy; @@ -97,11 +96,6 @@ TEB * CDECL init_threading( int *nb_threads_ptr, struct ldt_copy **ldt_copy, SIZ nb_threads = nb_threads_ptr; teb = virtual_alloc_first_teb(); - thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch; - thread_data->request_fd = -1; - thread_data->reply_fd = -1; - thread_data->wait_fd[0] = -1; - thread_data->wait_fd[1] = -1; signal_init_threading(); signal_alloc_thread( teb ); @@ -288,9 +282,6 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch; thread_data->request_fd = request_pipe[1]; - thread_data->reply_fd = -1; - thread_data->wait_fd[0] = -1; - thread_data->wait_fd[1] = -1; thread_data->start_stack = (char *)teb->Tib.StackBase; pthread_attr_init( &pthread_attr ); @@ -673,3 +664,434 @@ NTSTATUS get_thread_context( HANDLE handle, context_t *context, unsigned int fla } return ret; } + + +/****************************************************************************** + * NtQueryInformationThread (NTDLL.@) + */ +NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class, + void *data, ULONG length, ULONG *ret_len ) +{ + NTSTATUS status; + + switch (class) + { + case ThreadBasicInformation: + { + THREAD_BASIC_INFORMATION info; + const ULONG_PTR affinity_mask = get_system_affinity_mask(); + + SERVER_START_REQ( get_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->tid_in = 0; + if (!(status = wine_server_call( req ))) + { + info.ExitStatus = reply->exit_code; + info.TebBaseAddress = wine_server_get_ptr( reply->teb ); + info.ClientId.UniqueProcess = ULongToHandle(reply->pid); + info.ClientId.UniqueThread = ULongToHandle(reply->tid); + info.AffinityMask = reply->affinity & affinity_mask; + info.Priority = reply->priority; + info.BasePriority = reply->priority; /* FIXME */ + } + } + SERVER_END_REQ; + if (status == STATUS_SUCCESS) + { + if (data) memcpy( data, &info, min( length, sizeof(info) )); + if (ret_len) *ret_len = min( length, sizeof(info) ); + } + return status; + } + + case ThreadAffinityMask: + { + const ULONG_PTR affinity_mask = get_system_affinity_mask(); + ULONG_PTR affinity = 0; + + SERVER_START_REQ( get_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->tid_in = 0; + if (!(status = wine_server_call( req ))) affinity = reply->affinity & affinity_mask; + } + SERVER_END_REQ; + if (status == STATUS_SUCCESS) + { + if (data) memcpy( data, &affinity, min( length, sizeof(affinity) )); + if (ret_len) *ret_len = min( length, sizeof(affinity) ); + } + return status; + } + + case ThreadTimes: + { + KERNEL_USER_TIMES kusrt; + + SERVER_START_REQ( get_thread_times ) + { + req->handle = wine_server_obj_handle( handle ); + status = wine_server_call( req ); + if (status == STATUS_SUCCESS) + { + kusrt.CreateTime.QuadPart = reply->creation_time; + kusrt.ExitTime.QuadPart = reply->exit_time; + } + } + SERVER_END_REQ; + if (status == STATUS_SUCCESS) + { + /* We call times(2) for kernel time or user time */ + /* We can only (portably) do this for the current thread */ + if (handle == GetCurrentThread()) + { + struct tms time_buf; + long clocks_per_sec = sysconf(_SC_CLK_TCK); + + times(&time_buf); + kusrt.KernelTime.QuadPart = (ULONGLONG)time_buf.tms_stime * 10000000 / clocks_per_sec; + kusrt.UserTime.QuadPart = (ULONGLONG)time_buf.tms_utime * 10000000 / clocks_per_sec; + } + else + { + static BOOL reported = FALSE; + + kusrt.KernelTime.QuadPart = 0; + kusrt.UserTime.QuadPart = 0; + if (reported) + TRACE("Cannot get kerneltime or usertime of other threads\n"); + else + { + FIXME("Cannot get kerneltime or usertime of other threads\n"); + reported = TRUE; + } + } + if (data) memcpy( data, &kusrt, min( length, sizeof(kusrt) )); + if (ret_len) *ret_len = min( length, sizeof(kusrt) ); + } + return status; + } + + case ThreadDescriptorTableEntry: + return get_thread_ldt_entry( handle, data, length, ret_len ); + + case ThreadAmILastThread: + { + SERVER_START_REQ( get_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->tid_in = 0; + status = wine_server_call( req ); + if (status == STATUS_SUCCESS) + { + BOOLEAN last = reply->last; + if (data) memcpy( data, &last, min( length, sizeof(last) )); + if (ret_len) *ret_len = min( length, sizeof(last) ); + } + } + SERVER_END_REQ; + return status; + } + + case ThreadQuerySetWin32StartAddress: + { + SERVER_START_REQ( get_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->tid_in = 0; + status = wine_server_call( req ); + if (status == STATUS_SUCCESS) + { + PRTL_THREAD_START_ROUTINE entry = wine_server_get_ptr( reply->entry_point ); + if (data) memcpy( data, &entry, min( length, sizeof(entry) ) ); + if (ret_len) *ret_len = min( length, sizeof(entry) ); + } + } + SERVER_END_REQ; + return status; + } + + case ThreadGroupInformation: + { + const ULONG_PTR affinity_mask = get_system_affinity_mask(); + GROUP_AFFINITY affinity; + + memset( &affinity, 0, sizeof(affinity) ); + affinity.Group = 0; /* Wine only supports max 64 processors */ + + SERVER_START_REQ( get_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->tid_in = 0; + if (!(status = wine_server_call( req ))) affinity.Mask = reply->affinity & affinity_mask; + } + SERVER_END_REQ; + if (status == STATUS_SUCCESS) + { + if (data) memcpy( data, &affinity, min( length, sizeof(affinity) )); + if (ret_len) *ret_len = min( length, sizeof(affinity) ); + } + return status; + } + + case ThreadIsIoPending: + FIXME( "ThreadIsIoPending info class not supported yet\n" ); + if (length != sizeof(BOOL)) return STATUS_INFO_LENGTH_MISMATCH; + if (!data) return STATUS_ACCESS_DENIED; + *(BOOL*)data = FALSE; + if (ret_len) *ret_len = sizeof(BOOL); + return STATUS_SUCCESS; + + case ThreadSuspendCount: + if (length != sizeof(ULONG)) return STATUS_INFO_LENGTH_MISMATCH; + if (!data) return STATUS_ACCESS_VIOLATION; + + SERVER_START_REQ( get_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->tid_in = 0; + if (!(status = wine_server_call( req ))) *(ULONG *)data = reply->suspend_count; + } + SERVER_END_REQ; + return status; + + case ThreadDescription: + { + THREAD_DESCRIPTION_INFORMATION *info = data; + data_size_t len, desc_len = 0; + WCHAR *ptr; + + len = length >= sizeof(*info) ? length - sizeof(*info) : 0; + ptr = info ? (WCHAR *)(info + 1) : NULL; + + SERVER_START_REQ( get_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + if (ptr) wine_server_set_reply( req, ptr, len ); + status = wine_server_call( req ); + desc_len = reply->desc_len; + } + SERVER_END_REQ; + + if (!info) status = STATUS_BUFFER_TOO_SMALL; + else if (status == STATUS_SUCCESS) + { + info->Description.Length = info->Description.MaximumLength = desc_len; + info->Description.Buffer = ptr; + } + + if (ret_len && (status == STATUS_SUCCESS || status == STATUS_BUFFER_TOO_SMALL)) + *ret_len = sizeof(*info) + desc_len; + return status; + } + + case ThreadPriority: + case ThreadBasePriority: + case ThreadImpersonationToken: + case ThreadEnableAlignmentFaultFixup: + case ThreadEventPair_Reusable: + case ThreadZeroTlsCell: + case ThreadPerformanceCount: + case ThreadIdealProcessor: + case ThreadPriorityBoost: + case ThreadSetTlsArrayAddress: + default: + FIXME( "info class %d not supported yet\n", class ); + return STATUS_NOT_IMPLEMENTED; + } +} + + +/****************************************************************************** + * NtSetInformationThread (NTDLL.@) + */ +NTSTATUS WINAPI NtSetInformationThread( HANDLE handle, THREADINFOCLASS class, + const void *data, ULONG length ) +{ + NTSTATUS status; + + switch (class) + { + case ThreadZeroTlsCell: + if (handle == GetCurrentThread()) + { + if (length != sizeof(DWORD)) return STATUS_INVALID_PARAMETER; + return virtual_clear_tls_index( *(const ULONG *)data ); + } + FIXME( "ZeroTlsCell not supported on other threads\n" ); + return STATUS_NOT_IMPLEMENTED; + + case ThreadImpersonationToken: + { + const HANDLE *token = data; + + if (length != sizeof(HANDLE)) return STATUS_INVALID_PARAMETER; + TRACE("Setting ThreadImpersonationToken handle to %p\n", *token ); + SERVER_START_REQ( set_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->token = wine_server_obj_handle( *token ); + req->mask = SET_THREAD_INFO_TOKEN; + status = wine_server_call( req ); + } + SERVER_END_REQ; + return status; + } + + case ThreadBasePriority: + { + 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 ThreadAffinityMask: + { + const ULONG_PTR affinity_mask = get_system_affinity_mask(); + ULONG_PTR req_aff; + + if (length != sizeof(ULONG_PTR)) return STATUS_INVALID_PARAMETER; + req_aff = *(const ULONG_PTR *)data & affinity_mask; + if (!req_aff) return STATUS_INVALID_PARAMETER; + + SERVER_START_REQ( set_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->affinity = req_aff; + req->mask = SET_THREAD_INFO_AFFINITY; + status = wine_server_call( req ); + } + SERVER_END_REQ; + return status; + } + + case ThreadHideFromDebugger: + /* pretend the call succeeded to satisfy some code protectors */ + return STATUS_SUCCESS; + + case ThreadQuerySetWin32StartAddress: + { + const PRTL_THREAD_START_ROUTINE *entry = data; + if (length != sizeof(PRTL_THREAD_START_ROUTINE)) return STATUS_INVALID_PARAMETER; + SERVER_START_REQ( set_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->mask = SET_THREAD_INFO_ENTRYPOINT; + req->entry_point = wine_server_client_ptr( *entry ); + status = wine_server_call( req ); + } + SERVER_END_REQ; + return status; + } + + case ThreadGroupInformation: + { + const ULONG_PTR affinity_mask = get_system_affinity_mask(); + const GROUP_AFFINITY *req_aff; + + if (length != sizeof(*req_aff)) return STATUS_INVALID_PARAMETER; + if (!data) return STATUS_ACCESS_VIOLATION; + req_aff = data; + + /* On Windows the request fails if the reserved fields are set */ + if (req_aff->Reserved[0] || req_aff->Reserved[1] || req_aff->Reserved[2]) + return STATUS_INVALID_PARAMETER; + + /* Wine only supports max 64 processors */ + if (req_aff->Group) return STATUS_INVALID_PARAMETER; + if (req_aff->Mask & ~affinity_mask) return STATUS_INVALID_PARAMETER; + if (!req_aff->Mask) return STATUS_INVALID_PARAMETER; + SERVER_START_REQ( set_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->affinity = req_aff->Mask; + req->mask = SET_THREAD_INFO_AFFINITY; + status = wine_server_call( req ); + } + SERVER_END_REQ; + return status; + } + + case ThreadDescription: + { + const THREAD_DESCRIPTION_INFORMATION *info = data; + + if (length != sizeof(*info)) return STATUS_INFO_LENGTH_MISMATCH; + if (!info) return STATUS_ACCESS_VIOLATION; + if (info->Description.Length != info->Description.MaximumLength) return STATUS_INVALID_PARAMETER; + if (info->Description.Length && !info->Description.Buffer) return STATUS_ACCESS_VIOLATION; + + SERVER_START_REQ( set_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->mask = SET_THREAD_INFO_DESCRIPTION; + wine_server_add_data( req, info->Description.Buffer, info->Description.Length ); + status = wine_server_call( req ); + } + SERVER_END_REQ; + return status; + } + + case ThreadBasicInformation: + case ThreadTimes: + case ThreadPriority: + case ThreadDescriptorTableEntry: + case ThreadEnableAlignmentFaultFixup: + case ThreadEventPair_Reusable: + case ThreadPerformanceCount: + case ThreadAmILastThread: + case ThreadIdealProcessor: + case ThreadPriorityBoost: + case ThreadSetTlsArrayAddress: + case ThreadIsIoPending: + default: + FIXME( "info class %d not supported yet\n", class ); + return STATUS_NOT_IMPLEMENTED; + } +} + + +/****************************************************************************** + * NtGetCurrentProcessorNumber (NTDLL.@) + */ +ULONG WINAPI NtGetCurrentProcessorNumber(void) +{ + ULONG processor; + +#if defined(__linux__) && defined(__NR_getcpu) + int res = syscall(__NR_getcpu, &processor, NULL, NULL); + if (res != -1) return processor; +#endif + + if (NtCurrentTeb()->Peb->NumberOfProcessors > 1) + { + ULONG_PTR thread_mask, processor_mask; + + if (!NtQueryInformationThread( GetCurrentThread(), ThreadAffinityMask, + &thread_mask, sizeof(thread_mask), NULL )) + { + for (processor = 0; processor < NtCurrentTeb()->Peb->NumberOfProcessors; processor++) + { + processor_mask = (1 << processor); + if (thread_mask & processor_mask) + { + if (thread_mask != processor_mask) + FIXME( "need multicore support (%d processors)\n", + NtCurrentTeb()->Peb->NumberOfProcessors ); + return processor; + } + } + } + } + /* fallback to the first processor */ + return 0; +} diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 418be951338..dbd8ce31528 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -22,6 +22,7 @@ #define __NTDLL_UNIX_PRIVATE_H #include "unixlib.h" +#include "wine/list.h" #ifndef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 #define InterlockedCompareExchange64(dest,xchg,cmp) RtlInterlockedCompareExchange64(dest,xchg,cmp) @@ -45,6 +46,7 @@ struct ntdll_thread_data int wait_fd[2]; /* fd for sleeping server requests */ BOOL wow64_redir; /* Wow64 filesystem redirection flag */ pthread_t pthread_id; /* pthread thread id */ + struct list entry; /* entry in TEB list */ }; C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); @@ -113,7 +115,6 @@ extern TEB * CDECL init_threading( int *nb_threads_ptr, struct ldt_copy **ldt_co timeout_t *start_time ) DECLSPEC_HIDDEN; extern void CDECL DECLSPEC_NORETURN exit_thread( int status ) DECLSPEC_HIDDEN; extern void CDECL DECLSPEC_NORETURN exit_process( int status ) DECLSPEC_HIDDEN; -extern NTSTATUS CDECL get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len ) DECLSPEC_HIDDEN; extern NTSTATUS CDECL exec_process( UNICODE_STRING *path, UNICODE_STRING *cmdline, NTSTATUS status ) DECLSPEC_HIDDEN; extern NTSTATUS CDECL nt_to_unix_file_name( const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret, @@ -173,6 +174,7 @@ extern ULONG_PTR get_system_affinity_mask(void) DECLSPEC_HIDDEN; extern TEB *virtual_alloc_first_teb(void) DECLSPEC_HIDDEN; extern NTSTATUS virtual_alloc_teb( TEB **ret_teb ) DECLSPEC_HIDDEN; extern void virtual_free_teb( TEB *teb ) DECLSPEC_HIDDEN; +extern NTSTATUS virtual_clear_tls_index( ULONG index ) DECLSPEC_HIDDEN; extern void virtual_map_user_shared_data(void) DECLSPEC_HIDDEN; extern NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err, BOOL on_signal_stack ) DECLSPEC_HIDDEN; extern unsigned int virtual_locked_server_call( void *req_ptr ) DECLSPEC_HIDDEN; @@ -188,6 +190,7 @@ extern void virtual_set_force_exec( BOOL enable ) DECLSPEC_HIDDEN; extern void virtual_fill_image_information( const pe_image_info_t *pe_info, SECTION_IMAGE_INFORMATION *info ) DECLSPEC_HIDDEN; +extern NTSTATUS get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len ) DECLSPEC_HIDDEN; extern void signal_init_threading(void) DECLSPEC_HIDDEN; extern NTSTATUS signal_alloc_thread( TEB *teb ) DECLSPEC_HIDDEN; extern void signal_free_thread( TEB *teb ) DECLSPEC_HIDDEN; diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index ad625048665..a32059f0197 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -170,6 +170,7 @@ static SIZE_T signal_stack_align; static TEB *teb_block; static TEB *next_free_teb; static int teb_block_pos; +static struct list teb_list = LIST_INIT( teb_list ); #define ROUND_ADDR(addr,mask) ((void *)((UINT_PTR)(addr) & ~(UINT_PTR)(mask))) #define ROUND_SIZE(addr,size) (((SIZE_T)(size) + ((UINT_PTR)(addr) & page_mask) + page_mask) & ~page_mask) @@ -2537,6 +2538,25 @@ NTSTATUS CDECL virtual_create_builtin_view( void *module ) } +/* set some initial values in a new TEB */ +static void init_teb( TEB *teb, PEB *peb ) +{ + struct ntdll_thread_data *thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch; + + teb->Peb = peb; + teb->Tib.Self = &teb->Tib; + teb->Tib.ExceptionList = (void *)~0ul; + teb->Tib.StackBase = (void *)~0ul; + teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; + teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); + thread_data->request_fd = -1; + thread_data->reply_fd = -1; + thread_data->wait_fd[0] = -1; + thread_data->wait_fd[1] = -1; + list_add_head( &teb_list, &thread_data->entry ); +} + + /*********************************************************************** * virtual_alloc_first_teb */ @@ -2566,13 +2586,7 @@ TEB *virtual_alloc_first_teb(void) peb = (PEB *)((char *)teb_block + 32 * teb_size - peb_size); NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&teb, 0, &teb_size, MEM_COMMIT, PAGE_READWRITE ); NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&peb, 0, &peb_size, MEM_COMMIT, PAGE_READWRITE ); - - teb->Peb = peb; - teb->Tib.Self = &teb->Tib; - teb->Tib.ExceptionList = (void *)~0ul; - teb->Tib.StackBase = (void *)~0ul; - teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; - teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); + init_teb( teb, peb ); use_locks = TRUE; return teb; } @@ -2615,14 +2629,10 @@ NTSTATUS virtual_alloc_teb( TEB **ret_teb ) NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&teb, 0, &teb_size, MEM_COMMIT, PAGE_READWRITE ); } + init_teb( teb, NtCurrentTeb()->Peb ); + *ret_teb = teb; server_leave_uninterrupted_section( &csVirtual, &sigset ); - *ret_teb = teb; - teb->Peb = NtCurrentTeb()->Peb; - teb->Tib.Self = &teb->Tib; - teb->Tib.ExceptionList = (void *)~0UL; - teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; - teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); if ((status = signal_alloc_thread( teb ))) { server_enter_uninterrupted_section( &csVirtual, &sigset ); @@ -2656,12 +2666,49 @@ void virtual_free_teb( TEB *teb ) } server_enter_uninterrupted_section( &csVirtual, &sigset ); + list_remove( &thread_data->entry ); *(TEB **)teb = next_free_teb; next_free_teb = teb; server_leave_uninterrupted_section( &csVirtual, &sigset ); } +/*********************************************************************** + * virtual_clear_tls_index + */ +NTSTATUS virtual_clear_tls_index( ULONG index ) +{ + struct ntdll_thread_data *thread_data; + sigset_t sigset; + + if (index < TLS_MINIMUM_AVAILABLE) + { + server_enter_uninterrupted_section( &csVirtual, &sigset ); + LIST_FOR_EACH_ENTRY( thread_data, &teb_list, struct ntdll_thread_data, entry ) + { + TEB *teb = CONTAINING_RECORD( thread_data, TEB, GdiTebBatch ); + teb->TlsSlots[index] = 0; + } + server_leave_uninterrupted_section( &csVirtual, &sigset ); + } + else + { + index -= TLS_MINIMUM_AVAILABLE; + if (index >= 8 * sizeof(NtCurrentTeb()->Peb->TlsExpansionBitmapBits)) + return STATUS_INVALID_PARAMETER; + + server_enter_uninterrupted_section( &csVirtual, &sigset ); + LIST_FOR_EACH_ENTRY( thread_data, &teb_list, struct ntdll_thread_data, entry ) + { + TEB *teb = CONTAINING_RECORD( thread_data, TEB, GdiTebBatch ); + if (teb->TlsExpansionSlots) teb->TlsExpansionSlots[index] = 0; + } + server_leave_uninterrupted_section( &csVirtual, &sigset ); + } + return STATUS_SUCCESS; +} + + /*********************************************************************** * virtual_alloc_thread_stack */ diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h index 37238fee83d..6037b6e0e6a 100644 --- a/dlls/ntdll/unixlib.h +++ b/dlls/ntdll/unixlib.h @@ -28,7 +28,7 @@ struct ldt_copy; struct msghdr; /* increment this when you change the function table */ -#define NTDLL_UNIXLIB_VERSION 53 +#define NTDLL_UNIXLIB_VERSION 54 struct unix_funcs { @@ -105,6 +105,7 @@ struct unix_funcs void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size ); NTSTATUS (WINAPI *NtGetContextThread)( HANDLE handle, CONTEXT *context ); + ULONG (WINAPI *NtGetCurrentProcessorNumber)(void); NTSTATUS (WINAPI *NtGetWriteWatch)( HANDLE process, ULONG flags, PVOID base, SIZE_T size, PVOID *addresses, ULONG_PTR *count, ULONG *granularity ); NTSTATUS (WINAPI *NtIsProcessInJob)( HANDLE process, HANDLE job ); @@ -154,6 +155,8 @@ struct unix_funcs void *info, ULONG len, ULONG *ret_len ); NTSTATUS (WINAPI *NtQueryInformationProcess)( HANDLE handle, PROCESSINFOCLASS class, void *info, ULONG size, ULONG *ret_len ); + NTSTATUS (WINAPI *NtQueryInformationThread)( HANDLE handle, THREADINFOCLASS class, + void *data, ULONG length, ULONG *ret_len ); NTSTATUS (WINAPI *NtQueryIoCompletion)( HANDLE handle, IO_COMPLETION_INFORMATION_CLASS class, void *buffer, ULONG len, ULONG *ret_len ); NTSTATUS (WINAPI *NtQueryMutant)( HANDLE handle, MUTANT_INFORMATION_CLASS class, @@ -202,6 +205,8 @@ struct unix_funcs void *info, ULONG len ); NTSTATUS (WINAPI *NtSetInformationProcess)( HANDLE handle, PROCESSINFOCLASS class, void *info, ULONG size ); + NTSTATUS (WINAPI *NtSetInformationThread)( HANDLE handle, THREADINFOCLASS class, + const void *data, ULONG length ); NTSTATUS (WINAPI *NtSetIoCompletion)( HANDLE handle, ULONG_PTR key, ULONG_PTR value, NTSTATUS status, SIZE_T count ); NTSTATUS (WINAPI *NtSetLdtEntries)( ULONG sel1, LDT_ENTRY entry1, ULONG sel2, LDT_ENTRY entry2 ); @@ -291,7 +296,6 @@ struct unix_funcs BOOL *suspend, unsigned int *cpus, BOOL *wow64, timeout_t *start_time ); void (CDECL *exit_thread)( int status ); void (CDECL *exit_process)( int status ); - NTSTATUS (CDECL *get_thread_ldt_entry)( HANDLE handle, void *data, ULONG len, ULONG *ret_len ); NTSTATUS (CDECL *exec_process)( UNICODE_STRING *path, UNICODE_STRING *cmdline, NTSTATUS status ); /* server functions */