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