This MR implements group affinity support in attributes list passed to CreateRemoteThreadEx().
(up to the current Wine limit of 64 logical cores).
It also adds a couple of missing APIs definitions related to group affinity support.
Notes: - duplicating for the Nth time struct _PROC_THREAD_ATTRIBUTE_LIST definition isn't very nice... perhaps we should define it somewhere? - it's very likely that CreateRemoteThreadEx() should call into NtCreateThreadEx() (instead of the Rtl counter part), but I opted for the simpler approach to set thread affinity after thread creation (this could be changed if needed).
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/kernel32/tests/process.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c index f539f69b3af..4e3a272270e 100644 --- a/dlls/kernel32/tests/process.c +++ b/dlls/kernel32/tests/process.c @@ -4391,6 +4391,7 @@ static void test_ProcThreadAttributeList(void) int i; struct _PROC_THREAD_ATTRIBUTE_LIST list, expect_list; HANDLE handles[4]; + GROUP_AFFINITY gaff = {.Group = 0, .Mask = 0xffff};
if (!pInitializeProcThreadAttributeList) { @@ -4490,6 +4491,18 @@ static void test_ProcThreadAttributeList(void) expect_list.attrs[i].value = handles; }
+ ret = pUpdateProcThreadAttribute(&list, 0, PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY, &gaff, sizeof(gaff), NULL, NULL); + todo_wine + ok(ret, "got %d gle %ld\n", ret, GetLastError()); + if (ret) + { + unsigned int i = expect_list.count++; + expect_list.mask |= 1 << ProcThreadAttributeGroupAffinity; + expect_list.attrs[i].attr = PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY; + expect_list.attrs[i].size = sizeof(GROUP_AFFINITY); + expect_list.attrs[i].value = &gaff; + } + ok(!memcmp(&list, &expect_list, size), "mismatch\n");
pDeleteProcThreadAttributeList(&list);
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/kernel32/tests/process.c | 1 - dlls/kernelbase/process.c | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c index 4e3a272270e..db7b364534d 100644 --- a/dlls/kernel32/tests/process.c +++ b/dlls/kernel32/tests/process.c @@ -4492,7 +4492,6 @@ static void test_ProcThreadAttributeList(void) }
ret = pUpdateProcThreadAttribute(&list, 0, PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY, &gaff, sizeof(gaff), NULL, NULL); - todo_wine ok(ret, "got %d gle %ld\n", ret, GetLastError()); if (ret) { diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index e1c28fc67ed..56ee5b2fbfb 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -1798,6 +1798,9 @@ static inline DWORD validate_proc_thread_attribute( DWORD_PTR attr, SIZE_T size case PROC_THREAD_ATTRIBUTE_MACHINE_TYPE: if (size != sizeof(USHORT)) return ERROR_BAD_LENGTH; break; + case PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY: + if (size != sizeof(GROUP_AFFINITY)) return ERROR_BAD_LENGTH; + break; default: FIXME( "Unhandled attribute %Iu\n", attr & PROC_THREAD_ATTRIBUTE_NUMBER ); return ERROR_NOT_SUPPORTED;
From: Eric Pouech epouech@codeweavers.com
--- include/winbase.h | 6 ++++++ include/winnt.h | 23 +++++++++++++++++------ include/winternl.h | 2 ++ 3 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/include/winbase.h b/include/winbase.h index c96f58a61b3..0213b40ef57 100644 --- a/include/winbase.h +++ b/include/winbase.h @@ -1823,6 +1823,7 @@ WINADVAPI PVOID WINAPI FreeSid(PSID); WINADVAPI BOOL WINAPI GetAce(PACL,DWORD,LPVOID*); WINADVAPI BOOL WINAPI GetAclInformation(PACL,LPVOID,DWORD,ACL_INFORMATION_CLASS); WINBASEAPI DWORD WINAPI GetActiveProcessorCount(WORD); +WINBASEAPI WORD WINAPI GetActiveProcessorGroupCount(void); WINBASEAPI HRESULT WINAPI GetApplicationRestartSettings(HANDLE,WCHAR*,DWORD*,DWORD*); WINBASEAPI UINT WINAPI GetAtomNameA(ATOM,LPSTR,INT); WINBASEAPI UINT WINAPI GetAtomNameW(ATOM,LPWSTR,INT); @@ -1921,6 +1922,7 @@ WINBASEAPI DWORD WINAPI GetLongPathNameW(LPCWSTR,LPWSTR,DWORD); #define GetLongPathName WINELIB_NAME_AW(GetLongPathName) WINBASEAPI BOOL WINAPI GetMailslotInfo(HANDLE,LPDWORD,LPDWORD,LPDWORD,LPDWORD); WINBASEAPI DWORD WINAPI GetMaximumProcessorCount(WORD); +WINBASEAPI WORD WINAPI GetMaximumProcessorGroupCount(void); WINBASEAPI DWORD WINAPI GetModuleFileNameA(HMODULE,LPSTR,DWORD); WINBASEAPI DWORD WINAPI GetModuleFileNameW(HMODULE,LPWSTR,DWORD); #define GetModuleFileName WINELIB_NAME_AW(GetModuleFileName) @@ -1969,6 +1971,7 @@ WINBASEAPI BOOL WINAPI GetPrivateProfileStructW(LPCWSTR,LPCWSTR,LPVOID,UI #define GetPrivateProfileStruct WINELIB_NAME_AW(GetPrivateProfileStruct) WINBASEAPI FARPROC WINAPI GetProcAddress(HMODULE,LPCSTR); WINBASEAPI BOOL WINAPI GetProcessAffinityMask(HANDLE,PDWORD_PTR,PDWORD_PTR); +WINBASEAPI BOOL WINAPI GetProcessGroupAffinity(HANDLE, PUSHORT, PUSHORT); WINBASEAPI DWORD WINAPI GetProcessHeaps(DWORD,PHANDLE); WINBASEAPI DWORD WINAPI GetProcessId(HANDLE); WINBASEAPI DWORD WINAPI GetProcessIdOfThread(HANDLE); @@ -2031,6 +2034,7 @@ WINBASEAPI DWORD WINAPI GetTempPathW(DWORD,LPWSTR); #define GetTempPath WINELIB_NAME_AW(GetTempPath) WINBASEAPI BOOL WINAPI GetThreadContext(HANDLE,CONTEXT *); WINBASEAPI DWORD WINAPI GetThreadErrorMode(void); +WINBASEAPI BOOL WINAPI GetThreadGroupAffinity(HANDLE, PGROUP_AFFINITY); WINBASEAPI DWORD WINAPI GetThreadId(HANDLE); WINBASEAPI BOOL WINAPI GetThreadIOPendingFlag(HANDLE,PBOOL); WINBASEAPI INT WINAPI GetThreadPriority(HANDLE); @@ -2409,7 +2413,9 @@ WINBASEAPI DWORD_PTR WINAPI SetThreadAffinityMask(HANDLE,DWORD_PTR); WINBASEAPI BOOL WINAPI SetThreadContext(HANDLE,const CONTEXT *); WINBASEAPI BOOL WINAPI SetThreadErrorMode(DWORD,LPDWORD); WINBASEAPI DWORD WINAPI SetThreadExecutionState(EXECUTION_STATE); +WINBASEAPI BOOL WINAPI SetThreadGroupAffinity(HANDLE, const GROUP_AFFINITY *, GROUP_AFFINITY *); WINBASEAPI DWORD WINAPI SetThreadIdealProcessor(HANDLE,DWORD); +WINBASEAPI BOOL WINAPI SetThreadIdealProcessorEx(HANDLE, PROCESSOR_NUMBER *, PROCESSOR_NUMBER *); WINBASEAPI BOOL WINAPI SetThreadPriority(HANDLE,INT); WINBASEAPI BOOL WINAPI SetThreadPriorityBoost(HANDLE,BOOL); WINADVAPI BOOL WINAPI SetThreadToken(PHANDLE,HANDLE); diff --git a/include/winnt.h b/include/winnt.h index d360f5d0178..e53446472d6 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -734,10 +734,11 @@ typedef DWORD FLONG; #define PROCESSOR_OPTIL 18767
#ifdef _WIN64 -#define MAXIMUM_PROCESSORS 64 +#define MAXIMUM_PROC_PER_GROUP 64 #else -#define MAXIMUM_PROCESSORS 32 +#define MAXIMUM_PROC_PER_GROUP 32 #endif +#define MAXIMUM_PROCESSORS MAXIMUM_PROC_PER_GROUP
typedef struct _MEMORY_BASIC_INFORMATION { @@ -6666,8 +6667,13 @@ typedef struct _PROCESSOR_RELATIONSHIP typedef struct _NUMA_NODE_RELATIONSHIP { DWORD NodeNumber; - BYTE Reserved[20]; - GROUP_AFFINITY GroupMask; + BYTE Reserved[18]; + WORD GroupCount; + union + { + GROUP_AFFINITY GroupMask; + GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY]; + }; } NUMA_NODE_RELATIONSHIP, *PNUMA_NODE_RELATIONSHIP;
typedef struct _CACHE_RELATIONSHIP @@ -6677,8 +6683,13 @@ typedef struct _CACHE_RELATIONSHIP WORD LineSize; DWORD CacheSize; PROCESSOR_CACHE_TYPE Type; - BYTE Reserved[20]; - GROUP_AFFINITY GroupMask; + BYTE Reserved[18]; + WORD GroupCount; + union + { + GROUP_AFFINITY GroupMask; + GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY]; + }; } CACHE_RELATIONSHIP, *PCACHE_RELATIONSHIP;
typedef struct _GROUP_RELATIONSHIP diff --git a/include/winternl.h b/include/winternl.h index 2c985465096..6ee4192bb64 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -4586,6 +4586,7 @@ NTSYSAPI NTSTATUS WINAPI NtFreeVirtualMemory(HANDLE,PVOID*,SIZE_T*,ULONG); NTSYSAPI NTSTATUS WINAPI NtFsControlFile(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK,ULONG,PVOID,ULONG,PVOID,ULONG); NTSYSAPI NTSTATUS WINAPI NtGetContextThread(HANDLE,CONTEXT*); NTSYSAPI ULONG WINAPI NtGetCurrentProcessorNumber(void); +NTSYSAPI ULONG WINAPI NtGetCurrentProcessorNumberEx(PROCESSOR_NUMBER*); NTSYSAPI NTSTATUS WINAPI NtGetNextProcess(HANDLE,ACCESS_MASK,ULONG,ULONG,HANDLE*); NTSYSAPI NTSTATUS WINAPI NtGetNextThread(HANDLE,HANDLE,ACCESS_MASK,ULONG,ULONG,HANDLE*); NTSYSAPI NTSTATUS WINAPI NtGetNlsSectionPtr(ULONG,ULONG,void*,void**,SIZE_T*); @@ -4955,6 +4956,7 @@ NTSYSAPI NTSTATUS WINAPI RtlGetCompressionWorkSpaceSize(USHORT,PULONG,PULONG); NTSYSAPI NTSTATUS WINAPI RtlGetControlSecurityDescriptor(PSECURITY_DESCRIPTOR, PSECURITY_DESCRIPTOR_CONTROL,LPDWORD); NTSYSAPI ULONG WINAPI RtlGetCurrentDirectory_U(ULONG, LPWSTR); NTSYSAPI PEB * WINAPI RtlGetCurrentPeb(void); +NTSYSAPI ULONG WINAPI RtlGetCurrentProcessorNumber(void); NTSYSAPI void WINAPI RtlGetCurrentProcessorNumberEx(PROCESSOR_NUMBER*); NTSYSAPI HANDLE WINAPI RtlGetCurrentTransaction(void); NTSYSAPI NTSTATUS WINAPI RtlGetDaclSecurityDescriptor(PSECURITY_DESCRIPTOR,PBOOLEAN,PACL *,PBOOLEAN);
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/kernel32/tests/thread.c | 95 ++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 3 deletions(-)
diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c index c0303b471e8..483b1cdd987 100644 --- a/dlls/kernel32/tests/thread.c +++ b/dlls/kernel32/tests/thread.c @@ -88,21 +88,26 @@ static BOOL (WINAPI *pUnregisterWait)(HANDLE); static BOOL (WINAPI *pIsWow64Process)(HANDLE,PBOOL); static BOOL (WINAPI *pSetThreadErrorMode)(DWORD,PDWORD); static DWORD (WINAPI *pGetThreadErrorMode)(void); -static DWORD (WINAPI *pRtlGetThreadErrorMode)(void); static PTP_POOL (WINAPI *pCreateThreadpool)(PVOID); static void (WINAPI *pCloseThreadpool)(PTP_POOL); static PTP_WORK (WINAPI *pCreateThreadpoolWork)(PTP_WORK_CALLBACK,PVOID,PTP_CALLBACK_ENVIRON); static void (WINAPI *pSubmitThreadpoolWork)(PTP_WORK); static void (WINAPI *pWaitForThreadpoolWorkCallbacks)(PTP_WORK,BOOL); static void (WINAPI *pCloseThreadpoolWork)(PTP_WORK); -static NTSTATUS (WINAPI *pNtQueryInformationThread)(HANDLE,THREADINFOCLASS,PVOID,ULONG,PULONG); static BOOL (WINAPI *pGetThreadGroupAffinity)(HANDLE,GROUP_AFFINITY*); static BOOL (WINAPI *pSetThreadGroupAffinity)(HANDLE,const GROUP_AFFINITY*,GROUP_AFFINITY*); -static NTSTATUS (WINAPI *pNtSetInformationThread)(HANDLE,THREADINFOCLASS,LPCVOID,ULONG); +static BOOL (WINAPI *pInitializeProcThreadAttributeList)(struct _PROC_THREAD_ATTRIBUTE_LIST*, DWORD, DWORD, SIZE_T*); +static BOOL (WINAPI *pUpdateProcThreadAttribute)(struct _PROC_THREAD_ATTRIBUTE_LIST*, DWORD, DWORD_PTR, void *,SIZE_T,void*,SIZE_T*); +static void (WINAPI *pDeleteProcThreadAttributeList)(struct _PROC_THREAD_ATTRIBUTE_LIST*); static HRESULT (WINAPI *pSetThreadDescription)(HANDLE,const WCHAR *); static HRESULT (WINAPI *pGetThreadDescription)(HANDLE,WCHAR **); +static HANDLE (WINAPI *pCreateRemoteThreadEx)(HANDLE, SECURITY_ATTRIBUTES *, SIZE_T, LPTHREAD_START_ROUTINE, + LPVOID, DWORD, LPPROC_THREAD_ATTRIBUTE_LIST, DWORD *); +static NTSTATUS (WINAPI *pNtSetInformationThread)(HANDLE,THREADINFOCLASS,LPCVOID,ULONG); static PVOID (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG,PVECTORED_EXCEPTION_HANDLER); static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID); +static NTSTATUS (WINAPI *pNtQueryInformationThread)(HANDLE,THREADINFOCLASS,PVOID,ULONG,PULONG); +static DWORD (WINAPI *pRtlGetThreadErrorMode)(void);
static HANDLE create_target_process(const char *arg) { @@ -2634,6 +2639,12 @@ static void init_funcs(void) X(FlsFree); X(FlsSetValue); X(FlsGetValue); + + X(InitializeProcThreadAttributeList); + X(UpdateProcThreadAttribute); + X(DeleteProcThreadAttributeList); + + X(CreateRemoteThreadEx); #undef X
#define X(f) p##f = (void*)GetProcAddress(ntdll, #f) @@ -2648,6 +2659,83 @@ static void init_funcs(void) #undef X }
+static DWORD CALLBACK thread_ex_proc(void *_pmt) +{ + BOOL ret; + + ret = GetThreadGroupAffinity(GetCurrentThread(), (GROUP_AFFINITY *)_pmt); + ok(ret, "Expected GetTheadGroupAffinity to succeed\n"); + return 0; +} + +static void test_CreateRemoteThreadEx_affinity(void) +{ + struct _PROC_THREAD_ATTRIBUTE_LIST *attr_list; + GROUP_AFFINITY gaff, thread_gaff; + HANDLE handle; + SIZE_T size; + BOOL ret; + + if (RtlGetCurrentPeb()->NumberOfProcessors < 2) + { + skip("Not enough cores to test\n"); + return; + } + + ret = pInitializeProcThreadAttributeList(NULL, 1, 0, &size); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); + + attr_list = HeapAlloc(GetProcessHeap(), 0, size); + ret = pInitializeProcThreadAttributeList(attr_list, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); + memset(&gaff, 0, sizeof(gaff)); + gaff.Mask = (ULONG_PTR)1u << ((GetCurrentProcessorNumber() + 1) % RtlGetCurrentPeb()->NumberOfProcessors); + ret = pUpdateProcThreadAttribute(attr_list, 0, PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY, + &gaff, sizeof(gaff), NULL, NULL); + ok(ret, "Couldn't update attr_list\n"); + + handle = pCreateRemoteThreadEx(GetCurrentProcess(), NULL, 0, &thread_ex_proc, &thread_gaff, 0, attr_list, NULL); + ok(handle != NULL, "Couldn't create thread %lu %p %lu\n", GetLastError(), &thread_ex_proc, RtlGetCurrentPeb()->NumberOfProcessors); + + ret = WaitForSingleObject(handle, INFINITE) == WAIT_OBJECT_0; + ok(ret, "Couldn't wait for thread termination\n"); + ok(thread_gaff.Group == gaff.Group, "Unexpected group %x (expecting %x)\n", thread_gaff.Group, gaff.Group); + todo_wine + ok(thread_gaff.Mask == gaff.Mask, "Unexpected affinity %Ix (expecting %Ix)\n", thread_gaff.Mask, gaff.Mask); + CloseHandle(handle); + + pDeleteProcThreadAttributeList(attr_list); + HeapFree(GetProcessHeap(), 0, attr_list); + + ret = pInitializeProcThreadAttributeList(NULL, 1, 0, &size); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); + + /* check invalid core number */ + if ((RtlGetCurrentPeb()->NumberOfProcessors + 1) < MAXIMUM_PROCESSORS) + { + attr_list = HeapAlloc(GetProcessHeap(), 0, size); + ret = pInitializeProcThreadAttributeList(attr_list, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); + memset(&gaff, 0, sizeof(gaff)); + gaff.Mask = (ULONG_PTR)1u << (RtlGetCurrentPeb()->NumberOfProcessors + 1); + ret = pUpdateProcThreadAttribute(attr_list, 0, PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY, + &gaff, sizeof(gaff), NULL, NULL); + ok(ret, "Couldn't update attr_list\n"); + + SetLastError(0xdeadbeef); + handle = pCreateRemoteThreadEx(GetCurrentProcess(), NULL, 0, &thread_ex_proc, &thread_gaff, 0, attr_list, NULL); + todo_wine + ok(handle == NULL, "Expecting failure\n"); + todo_wine + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected gle %lu\n", GetLastError()); + if (handle) CloseHandle(handle); + pDeleteProcThreadAttributeList(attr_list); + HeapFree(GetProcessHeap(), 0, attr_list); + } +} + START_TEST(thread) { int argc; @@ -2709,6 +2797,7 @@ START_TEST(thread) test_thread_fpu_cw(); test_thread_actctx(); test_thread_description(); + test_CreateRemoteThreadEx_affinity();
test_threadpool(); }
From: Eric Pouech epouech@codeweavers.com
In CreateRemoteThreadEx().
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/kernel32/tests/thread.c | 3 --- dlls/kernelbase/thread.c | 44 ++++++++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 5 deletions(-)
diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c index 483b1cdd987..54d838da7bb 100644 --- a/dlls/kernel32/tests/thread.c +++ b/dlls/kernel32/tests/thread.c @@ -2701,7 +2701,6 @@ static void test_CreateRemoteThreadEx_affinity(void) ret = WaitForSingleObject(handle, INFINITE) == WAIT_OBJECT_0; ok(ret, "Couldn't wait for thread termination\n"); ok(thread_gaff.Group == gaff.Group, "Unexpected group %x (expecting %x)\n", thread_gaff.Group, gaff.Group); - todo_wine ok(thread_gaff.Mask == gaff.Mask, "Unexpected affinity %Ix (expecting %Ix)\n", thread_gaff.Mask, gaff.Mask); CloseHandle(handle);
@@ -2726,9 +2725,7 @@ static void test_CreateRemoteThreadEx_affinity(void)
SetLastError(0xdeadbeef); handle = pCreateRemoteThreadEx(GetCurrentProcess(), NULL, 0, &thread_ex_proc, &thread_gaff, 0, attr_list, NULL); - todo_wine ok(handle == NULL, "Expecting failure\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected gle %lu\n", GetLastError()); if (handle) CloseHandle(handle); pDeleteProcThreadAttributeList(attr_list); diff --git a/dlls/kernelbase/thread.c b/dlls/kernelbase/thread.c index dbde4dcfaf3..2751c33f791 100644 --- a/dlls/kernelbase/thread.c +++ b/dlls/kernelbase/thread.c @@ -64,6 +64,22 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateRemoteThread( HANDLE process, SECURITY_ATT return CreateRemoteThreadEx( process, sa, stack, start, param, flags, NULL, id ); }
+struct proc_thread_attr +{ + DWORD_PTR attr; + SIZE_T size; + void *value; +}; + +struct _PROC_THREAD_ATTRIBUTE_LIST +{ + DWORD mask; /* bitmask of items in list */ + DWORD size; /* max number of items in list */ + DWORD count; /* number of items in list */ + DWORD pad; + DWORD_PTR unk; + struct proc_thread_attr attrs[1]; +};
/*************************************************************************** * CreateRemoteThreadEx (kernelbase.@) @@ -76,8 +92,27 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateRemoteThreadEx( HANDLE process, SECURITY_A HANDLE handle; CLIENT_ID client_id; SIZE_T stack_reserve = 0, stack_commit = 0; + GROUP_AFFINITY *group_affinity = NULL;
- if (attributes) FIXME("thread attributes ignored\n"); + if (attributes) + { + DWORD i; + for (i = 0; i < attributes->count; i++) + switch (attributes->attrs[i].attr) + { + case PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY: + group_affinity = attributes->attrs[i].value; + if (group_affinity->Group || (group_affinity->Mask >> RtlGetCurrentPeb()->NumberOfProcessors)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + break; + default: + FIXME("thread attributes %Ix ignored\n", attributes->attrs[i].attr); + break; + } + }
if (flags & STACK_SIZE_PARAM_IS_A_RESERVATION) stack_reserve = stack; else stack_commit = stack; @@ -90,7 +125,12 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateRemoteThreadEx( HANDLE process, SECURITY_A if (id) *id = HandleToULong( client_id.UniqueThread ); if (sa && sa->nLength >= sizeof(*sa) && sa->bInheritHandle) SetHandleInformation( handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT ); - if (!(flags & CREATE_SUSPENDED)) + if (group_affinity && !SetThreadGroupAffinity(handle, group_affinity, NULL)) + { + NtClose( handle ); + handle = 0; + } + else if (!(flags & CREATE_SUSPENDED)) { ULONG ret; if (NtResumeThread( handle, &ret ))