Added test for thread functions.
diff --git a/dlls/kernel/Makefile.in b/dlls/kernel/Makefile.in
index 6c2d537..6ca156a 100644
--- a/dlls/kernel/Makefile.in
+++ b/dlls/kernel/Makefile.in
@@ -41,7 +41,8 @@
CTESTS = \
tests/alloc.c \
- tests/directory.c
+ tests/directory.c \
+ tests/thread.c
PLTESTS = \
tests/atom.pl
diff --git a/dlls/kernel/kernel32.spec b/dlls/kernel/kernel32.spec
index ba07c8e..48dc907 100644
--- a/dlls/kernel/kernel32.spec
+++ b/dlls/kernel/kernel32.spec
@@ -940,7 +940,7 @@
@ stdcall SetConsoleInputExeNameW(ptr) SetConsoleInputExeNameW
@ stdcall SetProcessAffinityMask(long long) SetProcessAffinityMask
@ stdcall SetProcessPriorityBoost(long long) SetProcessPriorityBoost
-@ stub SetThreadIdealProcessor
+@ stdcall SetThreadIdealProcessor(long long) SetThreadIdealProcessor
@ stdcall SetThreadPriorityBoost(long long) SetThreadPriorityBoost
@ stdcall SetWaitableTimer(long ptr long ptr ptr long) SetWaitableTimer
@ stub SignalObjectAndWait
diff --git a/dlls/kernel/tests/.cvsignore b/dlls/kernel/tests/.cvsignore
index 3925a6f..1c13767 100644
--- a/dlls/kernel/tests/.cvsignore
+++ b/dlls/kernel/tests/.cvsignore
@@ -1,5 +1,6 @@
alloc.ok
atom.ok
directory.ok
+thread.ok
kernel32_test.spec.c
testlist.c
diff --git a/dlls/kernel/tests/thread.c b/dlls/kernel/tests/thread.c
new file mode 100644
index 0000000..08799c6
--- /dev/null
+++ b/dlls/kernel/tests/thread.c
@@ -0,0 +1,564 @@
+/*
+ * Unit test suite for directory functions.
+ *
+ * Copyright 2002 Geoffrey Hausheer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <winbase.h>
+#include <winnt.h>
+#include <winerror.h>
+
+#include "wine/test.h"
+/* Specify the number of simultaneous threads to test */
+#define NUM_THREADS 4
+/* Specify whether to test the extended priorities for Win2k/XP */
+#define USE_EXTENDED_PRIORITIES 0
+/* Specify whether the TerminateThread tests should be skipped */
+#define SKIP_TERMINATE 0
+/* Specify whether to test the stack allocation in CreateThread */
+#define CHECK_STACK 0
+
+/* Set CHECK_STACK to 1 if you want to try to test the stack-limit from
+ CreateThread. So far I have been unable to make this work, and
+ I am in doubt as to how portable it is. Also, according to MSDN,
+ you shouldn't mix C-run-time-libraries (i.e. alloca) with CreateThread.
+ Anyhow, the check is currently commented out
+*/
+#if CHECK_STACK
+ #ifdef __try
+ #define __TRY __try
+ #define __EXCEPT __except
+ #define __ENDTRY
+ #else
+ #include "wine/exception.h"
+ #endif
+#endif
+typedef HANDLE WINAPI (*OPENTHREADPTR)(DWORD,BOOL,DWORD);
+OPENTHREADPTR OpenThreadPtr;
+HANDLE WINAPI OpenThreadDefault(DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ DWORD dwThreadId)
+{
+ return (HANDLE)NULL;
+}
+/* define a check for whether we are running in Win2k or XP */
+#define WIN2K_PLUS(version) (version < 0x80000000 && (version & 0xFF) >= 5)
+
+/* Functions not tested yet:
+ AttachThreadInput
+ CreateRemoteThread
+ SetThreadContext
+ SwitchToThread
+
+In addition there are no checks that the inheritance works properly in
+CreateThread
+*/
+
+DWORD tlsIndex;
+
+typedef struct {
+ int threadnum;
+ HANDLE *event;
+ DWORD *threadmem;
+} t1Struct;
+
+/* Basic test that simulatneous threads can access shared memory,
+ that the thread local storage routines work correctly, and that
+ threads actually run concurrently
+*/
+VOID WINAPI threadFunc1(t1Struct *tstruct)
+{
+ int i;
+/* write our thread # into shared memory */
+ tstruct->threadmem[tstruct->threadnum]=GetCurrentThreadId();
+ ok(TlsSetValue(tlsIndex,(LPVOID)(tstruct->threadnum+1))!=0,
+ "TlsSetValue failed");
+/* The threads synchronize before terminating. This is done by
+ Signaling an event, and waiting for all events to occur
+*/
+ SetEvent(tstruct->event[tstruct->threadnum]);
+ WaitForMultipleObjects(NUM_THREADS,tstruct->event,TRUE,INFINITE);
+/* Double check that all threads really did run by validating that
+ they have all written to the shared memory. There should be no race
+ here, since all threads were synchronized after the write.*/
+ for(i=0;i<NUM_THREADS;i++) {
+ while(tstruct->threadmem[i]==0) ;
+ }
+/* Check that noone cahnged our tls memory */
+ ok((DWORD)TlsGetValue(tlsIndex)-1==tstruct->threadnum,
+ "TlsGetValue failed");
+ ExitThread(NUM_THREADS+tstruct->threadnum);
+}
+
+VOID WINAPI threadFunc2()
+{
+ ExitThread(99);
+}
+
+VOID WINAPI threadFunc3()
+{
+ HANDLE thread;
+ thread=GetCurrentThread();
+ SuspendThread(thread);
+ ExitThread(99);
+}
+
+VOID WINAPI threadFunc4()
+{
+ Sleep(99000);
+ ExitThread(0);
+}
+
+#if CHECK_STACK
+VOID WINAPI threadFunc5(DWORD *exitCode)
+{
+ SYSTEM_INFO sysInfo;
+ sysInfo.dwPageSize=0;
+ GetSystemInfo(&sysInfo);
+ *exitCode=0;
+ __TRY
+ {
+ alloca(2*sysInfo.dwPageSize);
+ }
+ __EXCEPT(1) {
+ *exitCode=1;
+ }
+ __ENDTRY
+ ExitThread(0);
+}
+#endif
+
+/* Check basic funcationality of CreateThread and Tls* functions */
+VOID test_CreateThread_basic(DWORD version)
+{
+ HANDLE thread[NUM_THREADS],event[NUM_THREADS];
+ DWORD threadid[NUM_THREADS],curthreadId;
+ DWORD threadmem[NUM_THREADS];
+ DWORD exitCode;
+ t1Struct tstruct[NUM_THREADS];
+ int error;
+ int i,j;
+/* Retrieve current Thread ID for later comparisons */
+ curthreadId=GetCurrentThreadId();
+/* Allocate some local storage */
+ ok((tlsIndex=TlsAlloc())!=TLS_OUT_OF_INDEXES,"TlsAlloc failed");
+/* Create events for thread synchronization */
+ for(i=0;i<NUM_THREADS;i++) {
+ threadmem[i]=0;
+/* Note that it doesn't matter what type of event we chose here. This
+ test isn't trying to thoroughly test events
+*/
+ event[i]=CreateEventA(NULL,TRUE,FALSE,NULL);
+ tstruct[i].threadnum=i;
+ tstruct[i].threadmem=threadmem;
+ tstruct[i].event=event;
+ }
+
+/* Test that passing arguments to threads works okay */
+ for(i=0;i<NUM_THREADS;i++) {
+ thread[i] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunc1,
+ &tstruct[i],0,&threadid[i]);
+ ok(thread[i]!=(HANDLE)NULL,"Create Thread failed.");
+ }
+/* Test that the threads actually complete */
+ for(i=0;i<NUM_THREADS;i++) {
+ error=WaitForSingleObject(thread[i],5000);
+ ok(error==WAIT_OBJECT_0, "Thread did not complete within timelimit");
+ if(ok!=WAIT_OBJECT_0) {
+ TerminateThread(thread[i],1);
+ }
+ ok(GetExitCodeThread(thread[i],&exitCode),"Could not retrieve ext code");
+ todo_wine {
+ ok(exitCode==i+NUM_THREADS,"Thread returned an incorrect exit code");
+ }
+ }
+/* Test that each thread executed in its parent's address space
+ (it was able to change threadmem and pass that change back to its parent)
+ and that each thread id was independant). Note that we prove that the
+ threads actually execute concurrently by having them block on each other
+ in threadFunc1
+*/
+ for(i=0;i<NUM_THREADS;i++) {
+ error=0;
+ for(j=i+1;j<NUM_THREADS;j++) {
+ if (threadmem[i]==threadmem[j]) {
+ error=1;
+ }
+ }
+ ok(!error && threadmem[i]==threadid[i] && threadmem[i]!=curthreadId,
+ "Thread did not execute successfully");
+ ok(CloseHandle(thread[i])!=0,"CloseHandle failed");
+ }
+ ok(TlsFree(tlsIndex)!=0,"TlsFree failed");
+}
+
+/* Check that using the CREATE_SUSPENDED flag works */
+VOID test_CreateThread_suspended(DWORD version)
+{
+ HANDLE thread;
+ DWORD threadId;
+ int error;
+
+ thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunc2,NULL,
+ CREATE_SUSPENDED,&threadId);
+ ok(thread!=(HANDLE)NULL,"Create Thread failed.");
+/* Check that the thread is suspended */
+ ok(SuspendThread(thread)==1,"Thread did not start suspended");
+ ok(ResumeThread(thread)==2,"Resume thread returned an invalid value");
+/* Check that resume thread didn't actually start the thread. I can't think
+ of a better way of checking this than just waiting. I am not sure if this
+ will work on slow computers.
+*/
+ ok(WaitForSingleObject(thread,1000)==WAIT_TIMEOUT,
+ "ResumeThread should not have actually started the thread");
+/* Now actually resume the thread and make sure that it actually completes*/
+ ok(ResumeThread(thread)==1,"Resume thread returned an invalid value");
+ ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0,
+ "Thread did not resume");
+ if(error!=WAIT_OBJECT_0) {
+ TerminateThread(thread,1);
+ }
+ ok(CloseHandle(thread)!=0,"CloseHandle failed");
+}
+
+/* Check that SuspendThread and ResumeThread work */
+VOID test_SuspendThread(DWORD version)
+{
+ HANDLE thread,access_thread;
+ DWORD threadId,exitCode;
+ int i,error;
+
+ thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunc3,NULL,
+ 0,&threadId);
+ ok(thread!=(HANDLE)NULL,"Create Thread failed.");
+/* Check that the thread is suspended */
+/* Note that this is a polling method, and there is a race between
+ SuspendThread being called (in the child, and the loop below timing out,
+ so the test could fail on a heavily loaded or slow computer.
+*/
+ error=0;
+ for(i=0;error==0 && i<100;i++) {
+ error=SuspendThread(thread);
+ ResumeThread(thread);
+ if(error==0) {
+ Sleep(50);
+ i++;
+ }
+ }
+ ok(error==1,"SuspendThread did not work");
+/* check that access restrictions are obeyed */
+ if(WIN2K_PLUS(version)) {
+ access_thread=OpenThreadPtr(THREAD_ALL_ACCESS & (~THREAD_SUSPEND_RESUME),
+ 0,threadId);
+ todo_wine {
+ ok(access_thread!=(HANDLE)NULL,"OpenThread returned an invalid handle");
+ if(access_thread!=(HANDLE)NULL) {
+ ok(SuspendThread(access_thread)==-1,
+ "SuspendThread did not obey access restrictions");
+ ok(ResumeThread(access_thread)==-1,
+ "ResumeThread did not obey access restrictions");
+ ok(CloseHandle(access_thread)!=0,"CloseHandle Failed");
+ }
+ }
+ }
+/* Double check that the thread really is suspended */
+ ok((error=GetExitCodeThread(thread,&exitCode))!=0 && exitCode==STILL_ACTIVE,
+ "Thread did not really suspend");
+/* Resume the thread, and make sure it actually completes */
+ ok(ResumeThread(thread)==1,"Resume thread returned an invalid value");
+ ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0,
+ "Thread did not resume");
+ if(error!=WAIT_OBJECT_0) {
+ TerminateThread(thread,1);
+ }
+ ok(CloseHandle(thread)!=0,"CloseHandle Failed");
+}
+
+/* Check that TerminateThread works properly
+ NOTE: in my wine version (As of March 30, 2002), this code leaves a wine
+ process after the test completes. You can use SKIP_TERMINATE to disable
+ the testing, and not leave the wine process around.
+*/
+VOID test_TerminateThread(DWORD version)
+{
+ HANDLE thread,access_thread;
+ DWORD threadId,exitCode;
+ int i,error;
+ char msg[80];
+ i=0; error=0;
+ thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunc4,NULL,
+ 0,&threadId);
+ ok(thread!=(HANDLE)NULL,"Create Thread failed.");
+/* check that access restrictions are obeyed */
+ if(WIN2K_PLUS(version)) {
+ access_thread=OpenThreadPtr(THREAD_ALL_ACCESS & (~THREAD_TERMINATE),
+ 0,threadId);
+ todo_wine {
+ ok(access_thread!=(HANDLE)NULL,"OpenThread returned an invalid handle");
+ if(access_thread!=(HANDLE)NULL) {
+ ok(TerminateThread(access_thread,99)==0,
+ "TerminateThread did not obey access restrictions");
+ ok(CloseHandle(access_thread)!=0,"CloseHandle Failed");
+ }
+ }
+ }
+/* terminate a job and make sure it terminates */
+ ok(TerminateThread(thread,99)!=0,"TerminateThread failed");
+ ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
+ "TerminateThread didn't work");
+ ok(GetExitCodeThread(thread,&exitCode)!=STILL_ACTIVE,
+ "TerminateThread should not leave the thread 'STILL_ACTIVE'");
+ if(WIN2K_PLUS(version)) {
+/*NOTE: In Win2k, GetExitCodeThread does not return the value specified by
+ TerminateThread, even though MSDN says it should. So currently
+ there is no check being done for this.
+*/
+ sprintf(msg,"TerminateThread returned: 0x%lx instead of 0x%x\n",exitCode,99);
+ trace(msg);
+ } else {
+ ok(exitCode==99, "TerminateThread returned invalid exit code");
+ }
+ ok(CloseHandle(thread)!=0,"Error Closing thread handle");
+}
+
+/* Check if CreateThread obeys the specified stack size. This code does
+ not work properly, and is currently disabled
+*/
+VOID test_CreateThread_stack(DWORD version)
+{
+#if CHECK_STACK
+/* The only way I know of to test the stack size is to use alloca
+ and __try/__except. However, this is probably not portable,
+ and I couldn't get it to work under Wine anyhow. However, here
+ is the code which should allow for testing that CreateThread
+ respects the stack-size limit
+*/
+ HANDLE thread;
+ DWORD threadId,exitCode;
+
+ SYSTEM_INFO sysInfo;
+ sysInfo.dwPageSize=0;
+ GetSystemInfo(&sysInfo);
+ ok(sysInfo.dwPageSize>0,"GetSystemInfo should return a valid page size");
+ thread = CreateThread(NULL,sysInfo.dwPageSize,
+ (LPTHREAD_START_ROUTINE)threadFunc5,&exitCode,
+ 0,&threadId);
+ ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
+ "TerminateThread didn't work");
+ ok(exitCode==1,"CreateThread did not obey stack-size-limit");
+ ok(CloseHandle(thread)!=0,"CloseHandle failed");
+#endif
+}
+
+/* Check whether setting/retreiving thread priorities works */
+VOID test_thread_priority(DWORD version)
+{
+ HANDLE curthread,access_thread;
+ DWORD curthreadId,exitCode;
+ int min_priority=-2,max_priority=2;
+ int i,error;
+ char msg1[80],msg2[80];
+
+ curthread=GetCurrentThread();
+ curthreadId=GetCurrentThreadId();
+/* Check thread priority */
+/* NOTE: on Win2k/XP priority can be from -7 to 6. All other platforms it
+ is -2 to 2. However, even on a real Win2k system, using thread
+ priorities beyond the -2 to 2 range does not work. If you want to try
+ anyway, enable USE_EXTENDED_PRIORITIES
+*/
+ ok(GetThreadPriority(curthread)==THREAD_PRIORITY_NORMAL,
+ "GetThreadPriority Failed");
+
+ if(WIN2K_PLUS(version)) {
+/* check that access control is obeyed */
+ access_thread=OpenThreadPtr(THREAD_ALL_ACCESS &
+ (~THREAD_QUERY_INFORMATION) & (~THREAD_SET_INFORMATION),
+ 0,curthreadId);
+ todo_wine {
+ ok(access_thread!=(HANDLE)NULL,"OpenThread returned an invalid handle");
+ if(access_thread!=(HANDLE)NULL) {
+ ok(SetThreadPriority(access_thread,1)==0,
+ "SetThreadPriority did not obey access restrictions");
+ ok(GetThreadPriority(access_thread)==THREAD_PRIORITY_ERROR_RETURN,
+ "GetThreadPriority did not obey access restrictions");
+ ok(SetThreadPriorityBoost(access_thread,1)==0,
+ "SetThreadPriorityBoost did not obey access restrictions");
+ ok(GetThreadPriorityBoost(access_thread,&error)==0,
+ "GetThreadPriorityBoost did not obey access restrictions");
+ ok(GetExitCodeThread(access_thread,&exitCode)==0,
+ "GetExitCodeThread did not obey access restrictions");
+ ok(CloseHandle(access_thread),"Error Closing thread handle");
+ }
+ }
+#if USE_EXTENDED_PRIORITIES
+ min_priority=-7; max_priority=6;
+#endif
+ }
+ for(i=min_priority;i<=max_priority;i++) {
+ sprintf(msg1,"SetThreadPriority Failed for priority: %d",i);
+ sprintf(msg2,"GetThreadPriority Failed for priority: %d",i);
+ ok(SetThreadPriority(curthread,i)!=0,msg1);
+ ok(GetThreadPriority(curthread)==i,msg2);
+ }
+ ok(SetThreadPriority(curthread,THREAD_PRIORITY_TIME_CRITICAL)!=0,
+ "SetThreadPriority Failed");
+ ok(GetThreadPriority(curthread)==THREAD_PRIORITY_TIME_CRITICAL,
+ "GetThreadPriority Failed");
+ ok(SetThreadPriority(curthread,THREAD_PRIORITY_IDLE)!=0,
+ "SetThreadPriority Failed");
+ ok(GetThreadPriority(curthread)==THREAD_PRIORITY_IDLE,
+ "GetThreadPriority Failed");
+ ok(SetThreadPriority(curthread,0)!=0,"SetThreadPriority Failed");
+
+/* Check thread priority boost */
+/* NOTE: This only works on WinNT/2000/XP) */
+ if(version < 0x80000000) {
+ todo_wine {
+ ok(SetThreadPriorityBoost(curthread,1)!=0,
+ "SetThreadPriorityBoost Failed");
+ ok(GetThreadPriorityBoost(curthread,&error)!=0 && error==1,
+ "GetThreadPriorityBoost Failed");
+ ok(SetThreadPriorityBoost(curthread,0)!=0,
+ "SetThreadPriorityBoost Failed");
+ ok(GetThreadPriorityBoost(curthread,&error)!=0 && error==0,
+ "GetThreadPriorityBoost Failed");
+ }
+ }
+}
+
+/* check the GetThreadTimes function */
+VOID test_GetThreadTimes(DWORD version)
+{
+ HANDLE thread,access_thread=(HANDLE)NULL;
+ FILETIME creationTime,exitTime,kernelTime,userTime;
+ DWORD threadId;
+ int error;
+
+ thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunc2,NULL,
+ CREATE_SUSPENDED,&threadId);
+
+ ok(thread!=(HANDLE)NULL,"Create Thread failed.");
+/* check that access control is obeyed */
+ if(WIN2K_PLUS(version)) {
+ access_thread=OpenThreadPtr(THREAD_ALL_ACCESS &
+ (~THREAD_QUERY_INFORMATION), 0,threadId);
+ todo_wine {
+ ok(access_thread!=(HANDLE)NULL,
+ "OpenThread returned an invalid handle");
+ }
+ }
+ ok(ResumeThread(thread)==1,"Resume thread returned an invalid value");
+ ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
+ "ResumeThread didn't work");
+ if(WIN2K_PLUS(version)) {
+ if(access_thread!=(HANDLE)NULL) {
+ error=GetThreadTimes(access_thread,&creationTime,&exitTime,
+ &kernelTime,&userTime);
+ ok(error==0, "GetThreadTimes did not obey access restrictions");
+ ok(CloseHandle(access_thread)!=0,"CloseHandle Failed");
+ }
+ }
+ creationTime.dwLowDateTime=99; creationTime.dwHighDateTime=99;
+ exitTime.dwLowDateTime=99; exitTime.dwHighDateTime=99;
+ kernelTime.dwLowDateTime=99; kernelTime.dwHighDateTime=99;
+ userTime.dwLowDateTime=99; userTime.dwHighDateTime=99;
+/* GetThreadTimes should set all of the parameters passed to it */
+ todo_wine {
+ error=GetThreadTimes(thread,&creationTime,&exitTime,
+ &kernelTime,&userTime);
+ ok(error!=0,"GetThreadTimes failed");
+ ok(creationTime.dwLowDateTime!=99 || creationTime.dwHighDateTime!=99,
+ "creationTime was invalid");
+ ok(exitTime.dwLowDateTime!=99 || exitTime.dwHighDateTime!=99,
+ "exitTime was invalid");
+ ok(kernelTime.dwLowDateTime!=99 || kernelTime.dwHighDateTime!=99,
+ "kernelTime was invalid");
+ ok(userTime.dwLowDateTime!=99 || userTime.dwHighDateTime!=99,
+ "userTime was invalid");
+ }
+ ok(CloseHandle(thread)!=0,"ClosewHandle failed");
+}
+
+/* Check the processor affinity functions */
+/* NOTE: These functions should also be checked that they obey access control
+*/
+VOID test_thread_processor(DWORD version)
+{
+ HANDLE curthread,curproc;
+ DWORD processMask,systemMask;
+ SYSTEM_INFO sysInfo;
+ int error=0;
+
+ sysInfo.dwNumberOfProcessors=0;
+ GetSystemInfo(&sysInfo);
+ ok(sysInfo.dwNumberOfProcessors>0,
+ "GetSystemInfo failed to return a valid # of processors");
+/* Use the current Thread/process for all tests */
+ curthread=GetCurrentThread();
+ ok(curthread!=(HANDLE)NULL,"GetCurrentThread failed");
+ curproc=GetCurrentProcess();
+ ok(curproc!=(HANDLE)NULL,"GetCurrentProcess failed");
+/* Check the Affinity Mask functions */
+ ok(GetProcessAffinityMask(curproc,&processMask,&systemMask)!=0,
+ "GetProcessAffinityMask failed");
+ ok(SetThreadAffinityMask(curthread,processMask)==1,
+ "SetThreadAffinityMask failed");
+ ok(SetThreadAffinityMask(curthread,processMask+1)==0,
+ "SetThreadAffinityMask passed for an illegal processor");
+/* NOTE: This only works on WinNT/2000/XP) */
+ if(version < 0x80000000) {
+ todo_wine {
+ error=SetThreadIdealProcessor(curthread,0);
+ ok(error!=-1, "SetThreadIdealProcessor failed");
+ error=SetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1);
+ }
+ ok(error==-1,
+ "SetThreadIdealProccesor succeded with an illegal processor #");
+ todo_wine {
+ error=SetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS);
+ ok(error==0, "SetThreadIdealProccesor returned an incorrect value");
+ }
+ }
+}
+
+START_TEST(thread)
+{
+ DWORD version;
+ HINSTANCE lib;
+ version=GetVersion();
+/* Neither Cygwin nor mingW export OpenThread, so do a dynamic check
+ so that the compile passes
+*/
+ lib=LoadLibraryA("kernel32");
+ ok(lib!=(HANDLE)NULL,"Couldn't load kernel32.dll");
+ OpenThreadPtr=(OPENTHREADPTR)GetProcAddress(lib,"OpenThread");
+ if(OpenThreadPtr==NULL) {
+ OpenThreadPtr=&OpenThreadDefault;
+ }
+ test_CreateThread_basic(version);
+ test_CreateThread_suspended(version);
+ test_SuspendThread(version);
+#if ! SKIP_TERMINATE
+ test_TerminateThread(version);
+#endif
+ test_CreateThread_stack(version);
+ test_thread_priority(version);
+ test_GetThreadTimes(version);
+ test_thread_processor(version);
+}
diff --git a/include/winbase.h b/include/winbase.h
index 038ee1e..559a377 100644
--- a/include/winbase.h
+++ b/include/winbase.h
@@ -1255,6 +1255,7 @@
BOOL WINAPI GetNumberOfEventLogRecords(HANDLE,PDWORD);
BOOL WINAPI GetOldestEventLogRecord(HANDLE,PDWORD);
DWORD WINAPI GetPriorityClass(HANDLE);
+BOOL WINAPI GetProcessAffinityMask(HANDLE,PDWORD,PDWORD);
BOOL WINAPI GetProcessTimes(HANDLE,LPFILETIME,LPFILETIME,LPFILETIME,LPFILETIME);
DWORD WINAPI GetProcessVersion(DWORD);
BOOL WINAPI GetSecurityDescriptorControl(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR_CONTROL,LPDWORD);
@@ -1416,6 +1417,7 @@
DWORD WINAPI SetThreadAffinityMask(HANDLE,DWORD);
BOOL WINAPI SetThreadContext(HANDLE,const CONTEXT *);
DWORD WINAPI SetThreadExecutionState(EXECUTION_STATE);
+DWORD WINAPI SetThreadIdealProcessor(HANDLE,DWORD);
BOOL WINAPI SetThreadPriority(HANDLE,INT);
BOOL WINAPI SetThreadPriorityBoost(HANDLE,BOOL);
BOOL WINAPI SetThreadToken(PHANDLE,HANDLE);
diff --git a/include/winnt.h b/include/winnt.h
index 76976e8..d39cce1 100644
--- a/include/winnt.h
+++ b/include/winnt.h
@@ -460,6 +460,7 @@
#define PROCESSOR_ARM920 2336 /* 0x920 */
#define PROCESSOR_ARM_7TDMI 70001
+#define MAXIMUM_PROCESSORS 32
typedef struct _MEMORY_BASIC_INFORMATION
{
LPVOID BaseAddress;
diff --git a/scheduler/thread.c b/scheduler/thread.c
index 3df0807..a8f1407 100644
--- a/scheduler/thread.c
+++ b/scheduler/thread.c
@@ -549,6 +549,21 @@
return ret;
}
+/**********************************************************************
+ * SetThreadIdealProcessor [KERNEL32.@] Obtains timing information.
+ *
+ * RETURNS
+ * Success: Value of last call to SetThreadIdealProcessor
+ * Failure: -1
+ */
+DWORD WINAPI SetThreadIdealProcessor(
+ HANDLE hThread, /* [in] Specifies the thread of interest */
+ DWORD dwIdealProcessor) /* [in] Specifies the new preferred processor */
+{
+ FIXME("(0x%08x): stub\n",hThread);
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return -1L;
+}
/**********************************************************************
* TerminateThread [KERNEL32.@] Terminates a thread