Avoid spaces before preprocessor directives, that's not portable.
[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 /* Basic test that simulatneous threads can access shared memory,
86    that the thread local storage routines work correctly, and that
87    threads actually run concurrently
88 */
89 DWORD WINAPI threadFunc1(LPVOID p)
90 {
91     t1Struct *tstruct = (t1Struct *)p;
92    int i;
93 /* write our thread # into shared memory */
94    tstruct->threadmem[tstruct->threadnum]=GetCurrentThreadId();
95    ok(TlsSetValue(tlsIndex,(LPVOID)(tstruct->threadnum+1))!=0,
96       "TlsSetValue failed\n");
97 /* The threads synchronize before terminating.  This is done by
98    Signaling an event, and waiting for all events to occur
99 */
100    SetEvent(tstruct->event[tstruct->threadnum]);
101    WaitForMultipleObjects(NUM_THREADS,tstruct->event,TRUE,INFINITE);
102 /* Double check that all threads really did run by validating that
103    they have all written to the shared memory. There should be no race
104    here, since all threads were synchronized after the write.*/
105    for(i=0;i<NUM_THREADS;i++) {
106      while(tstruct->threadmem[i]==0) ;
107    }
108 /* Check that noone cahnged our tls memory */
109    ok((int)TlsGetValue(tlsIndex)-1==tstruct->threadnum,
110       "TlsGetValue failed\n");
111    return NUM_THREADS+tstruct->threadnum;
112 }
113
114 DWORD WINAPI threadFunc2(LPVOID p)
115 {
116    return 99;
117 }
118
119 DWORD WINAPI threadFunc3(LPVOID p)
120 {
121    HANDLE thread;
122    thread=GetCurrentThread();
123    SuspendThread(thread);
124    return 99;
125 }
126
127 DWORD WINAPI threadFunc4(LPVOID p)
128 {
129     HANDLE event = (HANDLE)p;
130    if(event != NULL) {
131      SetEvent(event);
132    }
133    Sleep(99000);
134    return 0;
135 }
136
137 #if CHECK_STACK
138 DWORD WINAPI threadFunc5(LPVOID p)
139 {
140   DWORD *exitCode = (DWORD *)p;
141   SYSTEM_INFO sysInfo;
142   sysInfo.dwPageSize=0;
143   GetSystemInfo(&sysInfo);
144   *exitCode=0;
145    __TRY
146    {
147      alloca(2*sysInfo.dwPageSize);
148    }
149     __EXCEPT(1) {
150      *exitCode=1;
151    }
152    __ENDTRY
153    return 0;
154 }
155 #endif
156
157 /* Check basic funcationality of CreateThread and Tls* functions */
158 VOID test_CreateThread_basic(void)
159 {
160    HANDLE thread[NUM_THREADS],event[NUM_THREADS];
161    DWORD threadid[NUM_THREADS],curthreadId;
162    DWORD threadmem[NUM_THREADS];
163    DWORD exitCode;
164    t1Struct tstruct[NUM_THREADS];
165    int error;
166    DWORD i,j;
167 /* Retrieve current Thread ID for later comparisons */
168   curthreadId=GetCurrentThreadId();
169 /* Allocate some local storage */
170   ok((tlsIndex=TlsAlloc())!=TLS_OUT_OF_INDEXES,"TlsAlloc failed\n");
171 /* Create events for thread synchronization */
172   for(i=0;i<NUM_THREADS;i++) {
173     threadmem[i]=0;
174 /* Note that it doesn't matter what type of event we chose here.  This
175    test isn't trying to thoroughly test events
176 */
177     event[i]=CreateEventA(NULL,TRUE,FALSE,NULL);
178     tstruct[i].threadnum=i;
179     tstruct[i].threadmem=threadmem;
180     tstruct[i].event=event;
181   }
182
183 /* Test that passing arguments to threads works okay */
184   for(i=0;i<NUM_THREADS;i++) {
185     thread[i] = CreateThread(NULL,0,threadFunc1,
186                              &tstruct[i],0,&threadid[i]);
187     ok(thread[i]!=NULL,"Create Thread failed\n");
188   }
189 /* Test that the threads actually complete */
190   for(i=0;i<NUM_THREADS;i++) {
191     error=WaitForSingleObject(thread[i],5000);
192     ok(error==WAIT_OBJECT_0, "Thread did not complete within timelimit\n");
193     if(error!=WAIT_OBJECT_0) {
194       TerminateThread(thread[i],i+NUM_THREADS);
195     }
196     ok(GetExitCodeThread(thread[i],&exitCode),"Could not retrieve ext code\n");
197     ok(exitCode==i+NUM_THREADS,"Thread returned an incorrect exit code\n");
198   }
199 /* Test that each thread executed in its parent's address space
200    (it was able to change threadmem and pass that change back to its parent)
201    and that each thread id was independant).  Note that we prove that the
202    threads actually execute concurrently by having them block on each other
203    in threadFunc1
204 */
205   for(i=0;i<NUM_THREADS;i++) {
206     error=0;
207     for(j=i+1;j<NUM_THREADS;j++) {
208       if (threadmem[i]==threadmem[j]) {
209         error=1;
210       }
211     }
212     ok(!error && threadmem[i]==threadid[i] && threadmem[i]!=curthreadId,
213          "Thread did not execute successfully\n");
214     ok(CloseHandle(thread[i])!=0,"CloseHandle failed\n");
215   }
216   ok(TlsFree(tlsIndex)!=0,"TlsFree failed\n");
217 }
218
219 /* Check that using the CREATE_SUSPENDED flag works */
220 VOID test_CreateThread_suspended(void)
221 {
222   HANDLE thread;
223   DWORD threadId;
224   int error;
225
226   thread = CreateThread(NULL,0,threadFunc2,NULL,
227                         CREATE_SUSPENDED,&threadId);
228   ok(thread!=NULL,"Create Thread failed\n");
229 /* Check that the thread is suspended */
230   ok(SuspendThread(thread)==1,"Thread did not start suspended\n");
231   ok(ResumeThread(thread)==2,"Resume thread returned an invalid value\n");
232 /* Check that resume thread didn't actually start the thread.  I can't think
233    of a better way of checking this than just waiting.  I am not sure if this
234    will work on slow computers.
235 */
236   ok(WaitForSingleObject(thread,1000)==WAIT_TIMEOUT,
237      "ResumeThread should not have actually started the thread\n");
238 /* Now actually resume the thread and make sure that it actually completes*/
239   ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
240   ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0,
241      "Thread did not resume\n");
242   if(error!=WAIT_OBJECT_0) {
243     TerminateThread(thread,1);
244   }
245   ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
246 }
247
248 /* Check that SuspendThread and ResumeThread work */
249 VOID test_SuspendThread(void)
250 {
251   HANDLE thread,access_thread;
252   DWORD threadId,exitCode,error;
253   int i;
254
255   thread = CreateThread(NULL,0,threadFunc3,NULL,
256                         0,&threadId);
257   ok(thread!=NULL,"Create Thread failed\n");
258 /* Check that the thread is suspended */
259 /* Note that this is a polling method, and there is a race between
260    SuspendThread being called (in the child, and the loop below timing out,
261    so the test could fail on a heavily loaded or slow computer.
262 */
263   error=0;
264   for(i=0;error==0 && i<100;i++) {
265     error=SuspendThread(thread);
266     ResumeThread(thread);
267     if(error==0) {
268       Sleep(50);
269       i++;
270     }
271   }
272   ok(error==1,"SuspendThread did not work\n");
273 /* check that access restrictions are obeyed */
274   if (pOpenThread) {
275     access_thread=pOpenThread(THREAD_ALL_ACCESS & (~THREAD_SUSPEND_RESUME),
276                            0,threadId);
277     ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
278     if (access_thread!=NULL) {
279       ok(SuspendThread(access_thread)==~0UL,
280          "SuspendThread did not obey access restrictions\n");
281       ok(ResumeThread(access_thread)==~0UL,
282          "ResumeThread did not obey access restrictions\n");
283       ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
284     }
285   }
286 /* Double check that the thread really is suspended */
287   ok((error=GetExitCodeThread(thread,&exitCode))!=0 && exitCode==STILL_ACTIVE,
288      "Thread did not really suspend\n");
289 /* Resume the thread, and make sure it actually completes */
290   ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
291   ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0,
292      "Thread did not resume\n");
293   if(error!=WAIT_OBJECT_0) {
294     TerminateThread(thread,1);
295   }
296   /* Trying to suspend a terminated thread should fail */
297   error=SuspendThread(thread);
298   ok(error==~0UL, "wrong return code: %ld\n", error);
299   ok(GetLastError()==ERROR_ACCESS_DENIED || GetLastError()==ERROR_NO_MORE_ITEMS, "unexpected error code: %ld\n", GetLastError());
300
301   ok(CloseHandle(thread)!=0,"CloseHandle Failed\n");
302 }
303
304 /* Check that TerminateThread works properly
305 */
306 VOID test_TerminateThread(void)
307 {
308   HANDLE thread,access_thread,event;
309   DWORD threadId,exitCode;
310   event=CreateEventA(NULL,TRUE,FALSE,NULL);
311   thread = CreateThread(NULL,0,threadFunc4,
312                         (LPVOID)event, 0,&threadId);
313   ok(thread!=NULL,"Create Thread failed\n");
314 /* Terminate thread has a race condition in Wine.  If the thread is terminated
315    before it starts, it leaves a process behind.  Therefore, we wait for the
316    thread to signal that it has started.  There is no easy way to force the
317    race to occur, so we don't try to find it.
318 */
319   ok(WaitForSingleObject(event,5000)==WAIT_OBJECT_0,
320      "TerminateThread didn't work\n");
321 /* check that access restrictions are obeyed */
322   if (pOpenThread) {
323     access_thread=pOpenThread(THREAD_ALL_ACCESS & (~THREAD_TERMINATE),
324                              0,threadId);
325     ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
326     if (access_thread!=NULL) {
327       ok(TerminateThread(access_thread,99)==0,
328          "TerminateThread did not obey access restrictions\n");
329       ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
330     }
331   }
332 /* terminate a job and make sure it terminates */
333   ok(TerminateThread(thread,99)!=0,"TerminateThread failed\n");
334   ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
335      "TerminateThread didn't work\n");
336   ok(GetExitCodeThread(thread,&exitCode)!=STILL_ACTIVE,
337      "TerminateThread should not leave the thread 'STILL_ACTIVE'\n");
338   ok(exitCode==99, "TerminateThread returned invalid exit code\n");
339   ok(CloseHandle(thread)!=0,"Error Closing thread handle\n");
340 }
341
342 /* Check if CreateThread obeys the specified stack size.  This code does
343    not work properly, and is currently disabled
344 */
345 VOID test_CreateThread_stack(void)
346 {
347 #if CHECK_STACK
348 /* The only way I know of to test the stack size is to use alloca
349    and __try/__except.  However, this is probably not portable,
350    and I couldn't get it to work under Wine anyhow.  However, here
351    is the code which should allow for testing that CreateThread
352    respects the stack-size limit
353 */
354      HANDLE thread;
355      DWORD threadId,exitCode;
356
357      SYSTEM_INFO sysInfo;
358      sysInfo.dwPageSize=0;
359      GetSystemInfo(&sysInfo);
360      ok(sysInfo.dwPageSize>0,"GetSystemInfo should return a valid page size\n");
361      thread = CreateThread(NULL,sysInfo.dwPageSize,
362                            threadFunc5,&exitCode,
363                            0,&threadId);
364      ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
365         "TerminateThread didn't work\n");
366      ok(exitCode==1,"CreateThread did not obey stack-size-limit\n");
367      ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
368 #endif
369 }
370
371 /* Check whether setting/retrieving thread priorities works */
372 VOID test_thread_priority(void)
373 {
374    HANDLE curthread,access_thread;
375    DWORD curthreadId,exitCode;
376    int min_priority=-2,max_priority=2;
377    BOOL disabled;
378    int i;
379
380    curthread=GetCurrentThread();
381    curthreadId=GetCurrentThreadId();
382 /* Check thread priority */
383 /* NOTE: on Win2k/XP priority can be from -7 to 6.  All other platforms it
384          is -2 to 2.  However, even on a real Win2k system, using thread
385          priorities beyond the -2 to 2 range does not work.  If you want to try
386          anyway, enable USE_EXTENDED_PRIORITIES
387 */
388    ok(GetThreadPriority(curthread)==THREAD_PRIORITY_NORMAL,
389       "GetThreadPriority Failed\n");
390
391    if (pOpenThread) {
392 /* check that access control is obeyed */
393      access_thread=pOpenThread(THREAD_ALL_ACCESS &
394                        (~THREAD_QUERY_INFORMATION) & (~THREAD_SET_INFORMATION),
395                        0,curthreadId);
396      ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
397      if (access_thread!=NULL) {
398        ok(SetThreadPriority(access_thread,1)==0,
399           "SetThreadPriority did not obey access restrictions\n");
400        ok(GetThreadPriority(access_thread)==THREAD_PRIORITY_ERROR_RETURN,
401           "GetThreadPriority did not obey access restrictions\n");
402        if (pSetThreadPriorityBoost)
403          ok(pSetThreadPriorityBoost(access_thread,1)==0,
404             "SetThreadPriorityBoost did not obey access restrictions\n");
405        if (pGetThreadPriorityBoost)
406          ok(pGetThreadPriorityBoost(access_thread,&disabled)==0,
407             "GetThreadPriorityBoost did not obey access restrictions\n");
408        ok(GetExitCodeThread(access_thread,&exitCode)==0,
409           "GetExitCodeThread did not obey access restrictions\n");
410        ok(CloseHandle(access_thread),"Error Closing thread handle\n");
411      }
412 #if USE_EXTENDED_PRIORITIES
413      min_priority=-7; max_priority=6;
414 #endif
415    }
416    for(i=min_priority;i<=max_priority;i++) {
417      ok(SetThreadPriority(curthread,i)!=0,
418         "SetThreadPriority Failed for priority: %d\n",i);
419      ok(GetThreadPriority(curthread)==i,
420         "GetThreadPriority Failed for priority: %d\n",i);
421    }
422    ok(SetThreadPriority(curthread,THREAD_PRIORITY_TIME_CRITICAL)!=0,
423       "SetThreadPriority Failed\n");
424    ok(GetThreadPriority(curthread)==THREAD_PRIORITY_TIME_CRITICAL,
425       "GetThreadPriority Failed\n");
426    ok(SetThreadPriority(curthread,THREAD_PRIORITY_IDLE)!=0,
427        "SetThreadPriority Failed\n");
428    ok(GetThreadPriority(curthread)==THREAD_PRIORITY_IDLE,
429        "GetThreadPriority Failed\n");
430    ok(SetThreadPriority(curthread,0)!=0,"SetThreadPriority Failed\n");
431
432 /* Check thread priority boost */
433    if (pGetThreadPriorityBoost && pSetThreadPriorityBoost) {
434      BOOL rc;
435      todo_wine {
436          SetLastError(0);
437          rc=pGetThreadPriorityBoost(curthread,&disabled);
438          if (rc!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
439              ok(rc!=0,"error=%ld\n",GetLastError());
440
441              rc = pSetThreadPriorityBoost(curthread,1);
442              ok( rc != 0, "error=%ld\n",GetLastError());
443              rc=pGetThreadPriorityBoost(curthread,&disabled);
444              ok(rc!=0 && disabled==1,
445                 "rc=%d error=%ld disabled=%d\n",rc,GetLastError(),disabled);
446
447              rc = pSetThreadPriorityBoost(curthread,0);
448              ok( rc != 0, "error=%ld\n",GetLastError());
449              rc=pGetThreadPriorityBoost(curthread,&disabled);
450              ok(rc!=0 && disabled==0,
451                 "rc=%d error=%ld disabled=%d\n",rc,GetLastError(),disabled);
452          }
453      }
454    }
455 }
456
457 /* check the GetThreadTimes function */
458 VOID test_GetThreadTimes(void)
459 {
460      HANDLE thread,access_thread=NULL;
461      FILETIME creationTime,exitTime,kernelTime,userTime;
462      DWORD threadId;
463      int error;
464
465      thread = CreateThread(NULL,0,threadFunc2,NULL,
466                            CREATE_SUSPENDED,&threadId);
467
468      ok(thread!=NULL,"Create Thread failed\n");
469 /* check that access control is obeyed */
470      if (pOpenThread) {
471        access_thread=pOpenThread(THREAD_ALL_ACCESS &
472                                    (~THREAD_QUERY_INFORMATION), 0,threadId);
473        ok(access_thread!=NULL,
474           "OpenThread returned an invalid handle\n");
475      }
476      ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
477      ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
478         "ResumeThread didn't work\n");
479      if(access_thread!=NULL) {
480        error=GetThreadTimes(access_thread,&creationTime,&exitTime,
481                             &kernelTime,&userTime);
482        ok(error==0, "GetThreadTimes did not obey access restrictions\n");
483        ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
484      }
485      creationTime.dwLowDateTime=99; creationTime.dwHighDateTime=99;
486      exitTime.dwLowDateTime=99;     exitTime.dwHighDateTime=99;
487      kernelTime.dwLowDateTime=99;   kernelTime.dwHighDateTime=99;
488      userTime.dwLowDateTime=99;     userTime.dwHighDateTime=99;
489 /* GetThreadTimes should set all of the parameters passed to it */
490      error=GetThreadTimes(thread,&creationTime,&exitTime,
491                           &kernelTime,&userTime);
492      if (error!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
493        ok(error!=0,"GetThreadTimes failed\n");
494        ok(creationTime.dwLowDateTime!=99 || creationTime.dwHighDateTime!=99,
495           "creationTime was invalid\n");
496        ok(exitTime.dwLowDateTime!=99 || exitTime.dwHighDateTime!=99,
497           "exitTime was invalid\n");
498        ok(kernelTime.dwLowDateTime!=99 || kernelTime.dwHighDateTime!=99,
499           "kernelTimewas invalid\n");
500        ok(userTime.dwLowDateTime!=99 || userTime.dwHighDateTime!=99,
501           "userTime was invalid\n");
502        ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
503      }
504 }
505
506 /* Check the processor affinity functions */
507 /* NOTE: These functions should also be checked that they obey access control
508 */
509 VOID test_thread_processor(void)
510 {
511    HANDLE curthread,curproc;
512    DWORD processMask,systemMask;
513    SYSTEM_INFO sysInfo;
514    int error=0;
515
516    sysInfo.dwNumberOfProcessors=0;
517    GetSystemInfo(&sysInfo);
518    ok(sysInfo.dwNumberOfProcessors>0,
519       "GetSystemInfo failed to return a valid # of processors\n");
520 /* Use the current Thread/process for all tests */
521    curthread=GetCurrentThread();
522    ok(curthread!=NULL,"GetCurrentThread failed\n");
523    curproc=GetCurrentProcess();
524    ok(curproc!=NULL,"GetCurrentProcess failed\n");
525 /* Check the Affinity Mask functions */
526    ok(GetProcessAffinityMask(curproc,&processMask,&systemMask)!=0,
527       "GetProcessAffinityMask failed\n");
528    ok(SetThreadAffinityMask(curthread,processMask)==1,
529       "SetThreadAffinityMask failed\n");
530    ok(SetThreadAffinityMask(curthread,processMask+1)==0,
531       "SetThreadAffinityMask passed for an illegal processor\n");
532 /* NOTE: This only works on WinNT/2000/XP) */
533    if (pSetThreadIdealProcessor) {
534      todo_wine {
535        SetLastError(0);
536        error=pSetThreadIdealProcessor(curthread,0);
537        if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
538          ok(error!=-1, "SetThreadIdealProcessor failed\n");
539        }
540      }
541      if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
542        error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1);
543        ok(error==-1,
544           "SetThreadIdealProcessor succeeded with an illegal processor #\n");
545        todo_wine {
546          error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS);
547          ok(error==0, "SetThreadIdealProcessor returned an incorrect value\n");
548        }
549      }
550    }
551 }
552
553 START_TEST(thread)
554 {
555    HINSTANCE lib;
556 /* Neither Cygwin nor mingW export OpenThread, so do a dynamic check
557    so that the compile passes
558 */
559    lib=LoadLibraryA("kernel32");
560    ok(lib!=NULL,"Couldn't load kernel32.dll\n");
561    pGetThreadPriorityBoost=(GetThreadPriorityBoost_t)GetProcAddress(lib,"GetThreadPriorityBoost");
562    pOpenThread=(OpenThread_t)GetProcAddress(lib,"OpenThread");
563    pSetThreadIdealProcessor=(SetThreadIdealProcessor_t)GetProcAddress(lib,"SetThreadIdealProcessor");
564    pSetThreadPriorityBoost=(SetThreadPriorityBoost_t)GetProcAddress(lib,"SetThreadPriorityBoost");
565    test_CreateThread_basic();
566    test_CreateThread_suspended();
567    test_SuspendThread();
568    test_TerminateThread();
569    test_CreateThread_stack();
570    test_thread_priority();
571    test_GetThreadTimes();
572    test_thread_processor();
573 }