Add a test for GetThreadExitCode.
[wine] / dlls / kernel / tests / thread.c
1 /*
2  * Unit test suite for directory functions.
3  *
4  * Copyright 2002 Geoffrey Hausheer
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 /* Define _WIN32_WINNT to get SetThreadIdealProcessor on Windows */
22 #define _WIN32_WINNT 0x0500
23
24 #include <stdarg.h>
25
26 #include "wine/test.h"
27 #include <windef.h>
28 #include <winbase.h>
29 #include <winnt.h>
30 #include <winerror.h>
31
32 /* Specify the number of simultaneous threads to test */
33 #define NUM_THREADS 4
34 /* Specify whether to test the extended priorities for Win2k/XP */
35 #define USE_EXTENDED_PRIORITIES 0
36 /* Specify whether to test the stack allocation in CreateThread */
37 #define CHECK_STACK 0
38
39 /* Set CHECK_STACK to 1 if you want to try to test the stack-limit from
40    CreateThread.  So far I have been unable to make this work, and
41    I am in doubt as to how portable it is.  Also, according to MSDN,
42    you shouldn't mix C-run-time-libraries (i.e. alloca) with CreateThread.
43    Anyhow, the check is currently commented out
44 */
45 #if CHECK_STACK
46 # ifdef __try
47 #  define __TRY __try
48 #  define __EXCEPT __except
49 #  define __ENDTRY
50 # else
51 #  include "wine/exception.h"
52 # endif
53 #endif
54
55 typedef BOOL (WINAPI *GetThreadPriorityBoost_t)(HANDLE,PBOOL);
56 static GetThreadPriorityBoost_t pGetThreadPriorityBoost=NULL;
57
58 typedef HANDLE (WINAPI *OpenThread_t)(DWORD,BOOL,DWORD);
59 static OpenThread_t pOpenThread=NULL;
60
61 typedef DWORD (WINAPI *SetThreadIdealProcessor_t)(HANDLE,DWORD);
62 static SetThreadIdealProcessor_t pSetThreadIdealProcessor=NULL;
63
64 typedef BOOL (WINAPI *SetThreadPriorityBoost_t)(HANDLE,BOOL);
65 static SetThreadPriorityBoost_t pSetThreadPriorityBoost=NULL;
66
67 /* Functions not tested yet:
68   AttachThreadInput
69   CreateRemoteThread
70   SetThreadContext
71   SwitchToThread
72
73 In addition there are no checks that the inheritance works properly in
74 CreateThread
75 */
76
77 DWORD tlsIndex;
78
79 typedef struct {
80   int threadnum;
81   HANDLE *event;
82   DWORD *threadmem;
83 } t1Struct;
84
85 /* WinME supports OpenThread but doesn't know about access restrictions so
86    we require them to be either completely ignored or always obeyed.
87 */
88 INT obeying_ars = 0; /* -1 == no, 0 == dunno yet, 1 == yes */
89 #define obey_ar(x) \
90   (obeying_ars == 0 \
91     ? ((x) \
92       ? (obeying_ars = +1) \
93       : ((obeying_ars = -1), \
94          trace("not restricted, assuming consistent behaviour\n"))) \
95     : (obeying_ars < 0) \
96       ? ok(!(x), "access restrictions obeyed\n") \
97       : ok( (x), "access restrictions not obeyed\n"))
98
99 /* Basic test that simultaneous threads can access shared memory,
100    that the thread local storage routines work correctly, and that
101    threads actually run concurrently
102 */
103 static DWORD WINAPI threadFunc1(LPVOID p)
104 {
105     t1Struct *tstruct = (t1Struct *)p;
106    int i;
107 /* write our thread # into shared memory */
108    tstruct->threadmem[tstruct->threadnum]=GetCurrentThreadId();
109    ok(TlsSetValue(tlsIndex,(LPVOID)(tstruct->threadnum+1))!=0,
110       "TlsSetValue failed\n");
111 /* The threads synchronize before terminating.  This is done by
112    Signaling an event, and waiting for all events to occur
113 */
114    SetEvent(tstruct->event[tstruct->threadnum]);
115    WaitForMultipleObjects(NUM_THREADS,tstruct->event,TRUE,INFINITE);
116 /* Double check that all threads really did run by validating that
117    they have all written to the shared memory. There should be no race
118    here, since all threads were synchronized after the write.*/
119    for(i=0;i<NUM_THREADS;i++) {
120      while(tstruct->threadmem[i]==0) ;
121    }
122 /* Check that noone changed our tls memory */
123    ok((int)TlsGetValue(tlsIndex)-1==tstruct->threadnum,
124       "TlsGetValue failed\n");
125    return NUM_THREADS+tstruct->threadnum;
126 }
127
128 static DWORD WINAPI threadFunc2(LPVOID p)
129 {
130    return 99;
131 }
132
133 static DWORD WINAPI threadFunc3(LPVOID p)
134 {
135    HANDLE thread;
136    thread=GetCurrentThread();
137    SuspendThread(thread);
138    return 99;
139 }
140
141 static DWORD WINAPI threadFunc4(LPVOID p)
142 {
143     HANDLE event = (HANDLE)p;
144    if(event != NULL) {
145      SetEvent(event);
146    }
147    Sleep(99000);
148    return 0;
149 }
150
151 #if CHECK_STACK
152 static DWORD WINAPI threadFunc5(LPVOID p)
153 {
154   DWORD *exitCode = (DWORD *)p;
155   SYSTEM_INFO sysInfo;
156   sysInfo.dwPageSize=0;
157   GetSystemInfo(&sysInfo);
158   *exitCode=0;
159    __TRY
160    {
161      alloca(2*sysInfo.dwPageSize);
162    }
163     __EXCEPT(1) {
164      *exitCode=1;
165    }
166    __ENDTRY
167    return 0;
168 }
169 #endif
170
171 /* Check basic funcationality of CreateThread and Tls* functions */
172 static VOID test_CreateThread_basic(void)
173 {
174    HANDLE thread[NUM_THREADS],event[NUM_THREADS];
175    DWORD threadid[NUM_THREADS],curthreadId;
176    DWORD threadmem[NUM_THREADS];
177    DWORD exitCode;
178    t1Struct tstruct[NUM_THREADS];
179    int error;
180    DWORD i,j;
181 /* Retrieve current Thread ID for later comparisons */
182   curthreadId=GetCurrentThreadId();
183 /* Allocate some local storage */
184   ok((tlsIndex=TlsAlloc())!=TLS_OUT_OF_INDEXES,"TlsAlloc failed\n");
185 /* Create events for thread synchronization */
186   for(i=0;i<NUM_THREADS;i++) {
187     threadmem[i]=0;
188 /* Note that it doesn't matter what type of event we chose here.  This
189    test isn't trying to thoroughly test events
190 */
191     event[i]=CreateEventA(NULL,TRUE,FALSE,NULL);
192     tstruct[i].threadnum=i;
193     tstruct[i].threadmem=threadmem;
194     tstruct[i].event=event;
195   }
196
197 /* Test that passing arguments to threads works okay */
198   for(i=0;i<NUM_THREADS;i++) {
199     thread[i] = CreateThread(NULL,0,threadFunc1,
200                              &tstruct[i],0,&threadid[i]);
201     ok(thread[i]!=NULL,"Create Thread failed\n");
202   }
203 /* Test that the threads actually complete */
204   for(i=0;i<NUM_THREADS;i++) {
205     error=WaitForSingleObject(thread[i],5000);
206     ok(error==WAIT_OBJECT_0, "Thread did not complete within timelimit\n");
207     if(error!=WAIT_OBJECT_0) {
208       TerminateThread(thread[i],i+NUM_THREADS);
209     }
210     ok(GetExitCodeThread(thread[i],&exitCode),"Could not retrieve ext code\n");
211     ok(exitCode==i+NUM_THREADS,"Thread returned an incorrect exit code\n");
212   }
213 /* Test that each thread executed in its parent's address space
214    (it was able to change threadmem and pass that change back to its parent)
215    and that each thread id was independant).  Note that we prove that the
216    threads actually execute concurrently by having them block on each other
217    in threadFunc1
218 */
219   for(i=0;i<NUM_THREADS;i++) {
220     error=0;
221     for(j=i+1;j<NUM_THREADS;j++) {
222       if (threadmem[i]==threadmem[j]) {
223         error=1;
224       }
225     }
226     ok(!error && threadmem[i]==threadid[i] && threadmem[i]!=curthreadId,
227          "Thread did not execute successfully\n");
228     ok(CloseHandle(thread[i])!=0,"CloseHandle failed\n");
229   }
230   ok(TlsFree(tlsIndex)!=0,"TlsFree failed\n");
231 }
232
233 /* Check that using the CREATE_SUSPENDED flag works */
234 static VOID test_CreateThread_suspended(void)
235 {
236   HANDLE thread;
237   DWORD threadId;
238   int error;
239
240   thread = CreateThread(NULL,0,threadFunc2,NULL,
241                         CREATE_SUSPENDED,&threadId);
242   ok(thread!=NULL,"Create Thread failed\n");
243 /* Check that the thread is suspended */
244   ok(SuspendThread(thread)==1,"Thread did not start suspended\n");
245   ok(ResumeThread(thread)==2,"Resume thread returned an invalid value\n");
246 /* Check that resume thread didn't actually start the thread.  I can't think
247    of a better way of checking this than just waiting.  I am not sure if this
248    will work on slow computers.
249 */
250   ok(WaitForSingleObject(thread,1000)==WAIT_TIMEOUT,
251      "ResumeThread should not have actually started the thread\n");
252 /* Now actually resume the thread and make sure that it actually completes*/
253   ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
254   ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0,
255      "Thread did not resume\n");
256   if(error!=WAIT_OBJECT_0) {
257     TerminateThread(thread,1);
258   }
259   ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
260 }
261
262 /* Check that SuspendThread and ResumeThread work */
263 static VOID test_SuspendThread(void)
264 {
265   HANDLE thread,access_thread;
266   DWORD threadId,exitCode,error;
267   int i;
268
269   thread = CreateThread(NULL,0,threadFunc3,NULL,
270                         0,&threadId);
271   ok(thread!=NULL,"Create Thread failed\n");
272 /* Check that the thread is suspended */
273 /* Note that this is a polling method, and there is a race between
274    SuspendThread being called (in the child, and the loop below timing out,
275    so the test could fail on a heavily loaded or slow computer.
276 */
277   error=0;
278   for(i=0;error==0 && i<100;i++) {
279     error=SuspendThread(thread);
280     ResumeThread(thread);
281     if(error==0) {
282       Sleep(50);
283       i++;
284     }
285   }
286   ok(error==1,"SuspendThread did not work\n");
287 /* check that access restrictions are obeyed */
288   if (pOpenThread) {
289     access_thread=pOpenThread(THREAD_ALL_ACCESS & (~THREAD_SUSPEND_RESUME),
290                            0,threadId);
291     ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
292     if (access_thread!=NULL) {
293       obey_ar(SuspendThread(access_thread)==~0U);
294       obey_ar(ResumeThread(access_thread)==~0U);
295       ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
296     }
297   }
298 /* Double check that the thread really is suspended */
299   ok((error=GetExitCodeThread(thread,&exitCode))!=0 && exitCode==STILL_ACTIVE,
300      "Thread did not really suspend\n");
301 /* Resume the thread, and make sure it actually completes */
302   ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
303   ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0,
304      "Thread did not resume\n");
305   if(error!=WAIT_OBJECT_0) {
306     TerminateThread(thread,1);
307   }
308   /* Trying to suspend a terminated thread should fail */
309   error=SuspendThread(thread);
310   ok(error==~0U, "wrong return code: %ld\n", error);
311   ok(GetLastError()==ERROR_ACCESS_DENIED || GetLastError()==ERROR_NO_MORE_ITEMS, "unexpected error code: %ld\n", GetLastError());
312
313   ok(CloseHandle(thread)!=0,"CloseHandle Failed\n");
314 }
315
316 /* Check that TerminateThread works properly
317 */
318 static VOID test_TerminateThread(void)
319 {
320   HANDLE thread,access_thread,event;
321   DWORD threadId,exitCode;
322   event=CreateEventA(NULL,TRUE,FALSE,NULL);
323   thread = CreateThread(NULL,0,threadFunc4,
324                         (LPVOID)event, 0,&threadId);
325   ok(thread!=NULL,"Create Thread failed\n");
326 /* TerminateThread has a race condition in Wine.  If the thread is terminated
327    before it starts, it leaves a process behind.  Therefore, we wait for the
328    thread to signal that it has started.  There is no easy way to force the
329    race to occur, so we don't try to find it.
330 */
331   ok(WaitForSingleObject(event,5000)==WAIT_OBJECT_0,
332      "TerminateThread didn't work\n");
333 /* check that access restrictions are obeyed */
334   if (pOpenThread) {
335     access_thread=pOpenThread(THREAD_ALL_ACCESS & (~THREAD_TERMINATE),
336                              0,threadId);
337     ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
338     if (access_thread!=NULL) {
339       obey_ar(TerminateThread(access_thread,99)==0);
340       ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
341     }
342   }
343 /* terminate a job and make sure it terminates */
344   ok(TerminateThread(thread,99)!=0,"TerminateThread failed\n");
345   ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
346      "TerminateThread didn't work\n");
347   ok(GetExitCodeThread(thread,&exitCode)!=STILL_ACTIVE,
348      "TerminateThread should not leave the thread 'STILL_ACTIVE'\n");
349   ok(exitCode==99, "TerminateThread returned invalid exit code\n");
350   ok(CloseHandle(thread)!=0,"Error Closing thread handle\n");
351 }
352
353 /* Check if CreateThread obeys the specified stack size.  This code does
354    not work properly, and is currently disabled
355 */
356 static VOID test_CreateThread_stack(void)
357 {
358 #if CHECK_STACK
359 /* The only way I know of to test the stack size is to use alloca
360    and __try/__except.  However, this is probably not portable,
361    and I couldn't get it to work under Wine anyhow.  However, here
362    is the code which should allow for testing that CreateThread
363    respects the stack-size limit
364 */
365      HANDLE thread;
366      DWORD threadId,exitCode;
367
368      SYSTEM_INFO sysInfo;
369      sysInfo.dwPageSize=0;
370      GetSystemInfo(&sysInfo);
371      ok(sysInfo.dwPageSize>0,"GetSystemInfo should return a valid page size\n");
372      thread = CreateThread(NULL,sysInfo.dwPageSize,
373                            threadFunc5,&exitCode,
374                            0,&threadId);
375      ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
376         "TerminateThread didn't work\n");
377      ok(exitCode==1,"CreateThread did not obey stack-size-limit\n");
378      ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
379 #endif
380 }
381
382 /* Check whether setting/retrieving thread priorities works */
383 static VOID test_thread_priority(void)
384 {
385    HANDLE curthread,access_thread;
386    DWORD curthreadId,exitCode;
387    int min_priority=-2,max_priority=2;
388    BOOL disabled,rc;
389    int i;
390
391    curthread=GetCurrentThread();
392    curthreadId=GetCurrentThreadId();
393 /* Check thread priority */
394 /* NOTE: on Win2k/XP priority can be from -7 to 6.  All other platforms it
395          is -2 to 2.  However, even on a real Win2k system, using thread
396          priorities beyond the -2 to 2 range does not work.  If you want to try
397          anyway, enable USE_EXTENDED_PRIORITIES
398 */
399    ok(GetThreadPriority(curthread)==THREAD_PRIORITY_NORMAL,
400       "GetThreadPriority Failed\n");
401
402    if (pOpenThread) {
403 /* check that access control is obeyed */
404      access_thread=pOpenThread(THREAD_ALL_ACCESS &
405                        (~THREAD_QUERY_INFORMATION) & (~THREAD_SET_INFORMATION),
406                        0,curthreadId);
407      ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
408      if (access_thread!=NULL) {
409        obey_ar(SetThreadPriority(access_thread,1)==0);
410        obey_ar(GetThreadPriority(access_thread)==THREAD_PRIORITY_ERROR_RETURN);
411        obey_ar(GetExitCodeThread(access_thread,&exitCode)==0);
412        ok(CloseHandle(access_thread),"Error Closing thread handle\n");
413      }
414 #if USE_EXTENDED_PRIORITIES
415      min_priority=-7; max_priority=6;
416 #endif
417    }
418    for(i=min_priority;i<=max_priority;i++) {
419      ok(SetThreadPriority(curthread,i)!=0,
420         "SetThreadPriority Failed for priority: %d\n",i);
421      ok(GetThreadPriority(curthread)==i,
422         "GetThreadPriority Failed for priority: %d\n",i);
423    }
424    ok(SetThreadPriority(curthread,THREAD_PRIORITY_TIME_CRITICAL)!=0,
425       "SetThreadPriority Failed\n");
426    ok(GetThreadPriority(curthread)==THREAD_PRIORITY_TIME_CRITICAL,
427       "GetThreadPriority Failed\n");
428    ok(SetThreadPriority(curthread,THREAD_PRIORITY_IDLE)!=0,
429        "SetThreadPriority Failed\n");
430    ok(GetThreadPriority(curthread)==THREAD_PRIORITY_IDLE,
431        "GetThreadPriority Failed\n");
432    ok(SetThreadPriority(curthread,0)!=0,"SetThreadPriority Failed\n");
433
434 /* Check thread priority boost */
435    if (!pGetThreadPriorityBoost || !pSetThreadPriorityBoost) 
436      return; /* Win9x */
437
438    SetLastError(0xdeadbeef);
439    rc=pGetThreadPriorityBoost(curthread,&disabled);
440    if (rc==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
441      return; /* WinME */
442
443 /* check that access control is obeyed */
444    access_thread=pOpenThread(THREAD_ALL_ACCESS &
445                      (~THREAD_QUERY_INFORMATION) & (~THREAD_SET_INFORMATION),
446                      0,curthreadId);
447    ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
448    if (access_thread!=NULL) {
449      obey_ar(pSetThreadPriorityBoost(access_thread,1)==0);
450      obey_ar(pGetThreadPriorityBoost(access_thread,&disabled)==0);
451      ok(CloseHandle(access_thread),"Error Closing thread handle\n");
452    }
453
454    todo_wine {
455      ok(rc!=0,"error=%ld\n",GetLastError());
456
457      rc = pSetThreadPriorityBoost(curthread,1);
458      ok( rc != 0, "error=%ld\n",GetLastError());
459      rc=pGetThreadPriorityBoost(curthread,&disabled);
460      ok(rc!=0 && disabled==1,
461         "rc=%d error=%ld disabled=%d\n",rc,GetLastError(),disabled);
462
463      rc = pSetThreadPriorityBoost(curthread,0);
464      ok( rc != 0, "error=%ld\n",GetLastError());
465      rc=pGetThreadPriorityBoost(curthread,&disabled);
466      ok(rc!=0 && disabled==0,
467         "rc=%d error=%ld disabled=%d\n",rc,GetLastError(),disabled);
468    }
469 }
470
471 /* check the GetThreadTimes function */
472 static VOID test_GetThreadTimes(void)
473 {
474      HANDLE thread,access_thread=NULL;
475      FILETIME creationTime,exitTime,kernelTime,userTime;
476      DWORD threadId;
477      int error;
478
479      thread = CreateThread(NULL,0,threadFunc2,NULL,
480                            CREATE_SUSPENDED,&threadId);
481
482      ok(thread!=NULL,"Create Thread failed\n");
483 /* check that access control is obeyed */
484      if (pOpenThread) {
485        access_thread=pOpenThread(THREAD_ALL_ACCESS &
486                                    (~THREAD_QUERY_INFORMATION), 0,threadId);
487        ok(access_thread!=NULL,
488           "OpenThread returned an invalid handle\n");
489      }
490      ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
491      ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
492         "ResumeThread didn't work\n");
493      creationTime.dwLowDateTime=99; creationTime.dwHighDateTime=99;
494      exitTime.dwLowDateTime=99;     exitTime.dwHighDateTime=99;
495      kernelTime.dwLowDateTime=99;   kernelTime.dwHighDateTime=99;
496      userTime.dwLowDateTime=99;     userTime.dwHighDateTime=99;
497 /* GetThreadTimes should set all of the parameters passed to it */
498      error=GetThreadTimes(thread,&creationTime,&exitTime,
499                           &kernelTime,&userTime);
500      if (error!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
501        ok(error!=0,"GetThreadTimes failed\n");
502        ok(creationTime.dwLowDateTime!=99 || creationTime.dwHighDateTime!=99,
503           "creationTime was invalid\n");
504        ok(exitTime.dwLowDateTime!=99 || exitTime.dwHighDateTime!=99,
505           "exitTime was invalid\n");
506        ok(kernelTime.dwLowDateTime!=99 || kernelTime.dwHighDateTime!=99,
507           "kernelTimewas invalid\n");
508        ok(userTime.dwLowDateTime!=99 || userTime.dwHighDateTime!=99,
509           "userTime was invalid\n");
510        ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
511        if(access_thread!=NULL)
512        {
513          error=GetThreadTimes(access_thread,&creationTime,&exitTime,
514                               &kernelTime,&userTime);
515          obey_ar(error==0);
516        }
517      }
518      if(access_thread!=NULL) {
519        ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
520      }
521 }
522
523 /* Check the processor affinity functions */
524 /* NOTE: These functions should also be checked that they obey access control
525 */
526 static VOID test_thread_processor(void)
527 {
528    HANDLE curthread,curproc;
529    DWORD processMask,systemMask;
530    SYSTEM_INFO sysInfo;
531    int error=0;
532
533    sysInfo.dwNumberOfProcessors=0;
534    GetSystemInfo(&sysInfo);
535    ok(sysInfo.dwNumberOfProcessors>0,
536       "GetSystemInfo failed to return a valid # of processors\n");
537 /* Use the current Thread/process for all tests */
538    curthread=GetCurrentThread();
539    ok(curthread!=NULL,"GetCurrentThread failed\n");
540    curproc=GetCurrentProcess();
541    ok(curproc!=NULL,"GetCurrentProcess failed\n");
542 /* Check the Affinity Mask functions */
543    ok(GetProcessAffinityMask(curproc,&processMask,&systemMask)!=0,
544       "GetProcessAffinityMask failed\n");
545    ok(SetThreadAffinityMask(curthread,processMask)==processMask,
546       "SetThreadAffinityMask failed\n");
547    ok(SetThreadAffinityMask(curthread,processMask+1)==0,
548       "SetThreadAffinityMask passed for an illegal processor\n");
549 /* NOTE: This only works on WinNT/2000/XP) */
550    if (pSetThreadIdealProcessor) {
551      todo_wine {
552        SetLastError(0);
553        error=pSetThreadIdealProcessor(curthread,0);
554        if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
555          ok(error!=-1, "SetThreadIdealProcessor failed\n");
556        }
557      }
558      if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
559        error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1);
560        ok(error==-1,
561           "SetThreadIdealProcessor succeeded with an illegal processor #\n");
562        todo_wine {
563          error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS);
564          ok(error==0, "SetThreadIdealProcessor returned an incorrect value\n");
565        }
566      }
567    }
568 }
569
570 static VOID test_GetThreadExitCode(void)
571 {
572     DWORD exitCode, threadid;
573     DWORD GLE, ret;
574     HANDLE thread;
575
576     ret = GetExitCodeThread((HANDLE)0x2bad2bad,&exitCode);
577     ok(ret==0, "GetExitCodeThread returned non zero value: %ld\n", ret);
578     GLE = GetLastError();
579     ok(GLE==ERROR_INVALID_HANDLE, "GetLastError returned %ld (expected 6)\n", GLE);
580
581     thread = CreateThread(NULL,0,threadFunc2,NULL,0,&threadid);
582     ret = WaitForSingleObject(thread,100);
583     ok(ret==WAIT_OBJECT_0, "threadFunc2 did not exit during 100 ms\n");
584     ret = GetExitCodeThread(thread,&exitCode);
585     ok(ret==exitCode || ret==1, 
586        "GetExitCodeThread returned %ld (expected 1 or %ld)\n", ret, exitCode);
587     ok(exitCode==99, "threadFunc2 exited with code %ld (expected 99)\n", exitCode);
588     ok(CloseHandle(thread)!=0,"Error closing thread handle\n");
589 }
590
591 START_TEST(thread)
592 {
593    HINSTANCE lib;
594 /* Neither Cygwin nor mingW export OpenThread, so do a dynamic check
595    so that the compile passes
596 */
597    lib=GetModuleHandleA("kernel32.dll");
598    ok(lib!=NULL,"Couldn't get a handle for kernel32.dll\n");
599    pGetThreadPriorityBoost=(GetThreadPriorityBoost_t)GetProcAddress(lib,"GetThreadPriorityBoost");
600    pOpenThread=(OpenThread_t)GetProcAddress(lib,"OpenThread");
601    pSetThreadIdealProcessor=(SetThreadIdealProcessor_t)GetProcAddress(lib,"SetThreadIdealProcessor");
602    pSetThreadPriorityBoost=(SetThreadPriorityBoost_t)GetProcAddress(lib,"SetThreadPriorityBoost");
603    test_CreateThread_basic();
604    test_CreateThread_suspended();
605    test_SuspendThread();
606    test_TerminateThread();
607    test_CreateThread_stack();
608    test_thread_priority();
609    test_GetThreadTimes();
610    test_thread_processor();
611    test_GetThreadExitCode();
612 }