Add some extra error information.
[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    DWORD GLE, ret;
182
183 /* Retrieve current Thread ID for later comparisons */
184   curthreadId=GetCurrentThreadId();
185 /* Allocate some local storage */
186   ok((tlsIndex=TlsAlloc())!=TLS_OUT_OF_INDEXES,"TlsAlloc failed\n");
187 /* Create events for thread synchronization */
188   for(i=0;i<NUM_THREADS;i++) {
189     threadmem[i]=0;
190 /* Note that it doesn't matter what type of event we chose here.  This
191    test isn't trying to thoroughly test events
192 */
193     event[i]=CreateEventA(NULL,TRUE,FALSE,NULL);
194     tstruct[i].threadnum=i;
195     tstruct[i].threadmem=threadmem;
196     tstruct[i].event=event;
197   }
198
199 /* Test that passing arguments to threads works okay */
200   for(i=0;i<NUM_THREADS;i++) {
201     thread[i] = CreateThread(NULL,0,threadFunc1,
202                              &tstruct[i],0,&threadid[i]);
203     ok(thread[i]!=NULL,"Create Thread failed\n");
204   }
205 /* Test that the threads actually complete */
206   for(i=0;i<NUM_THREADS;i++) {
207     error=WaitForSingleObject(thread[i],5000);
208     ok(error==WAIT_OBJECT_0, "Thread did not complete within timelimit\n");
209     if(error!=WAIT_OBJECT_0) {
210       TerminateThread(thread[i],i+NUM_THREADS);
211     }
212     ok(GetExitCodeThread(thread[i],&exitCode),"Could not retrieve ext code\n");
213     ok(exitCode==i+NUM_THREADS,"Thread returned an incorrect exit code\n");
214   }
215 /* Test that each thread executed in its parent's address space
216    (it was able to change threadmem and pass that change back to its parent)
217    and that each thread id was independant).  Note that we prove that the
218    threads actually execute concurrently by having them block on each other
219    in threadFunc1
220 */
221   for(i=0;i<NUM_THREADS;i++) {
222     error=0;
223     for(j=i+1;j<NUM_THREADS;j++) {
224       if (threadmem[i]==threadmem[j]) {
225         error=1;
226       }
227     }
228     ok(!error && threadmem[i]==threadid[i] && threadmem[i]!=curthreadId,
229          "Thread did not execute successfully\n");
230     ok(CloseHandle(thread[i])!=0,"CloseHandle failed\n");
231   }
232   ok(TlsFree(tlsIndex)!=0,"TlsFree failed\n");
233
234   /* Test how passing NULL as a pointer to threadid works */
235   SetLastError(0xFACEaBAD);
236   thread[0] = CreateThread(NULL,0,threadFunc2,NULL,0,NULL);
237   GLE = GetLastError();
238   if (thread[0]) { /* NT */
239     ok(GLE==0xFACEaBAD, "CreateThread set last error to %ld, expected 4207848365\n", GLE);
240     ret = WaitForSingleObject(thread[0],100);
241     ok(ret==WAIT_OBJECT_0, "threadFunc2 did not exit during 100 ms\n");
242     ret = GetExitCodeThread(thread[0],&exitCode);
243     ok(ret!=0, "GetExitCodeThread returned %ld (expected nonzero)\n", ret);
244     ok(exitCode==99, "threadFunc2 exited with code: %ld (expected 99)\n", exitCode);
245     ok(CloseHandle(thread[0])!=0,"Error closing thread handle\n");
246   }
247   else { /* 9x */
248     ok(GLE==ERROR_INVALID_PARAMETER, "CreateThread set last error to %ld, expected 87\n", GLE);
249   }
250 }
251
252 /* Check that using the CREATE_SUSPENDED flag works */
253 static VOID test_CreateThread_suspended(void)
254 {
255   HANDLE thread;
256   DWORD threadId;
257   int error;
258
259   thread = CreateThread(NULL,0,threadFunc2,NULL,
260                         CREATE_SUSPENDED,&threadId);
261   ok(thread!=NULL,"Create Thread failed\n");
262 /* Check that the thread is suspended */
263   ok(SuspendThread(thread)==1,"Thread did not start suspended\n");
264   ok(ResumeThread(thread)==2,"Resume thread returned an invalid value\n");
265 /* Check that resume thread didn't actually start the thread.  I can't think
266    of a better way of checking this than just waiting.  I am not sure if this
267    will work on slow computers.
268 */
269   ok(WaitForSingleObject(thread,1000)==WAIT_TIMEOUT,
270      "ResumeThread should not have actually started the thread\n");
271 /* Now actually resume the thread and make sure that it actually completes*/
272   ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
273   ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0,
274      "Thread did not resume\n");
275   if(error!=WAIT_OBJECT_0) {
276     TerminateThread(thread,1);
277   }
278   ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
279 }
280
281 /* Check that SuspendThread and ResumeThread work */
282 static VOID test_SuspendThread(void)
283 {
284   HANDLE thread,access_thread;
285   DWORD threadId,exitCode,error;
286   int i;
287
288   thread = CreateThread(NULL,0,threadFunc3,NULL,
289                         0,&threadId);
290   ok(thread!=NULL,"Create Thread failed\n");
291 /* Check that the thread is suspended */
292 /* Note that this is a polling method, and there is a race between
293    SuspendThread being called (in the child, and the loop below timing out,
294    so the test could fail on a heavily loaded or slow computer.
295 */
296   error=0;
297   for(i=0;error==0 && i<100;i++) {
298     error=SuspendThread(thread);
299     ResumeThread(thread);
300     if(error==0) {
301       Sleep(50);
302       i++;
303     }
304   }
305   ok(error==1,"SuspendThread did not work\n");
306 /* check that access restrictions are obeyed */
307   if (pOpenThread) {
308     access_thread=pOpenThread(THREAD_ALL_ACCESS & (~THREAD_SUSPEND_RESUME),
309                            0,threadId);
310     ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
311     if (access_thread!=NULL) {
312       obey_ar(SuspendThread(access_thread)==~0U);
313       obey_ar(ResumeThread(access_thread)==~0U);
314       ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
315     }
316   }
317 /* Double check that the thread really is suspended */
318   ok((error=GetExitCodeThread(thread,&exitCode))!=0 && exitCode==STILL_ACTIVE,
319      "Thread did not really suspend\n");
320 /* Resume the thread, and make sure it actually completes */
321   ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
322   ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0,
323      "Thread did not resume\n");
324   if(error!=WAIT_OBJECT_0) {
325     TerminateThread(thread,1);
326   }
327   /* Trying to suspend a terminated thread should fail */
328   error=SuspendThread(thread);
329   ok(error==~0U, "wrong return code: %ld\n", error);
330   ok(GetLastError()==ERROR_ACCESS_DENIED || GetLastError()==ERROR_NO_MORE_ITEMS, "unexpected error code: %ld\n", GetLastError());
331
332   ok(CloseHandle(thread)!=0,"CloseHandle Failed\n");
333 }
334
335 /* Check that TerminateThread works properly
336 */
337 static VOID test_TerminateThread(void)
338 {
339   HANDLE thread,access_thread,event;
340   DWORD threadId,exitCode;
341   event=CreateEventA(NULL,TRUE,FALSE,NULL);
342   thread = CreateThread(NULL,0,threadFunc4,
343                         (LPVOID)event, 0,&threadId);
344   ok(thread!=NULL,"Create Thread failed\n");
345 /* TerminateThread has a race condition in Wine.  If the thread is terminated
346    before it starts, it leaves a process behind.  Therefore, we wait for the
347    thread to signal that it has started.  There is no easy way to force the
348    race to occur, so we don't try to find it.
349 */
350   ok(WaitForSingleObject(event,5000)==WAIT_OBJECT_0,
351      "TerminateThread didn't work\n");
352 /* check that access restrictions are obeyed */
353   if (pOpenThread) {
354     access_thread=pOpenThread(THREAD_ALL_ACCESS & (~THREAD_TERMINATE),
355                              0,threadId);
356     ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
357     if (access_thread!=NULL) {
358       obey_ar(TerminateThread(access_thread,99)==0);
359       ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
360     }
361   }
362 /* terminate a job and make sure it terminates */
363   ok(TerminateThread(thread,99)!=0,"TerminateThread failed\n");
364   ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
365      "TerminateThread didn't work\n");
366   ok(GetExitCodeThread(thread,&exitCode)!=STILL_ACTIVE,
367      "TerminateThread should not leave the thread 'STILL_ACTIVE'\n");
368   ok(exitCode==99, "TerminateThread returned invalid exit code\n");
369   ok(CloseHandle(thread)!=0,"Error Closing thread handle\n");
370 }
371
372 /* Check if CreateThread obeys the specified stack size.  This code does
373    not work properly, and is currently disabled
374 */
375 static VOID test_CreateThread_stack(void)
376 {
377 #if CHECK_STACK
378 /* The only way I know of to test the stack size is to use alloca
379    and __try/__except.  However, this is probably not portable,
380    and I couldn't get it to work under Wine anyhow.  However, here
381    is the code which should allow for testing that CreateThread
382    respects the stack-size limit
383 */
384      HANDLE thread;
385      DWORD threadId,exitCode;
386
387      SYSTEM_INFO sysInfo;
388      sysInfo.dwPageSize=0;
389      GetSystemInfo(&sysInfo);
390      ok(sysInfo.dwPageSize>0,"GetSystemInfo should return a valid page size\n");
391      thread = CreateThread(NULL,sysInfo.dwPageSize,
392                            threadFunc5,&exitCode,
393                            0,&threadId);
394      ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
395         "TerminateThread didn't work\n");
396      ok(exitCode==1,"CreateThread did not obey stack-size-limit\n");
397      ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
398 #endif
399 }
400
401 /* Check whether setting/retrieving thread priorities works */
402 static VOID test_thread_priority(void)
403 {
404    HANDLE curthread,access_thread;
405    DWORD curthreadId,exitCode;
406    int min_priority=-2,max_priority=2;
407    BOOL disabled,rc;
408    int i;
409
410    curthread=GetCurrentThread();
411    curthreadId=GetCurrentThreadId();
412 /* Check thread priority */
413 /* NOTE: on Win2k/XP priority can be from -7 to 6.  All other platforms it
414          is -2 to 2.  However, even on a real Win2k system, using thread
415          priorities beyond the -2 to 2 range does not work.  If you want to try
416          anyway, enable USE_EXTENDED_PRIORITIES
417 */
418    ok(GetThreadPriority(curthread)==THREAD_PRIORITY_NORMAL,
419       "GetThreadPriority Failed\n");
420
421    if (pOpenThread) {
422 /* check that access control is obeyed */
423      access_thread=pOpenThread(THREAD_ALL_ACCESS &
424                        (~THREAD_QUERY_INFORMATION) & (~THREAD_SET_INFORMATION),
425                        0,curthreadId);
426      ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
427      if (access_thread!=NULL) {
428        obey_ar(SetThreadPriority(access_thread,1)==0);
429        obey_ar(GetThreadPriority(access_thread)==THREAD_PRIORITY_ERROR_RETURN);
430        obey_ar(GetExitCodeThread(access_thread,&exitCode)==0);
431        ok(CloseHandle(access_thread),"Error Closing thread handle\n");
432      }
433 #if USE_EXTENDED_PRIORITIES
434      min_priority=-7; max_priority=6;
435 #endif
436    }
437    for(i=min_priority;i<=max_priority;i++) {
438      ok(SetThreadPriority(curthread,i)!=0,
439         "SetThreadPriority Failed for priority: %d\n",i);
440      ok(GetThreadPriority(curthread)==i,
441         "GetThreadPriority Failed for priority: %d\n",i);
442    }
443    ok(SetThreadPriority(curthread,THREAD_PRIORITY_TIME_CRITICAL)!=0,
444       "SetThreadPriority Failed\n");
445    ok(GetThreadPriority(curthread)==THREAD_PRIORITY_TIME_CRITICAL,
446       "GetThreadPriority Failed\n");
447    ok(SetThreadPriority(curthread,THREAD_PRIORITY_IDLE)!=0,
448        "SetThreadPriority Failed\n");
449    ok(GetThreadPriority(curthread)==THREAD_PRIORITY_IDLE,
450        "GetThreadPriority Failed\n");
451    ok(SetThreadPriority(curthread,0)!=0,"SetThreadPriority Failed\n");
452
453 /* Check thread priority boost */
454    if (!pGetThreadPriorityBoost || !pSetThreadPriorityBoost) 
455      return; /* Win9x */
456
457    SetLastError(0xdeadbeef);
458    rc=pGetThreadPriorityBoost(curthread,&disabled);
459    if (rc==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
460      return; /* WinME */
461
462 /* check that access control is obeyed */
463    access_thread=pOpenThread(THREAD_ALL_ACCESS &
464                      (~THREAD_QUERY_INFORMATION) & (~THREAD_SET_INFORMATION),
465                      0,curthreadId);
466    ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
467    if (access_thread!=NULL) {
468      obey_ar(pSetThreadPriorityBoost(access_thread,1)==0);
469      obey_ar(pGetThreadPriorityBoost(access_thread,&disabled)==0);
470      ok(CloseHandle(access_thread),"Error Closing thread handle\n");
471    }
472
473    todo_wine {
474      ok(rc!=0,"error=%ld\n",GetLastError());
475
476      rc = pSetThreadPriorityBoost(curthread,1);
477      ok( rc != 0, "error=%ld\n",GetLastError());
478      rc=pGetThreadPriorityBoost(curthread,&disabled);
479      ok(rc!=0 && disabled==1,
480         "rc=%d error=%ld disabled=%d\n",rc,GetLastError(),disabled);
481
482      rc = pSetThreadPriorityBoost(curthread,0);
483      ok( rc != 0, "error=%ld\n",GetLastError());
484      rc=pGetThreadPriorityBoost(curthread,&disabled);
485      ok(rc!=0 && disabled==0,
486         "rc=%d error=%ld disabled=%d\n",rc,GetLastError(),disabled);
487    }
488 }
489
490 /* check the GetThreadTimes function */
491 static VOID test_GetThreadTimes(void)
492 {
493      HANDLE thread,access_thread=NULL;
494      FILETIME creationTime,exitTime,kernelTime,userTime;
495      DWORD threadId;
496      int error;
497
498      thread = CreateThread(NULL,0,threadFunc2,NULL,
499                            CREATE_SUSPENDED,&threadId);
500
501      ok(thread!=NULL,"Create Thread failed\n");
502 /* check that access control is obeyed */
503      if (pOpenThread) {
504        access_thread=pOpenThread(THREAD_ALL_ACCESS &
505                                    (~THREAD_QUERY_INFORMATION), 0,threadId);
506        ok(access_thread!=NULL,
507           "OpenThread returned an invalid handle\n");
508      }
509      ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
510      ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
511         "ResumeThread didn't work\n");
512      creationTime.dwLowDateTime=99; creationTime.dwHighDateTime=99;
513      exitTime.dwLowDateTime=99;     exitTime.dwHighDateTime=99;
514      kernelTime.dwLowDateTime=99;   kernelTime.dwHighDateTime=99;
515      userTime.dwLowDateTime=99;     userTime.dwHighDateTime=99;
516 /* GetThreadTimes should set all of the parameters passed to it */
517      error=GetThreadTimes(thread,&creationTime,&exitTime,
518                           &kernelTime,&userTime);
519      if (error!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
520        ok(error!=0,"GetThreadTimes failed\n");
521        ok(creationTime.dwLowDateTime!=99 || creationTime.dwHighDateTime!=99,
522           "creationTime was invalid\n");
523        ok(exitTime.dwLowDateTime!=99 || exitTime.dwHighDateTime!=99,
524           "exitTime was invalid\n");
525        ok(kernelTime.dwLowDateTime!=99 || kernelTime.dwHighDateTime!=99,
526           "kernelTimewas invalid\n");
527        ok(userTime.dwLowDateTime!=99 || userTime.dwHighDateTime!=99,
528           "userTime was invalid\n");
529        ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
530        if(access_thread!=NULL)
531        {
532          error=GetThreadTimes(access_thread,&creationTime,&exitTime,
533                               &kernelTime,&userTime);
534          obey_ar(error==0);
535        }
536      }
537      if(access_thread!=NULL) {
538        ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
539      }
540 }
541
542 /* Check the processor affinity functions */
543 /* NOTE: These functions should also be checked that they obey access control
544 */
545 static VOID test_thread_processor(void)
546 {
547    HANDLE curthread,curproc;
548    DWORD processMask,systemMask;
549    SYSTEM_INFO sysInfo;
550    int error=0;
551
552    sysInfo.dwNumberOfProcessors=0;
553    GetSystemInfo(&sysInfo);
554    ok(sysInfo.dwNumberOfProcessors>0,
555       "GetSystemInfo failed to return a valid # of processors\n");
556 /* Use the current Thread/process for all tests */
557    curthread=GetCurrentThread();
558    ok(curthread!=NULL,"GetCurrentThread failed\n");
559    curproc=GetCurrentProcess();
560    ok(curproc!=NULL,"GetCurrentProcess failed\n");
561 /* Check the Affinity Mask functions */
562    ok(GetProcessAffinityMask(curproc,&processMask,&systemMask)!=0,
563       "GetProcessAffinityMask failed\n");
564    ok(SetThreadAffinityMask(curthread,processMask)==processMask,
565       "SetThreadAffinityMask failed\n");
566    ok(SetThreadAffinityMask(curthread,processMask+1)==0,
567       "SetThreadAffinityMask passed for an illegal processor\n");
568 /* NOTE: This only works on WinNT/2000/XP) */
569    if (pSetThreadIdealProcessor) {
570      todo_wine {
571        SetLastError(0);
572        error=pSetThreadIdealProcessor(curthread,0);
573        if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
574          ok(error!=-1, "SetThreadIdealProcessor failed\n");
575        }
576      }
577      if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
578        error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1);
579        ok(error==-1,
580           "SetThreadIdealProcessor succeeded with an illegal processor #\n");
581        todo_wine {
582          error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS);
583          ok(error==0, "SetThreadIdealProcessor returned an incorrect value\n");
584        }
585      }
586    }
587 }
588
589 static VOID test_GetThreadExitCode(void)
590 {
591     DWORD exitCode, threadid;
592     DWORD GLE, ret;
593     HANDLE thread;
594
595     ret = GetExitCodeThread((HANDLE)0x2bad2bad,&exitCode);
596     ok(ret==0, "GetExitCodeThread returned non zero value: %ld\n", ret);
597     GLE = GetLastError();
598     ok(GLE==ERROR_INVALID_HANDLE, "GetLastError returned %ld (expected 6)\n", GLE);
599
600     thread = CreateThread(NULL,0,threadFunc2,NULL,0,&threadid);
601     ret = WaitForSingleObject(thread,100);
602     ok(ret==WAIT_OBJECT_0, "threadFunc2 did not exit during 100 ms\n");
603     ret = GetExitCodeThread(thread,&exitCode);
604     ok(ret==exitCode || ret==1, 
605        "GetExitCodeThread returned %ld (expected 1 or %ld)\n", ret, exitCode);
606     ok(exitCode==99, "threadFunc2 exited with code %ld (expected 99)\n", exitCode);
607     ok(CloseHandle(thread)!=0,"Error closing thread handle\n");
608 }
609
610 #ifdef __i386__
611
612 static int test_value = 0;
613
614 static void set_test_val( int val )
615 {
616     test_value += val;
617 }
618
619 static DWORD WINAPI threadFunc6(LPVOID p)
620 {
621     test_value *= (int)p;
622     return 0;
623 }
624
625 static void test_SetThreadContext(void)
626 {
627     CONTEXT ctx;
628     int *stack;
629     HANDLE thread = CreateThread( NULL, 0, threadFunc6, (void *)2, CREATE_SUSPENDED, NULL );
630
631     ctx.ContextFlags = CONTEXT_FULL;
632     ok( GetThreadContext( thread, &ctx ), "GetThreadContext failed : (%ld)\n", GetLastError() );
633     /* simulate a call to set_test_val(10) */
634     stack = (int *)ctx.Esp;
635     stack[-1] = 10;
636     stack[-2] = ctx.Eip;
637     ctx.Esp -= 2 * sizeof(int *);
638     ctx.Eip = (DWORD)set_test_val;
639     ok( SetThreadContext( thread, &ctx ), "SetThreadContext failed : (%ld)\n", GetLastError() );
640     ResumeThread( thread );
641     WaitForSingleObject( thread, INFINITE );
642     ok( test_value == 20, "test_value %d instead of 20\n", test_value );
643 }
644
645 #endif  /* __i386__ */
646
647
648 START_TEST(thread)
649 {
650    HINSTANCE lib;
651 /* Neither Cygwin nor mingW export OpenThread, so do a dynamic check
652    so that the compile passes
653 */
654    lib=GetModuleHandleA("kernel32.dll");
655    ok(lib!=NULL,"Couldn't get a handle for kernel32.dll\n");
656    pGetThreadPriorityBoost=(GetThreadPriorityBoost_t)GetProcAddress(lib,"GetThreadPriorityBoost");
657    pOpenThread=(OpenThread_t)GetProcAddress(lib,"OpenThread");
658    pSetThreadIdealProcessor=(SetThreadIdealProcessor_t)GetProcAddress(lib,"SetThreadIdealProcessor");
659    pSetThreadPriorityBoost=(SetThreadPriorityBoost_t)GetProcAddress(lib,"SetThreadPriorityBoost");
660    test_CreateThread_basic();
661    test_CreateThread_suspended();
662    test_SuspendThread();
663    test_TerminateThread();
664    test_CreateThread_stack();
665    test_thread_priority();
666    test_GetThreadTimes();
667    test_thread_processor();
668    test_GetThreadExitCode();
669 #ifdef __i386__
670    test_SetThreadContext();
671 #endif
672 }