| /* | |
| * Unit test suite for directory functions. | |
| * | |
| * Copyright 2002 Geoffrey Hausheer | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| /* Define _WIN32_WINNT to get SetThreadIdealProcessor on Windows */ | |
| #define _WIN32_WINNT 0x0500 | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #include "wine/test.h" | |
| #include <windef.h> | |
| #include <winbase.h> | |
| #include <winnt.h> | |
| #include <winerror.h> | |
| /* Specify the number of simultaneous threads to test */ | |
| #define NUM_THREADS 4 | |
| /* Specify whether to test the extended priorities for Win2k/XP */ | |
| #define USE_EXTENDED_PRIORITIES 0 | |
| /* Specify whether to test the stack allocation in CreateThread */ | |
| #define CHECK_STACK 0 | |
| /* Set CHECK_STACK to 1 if you want to try to test the stack-limit from | |
| CreateThread. So far I have been unable to make this work, and | |
| I am in doubt as to how portable it is. Also, according to MSDN, | |
| you shouldn't mix C-run-time-libraries (i.e. alloca) with CreateThread. | |
| Anyhow, the check is currently commented out | |
| */ | |
| #if CHECK_STACK | |
| # ifdef __try | |
| # define __TRY __try | |
| # define __EXCEPT __except | |
| # define __ENDTRY | |
| # else | |
| # include "wine/exception.h" | |
| # endif | |
| #endif | |
| typedef BOOL (WINAPI *GetThreadPriorityBoost_t)(HANDLE,PBOOL); | |
| static GetThreadPriorityBoost_t pGetThreadPriorityBoost=NULL; | |
| typedef HANDLE (WINAPI *OpenThread_t)(DWORD,BOOL,DWORD); | |
| static OpenThread_t pOpenThread=NULL; | |
| typedef BOOL (WINAPI *QueueUserWorkItem_t)(LPTHREAD_START_ROUTINE,PVOID,ULONG); | |
| static QueueUserWorkItem_t pQueueUserWorkItem=NULL; | |
| typedef DWORD (WINAPI *SetThreadIdealProcessor_t)(HANDLE,DWORD); | |
| static SetThreadIdealProcessor_t pSetThreadIdealProcessor=NULL; | |
| typedef BOOL (WINAPI *SetThreadPriorityBoost_t)(HANDLE,BOOL); | |
| static SetThreadPriorityBoost_t pSetThreadPriorityBoost=NULL; | |
| static HANDLE create_target_process(const char *arg) | |
| { | |
| char **argv; | |
| char cmdline[MAX_PATH]; | |
| PROCESS_INFORMATION pi; | |
| STARTUPINFO si = { 0 }; | |
| si.cb = sizeof(si); | |
| winetest_get_mainargs( &argv ); | |
| sprintf(cmdline, "%s %s %s", argv[0], argv[1], arg); | |
| ok(CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, | |
| &si, &pi) != 0, "error: %u\n", GetLastError()); | |
| ok(CloseHandle(pi.hThread) != 0, "error %u\n", GetLastError()); | |
| return pi.hProcess; | |
| } | |
| /* Functions not tested yet: | |
| AttachThreadInput | |
| SetThreadContext | |
| SwitchToThread | |
| In addition there are no checks that the inheritance works properly in | |
| CreateThread | |
| */ | |
| DWORD tlsIndex; | |
| typedef struct { | |
| int threadnum; | |
| HANDLE *event; | |
| DWORD *threadmem; | |
| } t1Struct; | |
| /* WinME supports OpenThread but doesn't know about access restrictions so | |
| we require them to be either completely ignored or always obeyed. | |
| */ | |
| INT obeying_ars = 0; /* -1 == no, 0 == dunno yet, 1 == yes */ | |
| #define obey_ar(x) \ | |
| (obeying_ars == 0 \ | |
| ? ((x) \ | |
| ? (obeying_ars = +1) \ | |
| : ((obeying_ars = -1), \ | |
| trace("not restricted, assuming consistent behaviour\n"))) \ | |
| : (obeying_ars < 0) \ | |
| ? ok(!(x), "access restrictions obeyed\n") \ | |
| : ok( (x), "access restrictions not obeyed\n")) | |
| /* Basic test that simultaneous threads can access shared memory, | |
| that the thread local storage routines work correctly, and that | |
| threads actually run concurrently | |
| */ | |
| static DWORD WINAPI threadFunc1(LPVOID p) | |
| { | |
| t1Struct *tstruct = (t1Struct *)p; | |
| int i; | |
| /* write our thread # into shared memory */ | |
| tstruct->threadmem[tstruct->threadnum]=GetCurrentThreadId(); | |
| ok(TlsSetValue(tlsIndex,(LPVOID)(tstruct->threadnum+1))!=0, | |
| "TlsSetValue failed\n"); | |
| /* The threads synchronize before terminating. This is done by | |
| Signaling an event, and waiting for all events to occur | |
| */ | |
| SetEvent(tstruct->event[tstruct->threadnum]); | |
| WaitForMultipleObjects(NUM_THREADS,tstruct->event,TRUE,INFINITE); | |
| /* Double check that all threads really did run by validating that | |
| they have all written to the shared memory. There should be no race | |
| here, since all threads were synchronized after the write.*/ | |
| for(i=0;i<NUM_THREADS;i++) { | |
| while(tstruct->threadmem[i]==0) ; | |
| } | |
| /* lstrlenA contains an exception handler so this makes sure exceptions work in threads */ | |
| ok( lstrlenA( (char *)0xdeadbeef ) == 0, "lstrlenA: unexpected success\n" ); | |
| /* Check that no one changed our tls memory */ | |
| ok((int)TlsGetValue(tlsIndex)-1==tstruct->threadnum, | |
| "TlsGetValue failed\n"); | |
| return NUM_THREADS+tstruct->threadnum; | |
| } | |
| static DWORD WINAPI threadFunc2(LPVOID p) | |
| { | |
| return 99; | |
| } | |
| static DWORD WINAPI threadFunc3(LPVOID p) | |
| { | |
| HANDLE thread; | |
| thread=GetCurrentThread(); | |
| SuspendThread(thread); | |
| return 99; | |
| } | |
| static DWORD WINAPI threadFunc4(LPVOID p) | |
| { | |
| HANDLE event = (HANDLE)p; | |
| if(event != NULL) { | |
| SetEvent(event); | |
| } | |
| Sleep(99000); | |
| return 0; | |
| } | |
| #if CHECK_STACK | |
| static DWORD WINAPI threadFunc5(LPVOID p) | |
| { | |
| DWORD *exitCode = (DWORD *)p; | |
| SYSTEM_INFO sysInfo; | |
| sysInfo.dwPageSize=0; | |
| GetSystemInfo(&sysInfo); | |
| *exitCode=0; | |
| __TRY | |
| { | |
| alloca(2*sysInfo.dwPageSize); | |
| } | |
| __EXCEPT(1) { | |
| *exitCode=1; | |
| } | |
| __ENDTRY | |
| return 0; | |
| } | |
| #endif | |
| static DWORD WINAPI threadFunc_SetEvent(LPVOID p) | |
| { | |
| SetEvent((HANDLE) p); | |
| return 0; | |
| } | |
| static DWORD WINAPI threadFunc_CloseHandle(LPVOID p) | |
| { | |
| CloseHandle((HANDLE) p); | |
| return 0; | |
| } | |
| static void create_function_addr_events(HANDLE events[2]) | |
| { | |
| char buffer[256]; | |
| sprintf(buffer, "threadFunc_SetEvent %p", threadFunc_SetEvent); | |
| events[0] = CreateEvent(NULL, FALSE, FALSE, buffer); | |
| sprintf(buffer, "threadFunc_CloseHandle %p", threadFunc_CloseHandle); | |
| events[1] = CreateEvent(NULL, FALSE, FALSE, buffer); | |
| } | |
| /* check CreateRemoteThread */ | |
| static VOID test_CreateRemoteThread(void) | |
| { | |
| HANDLE hProcess, hThread, hEvent, hRemoteEvent; | |
| DWORD tid, ret, exitcode; | |
| HANDLE hAddrEvents[2]; | |
| hProcess = create_target_process("sleep"); | |
| ok(hProcess != NULL, "Can't start process\n"); | |
| /* ensure threadFunc_SetEvent & threadFunc_CloseHandle are the same | |
| * address as in the child process */ | |
| create_function_addr_events(hAddrEvents); | |
| ret = WaitForMultipleObjects(2, hAddrEvents, TRUE, 5000); | |
| if (ret == WAIT_TIMEOUT) | |
| { | |
| skip("child process wasn't mapped at same address, so can't do CreateRemoteThread tests.\n"); | |
| return; | |
| } | |
| hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); | |
| ok(hEvent != NULL, "Can't create event, err=%u\n", GetLastError()); | |
| ret = DuplicateHandle(GetCurrentProcess(), hEvent, hProcess, &hRemoteEvent, | |
| 0, FALSE, DUPLICATE_SAME_ACCESS); | |
| ok(ret != 0, "DuplicateHandle failed, err=%u\n", GetLastError()); | |
| /* create suspended remote thread with entry point SetEvent() */ | |
| SetLastError(0xdeadbeef); | |
| hThread = CreateRemoteThread(hProcess, NULL, 0, threadFunc_SetEvent, | |
| hRemoteEvent, CREATE_SUSPENDED, &tid); | |
| if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) | |
| { | |
| skip("CreateRemoteThread is not implemented\n"); | |
| goto cleanup; | |
| } | |
| ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", GetLastError()); | |
| ok(tid != 0, "null tid\n"); | |
| ret = SuspendThread(hThread); | |
| ok(ret == 1, "ret=%u, err=%u\n", ret, GetLastError()); | |
| ret = ResumeThread(hThread); | |
| ok(ret == 2, "ret=%u, err=%u\n", ret, GetLastError()); | |
| /* thread still suspended, so wait times out */ | |
| ret = WaitForSingleObject(hEvent, 100); | |
| ok(ret == WAIT_TIMEOUT, "wait did not time out, ret=%u\n", ret); | |
| ret = ResumeThread(hThread); | |
| ok(ret == 1, "ret=%u, err=%u\n", ret, GetLastError()); | |
| /* wait that doesn't time out */ | |
| ret = WaitForSingleObject(hEvent, 100); | |
| ok(ret == WAIT_OBJECT_0, "object not signaled, ret=%u\n", ret); | |
| /* wait for thread end */ | |
| ret = WaitForSingleObject(hThread, 100); | |
| ok(ret == WAIT_OBJECT_0, "waiting for thread failed, ret=%u\n", ret); | |
| CloseHandle(hThread); | |
| /* create and wait for remote thread with entry point CloseHandle() */ | |
| hThread = CreateRemoteThread(hProcess, NULL, 0, | |
| threadFunc_CloseHandle, | |
| hRemoteEvent, 0, &tid); | |
| ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", GetLastError()); | |
| ret = WaitForSingleObject(hThread, 100); | |
| ok(ret == WAIT_OBJECT_0, "waiting for thread failed, ret=%u\n", ret); | |
| CloseHandle(hThread); | |
| /* create remote thread with entry point SetEvent() */ | |
| hThread = CreateRemoteThread(hProcess, NULL, 0, | |
| threadFunc_SetEvent, | |
| hRemoteEvent, 0, &tid); | |
| ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", GetLastError()); | |
| /* closed handle, so wait times out */ | |
| ret = WaitForSingleObject(hEvent, 100); | |
| ok(ret == WAIT_TIMEOUT, "wait did not time out, ret=%u\n", ret); | |
| /* check that remote SetEvent() failed */ | |
| ret = GetExitCodeThread(hThread, &exitcode); | |
| ok(ret != 0, "GetExitCodeThread failed, err=%u\n", GetLastError()); | |
| if (ret) ok(exitcode == 0, "SetEvent succeeded, expected to fail\n"); | |
| CloseHandle(hThread); | |
| cleanup: | |
| TerminateProcess(hProcess, 0); | |
| CloseHandle(hEvent); | |
| CloseHandle(hProcess); | |
| } | |
| /* Check basic funcationality of CreateThread and Tls* functions */ | |
| static VOID test_CreateThread_basic(void) | |
| { | |
| HANDLE thread[NUM_THREADS],event[NUM_THREADS]; | |
| DWORD threadid[NUM_THREADS],curthreadId; | |
| DWORD threadmem[NUM_THREADS]; | |
| DWORD exitCode; | |
| t1Struct tstruct[NUM_THREADS]; | |
| int error; | |
| DWORD i,j; | |
| DWORD GLE, ret; | |
| /* lstrlenA contains an exception handler so this makes sure exceptions work in the main thread */ | |
| ok( lstrlenA( (char *)0xdeadbeef ) == 0, "lstrlenA: unexpected success\n" ); | |
| /* Retrieve current Thread ID for later comparisons */ | |
| curthreadId=GetCurrentThreadId(); | |
| /* Allocate some local storage */ | |
| ok((tlsIndex=TlsAlloc())!=TLS_OUT_OF_INDEXES,"TlsAlloc failed\n"); | |
| /* Create events for thread synchronization */ | |
| for(i=0;i<NUM_THREADS;i++) { | |
| threadmem[i]=0; | |
| /* Note that it doesn't matter what type of event we chose here. This | |
| test isn't trying to thoroughly test events | |
| */ | |
| event[i]=CreateEventA(NULL,TRUE,FALSE,NULL); | |
| tstruct[i].threadnum=i; | |
| tstruct[i].threadmem=threadmem; | |
| tstruct[i].event=event; | |
| } | |
| /* Test that passing arguments to threads works okay */ | |
| for(i=0;i<NUM_THREADS;i++) { | |
| thread[i] = CreateThread(NULL,0,threadFunc1, | |
| &tstruct[i],0,&threadid[i]); | |
| ok(thread[i]!=NULL,"Create Thread failed\n"); | |
| } | |
| /* Test that the threads actually complete */ | |
| for(i=0;i<NUM_THREADS;i++) { | |
| error=WaitForSingleObject(thread[i],5000); | |
| ok(error==WAIT_OBJECT_0, "Thread did not complete within timelimit\n"); | |
| if(error!=WAIT_OBJECT_0) { | |
| TerminateThread(thread[i],i+NUM_THREADS); | |
| } | |
| ok(GetExitCodeThread(thread[i],&exitCode),"Could not retrieve ext code\n"); | |
| ok(exitCode==i+NUM_THREADS,"Thread returned an incorrect exit code\n"); | |
| } | |
| /* Test that each thread executed in its parent's address space | |
| (it was able to change threadmem and pass that change back to its parent) | |
| and that each thread id was independent). Note that we prove that the | |
| threads actually execute concurrently by having them block on each other | |
| in threadFunc1 | |
| */ | |
| for(i=0;i<NUM_THREADS;i++) { | |
| error=0; | |
| for(j=i+1;j<NUM_THREADS;j++) { | |
| if (threadmem[i]==threadmem[j]) { | |
| error=1; | |
| } | |
| } | |
| ok(!error && threadmem[i]==threadid[i] && threadmem[i]!=curthreadId, | |
| "Thread did not execute successfully\n"); | |
| ok(CloseHandle(thread[i])!=0,"CloseHandle failed\n"); | |
| } | |
| ok(TlsFree(tlsIndex)!=0,"TlsFree failed\n"); | |
| /* Test how passing NULL as a pointer to threadid works */ | |
| SetLastError(0xFACEaBAD); | |
| thread[0] = CreateThread(NULL,0,threadFunc2,NULL,0,NULL); | |
| GLE = GetLastError(); | |
| if (thread[0]) { /* NT */ | |
| ok(GLE==0xFACEaBAD, "CreateThread set last error to %d, expected 4207848365\n", GLE); | |
| ret = WaitForSingleObject(thread[0],100); | |
| ok(ret==WAIT_OBJECT_0, "threadFunc2 did not exit during 100 ms\n"); | |
| ret = GetExitCodeThread(thread[0],&exitCode); | |
| ok(ret!=0, "GetExitCodeThread returned %d (expected nonzero)\n", ret); | |
| ok(exitCode==99, "threadFunc2 exited with code: %d (expected 99)\n", exitCode); | |
| ok(CloseHandle(thread[0])!=0,"Error closing thread handle\n"); | |
| } | |
| else { /* 9x */ | |
| ok(GLE==ERROR_INVALID_PARAMETER, "CreateThread set last error to %d, expected 87\n", GLE); | |
| } | |
| } | |
| /* Check that using the CREATE_SUSPENDED flag works */ | |
| static VOID test_CreateThread_suspended(void) | |
| { | |
| HANDLE thread; | |
| DWORD threadId; | |
| int error; | |
| thread = CreateThread(NULL,0,threadFunc2,NULL, | |
| CREATE_SUSPENDED,&threadId); | |
| ok(thread!=NULL,"Create Thread failed\n"); | |
| /* Check that the thread is suspended */ | |
| ok(SuspendThread(thread)==1,"Thread did not start suspended\n"); | |
| ok(ResumeThread(thread)==2,"Resume thread returned an invalid value\n"); | |
| /* Check that resume thread didn't actually start the thread. I can't think | |
| of a better way of checking this than just waiting. I am not sure if this | |
| will work on slow computers. | |
| */ | |
| ok(WaitForSingleObject(thread,1000)==WAIT_TIMEOUT, | |
| "ResumeThread should not have actually started the thread\n"); | |
| /* Now actually resume the thread and make sure that it actually completes*/ | |
| ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n"); | |
| ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0, | |
| "Thread did not resume\n"); | |
| if(error!=WAIT_OBJECT_0) { | |
| TerminateThread(thread,1); | |
| } | |
| ok(CloseHandle(thread)!=0,"CloseHandle failed\n"); | |
| } | |
| /* Check that SuspendThread and ResumeThread work */ | |
| static VOID test_SuspendThread(void) | |
| { | |
| HANDLE thread,access_thread; | |
| DWORD threadId,exitCode,error; | |
| int i; | |
| thread = CreateThread(NULL,0,threadFunc3,NULL, | |
| 0,&threadId); | |
| ok(thread!=NULL,"Create Thread failed\n"); | |
| /* Check that the thread is suspended */ | |
| /* Note that this is a polling method, and there is a race between | |
| SuspendThread being called (in the child, and the loop below timing out, | |
| so the test could fail on a heavily loaded or slow computer. | |
| */ | |
| error=0; | |
| for(i=0;error==0 && i<100;i++) { | |
| error=SuspendThread(thread); | |
| ResumeThread(thread); | |
| if(error==0) { | |
| Sleep(50); | |
| i++; | |
| } | |
| } | |
| ok(error==1,"SuspendThread did not work\n"); | |
| /* check that access restrictions are obeyed */ | |
| if (pOpenThread) { | |
| access_thread=pOpenThread(THREAD_ALL_ACCESS & (~THREAD_SUSPEND_RESUME), | |
| 0,threadId); | |
| ok(access_thread!=NULL,"OpenThread returned an invalid handle\n"); | |
| if (access_thread!=NULL) { | |
| obey_ar(SuspendThread(access_thread)==~0U); | |
| obey_ar(ResumeThread(access_thread)==~0U); | |
| ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n"); | |
| } | |
| } | |
| /* Double check that the thread really is suspended */ | |
| ok((error=GetExitCodeThread(thread,&exitCode))!=0 && exitCode==STILL_ACTIVE, | |
| "Thread did not really suspend\n"); | |
| /* Resume the thread, and make sure it actually completes */ | |
| ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n"); | |
| ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0, | |
| "Thread did not resume\n"); | |
| if(error!=WAIT_OBJECT_0) { | |
| TerminateThread(thread,1); | |
| } | |
| /* Trying to suspend a terminated thread should fail */ | |
| error=SuspendThread(thread); | |
| ok(error==~0U, "wrong return code: %d\n", error); | |
| ok(GetLastError()==ERROR_ACCESS_DENIED || GetLastError()==ERROR_NO_MORE_ITEMS, "unexpected error code: %d\n", GetLastError()); | |
| ok(CloseHandle(thread)!=0,"CloseHandle Failed\n"); | |
| } | |
| /* Check that TerminateThread works properly | |
| */ | |
| static VOID test_TerminateThread(void) | |
| { | |
| HANDLE thread,access_thread,event; | |
| DWORD threadId,exitCode; | |
| event=CreateEventA(NULL,TRUE,FALSE,NULL); | |
| thread = CreateThread(NULL,0,threadFunc4, | |
| (LPVOID)event, 0,&threadId); | |
| ok(thread!=NULL,"Create Thread failed\n"); | |
| /* TerminateThread has a race condition in Wine. If the thread is terminated | |
| before it starts, it leaves a process behind. Therefore, we wait for the | |
| thread to signal that it has started. There is no easy way to force the | |
| race to occur, so we don't try to find it. | |
| */ | |
| ok(WaitForSingleObject(event,5000)==WAIT_OBJECT_0, | |
| "TerminateThread didn't work\n"); | |
| /* check that access restrictions are obeyed */ | |
| if (pOpenThread) { | |
| access_thread=pOpenThread(THREAD_ALL_ACCESS & (~THREAD_TERMINATE), | |
| 0,threadId); | |
| ok(access_thread!=NULL,"OpenThread returned an invalid handle\n"); | |
| if (access_thread!=NULL) { | |
| obey_ar(TerminateThread(access_thread,99)==0); | |
| ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n"); | |
| } | |
| } | |
| /* terminate a job and make sure it terminates */ | |
| ok(TerminateThread(thread,99)!=0,"TerminateThread failed\n"); | |
| ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0, | |
| "TerminateThread didn't work\n"); | |
| ok(GetExitCodeThread(thread,&exitCode)!=STILL_ACTIVE, | |
| "TerminateThread should not leave the thread 'STILL_ACTIVE'\n"); | |
| ok(exitCode==99, "TerminateThread returned invalid exit code\n"); | |
| ok(CloseHandle(thread)!=0,"Error Closing thread handle\n"); | |
| } | |
| /* Check if CreateThread obeys the specified stack size. This code does | |
| not work properly, and is currently disabled | |
| */ | |
| static VOID test_CreateThread_stack(void) | |
| { | |
| #if CHECK_STACK | |
| /* The only way I know of to test the stack size is to use alloca | |
| and __try/__except. However, this is probably not portable, | |
| and I couldn't get it to work under Wine anyhow. However, here | |
| is the code which should allow for testing that CreateThread | |
| respects the stack-size limit | |
| */ | |
| HANDLE thread; | |
| DWORD threadId,exitCode; | |
| SYSTEM_INFO sysInfo; | |
| sysInfo.dwPageSize=0; | |
| GetSystemInfo(&sysInfo); | |
| ok(sysInfo.dwPageSize>0,"GetSystemInfo should return a valid page size\n"); | |
| thread = CreateThread(NULL,sysInfo.dwPageSize, | |
| threadFunc5,&exitCode, | |
| 0,&threadId); | |
| ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0, | |
| "TerminateThread didn't work\n"); | |
| ok(exitCode==1,"CreateThread did not obey stack-size-limit\n"); | |
| ok(CloseHandle(thread)!=0,"CloseHandle failed\n"); | |
| #endif | |
| } | |
| /* Check whether setting/retrieving thread priorities works */ | |
| static VOID test_thread_priority(void) | |
| { | |
| HANDLE curthread,access_thread; | |
| DWORD curthreadId,exitCode; | |
| int min_priority=-2,max_priority=2; | |
| BOOL disabled,rc; | |
| int i; | |
| curthread=GetCurrentThread(); | |
| curthreadId=GetCurrentThreadId(); | |
| /* Check thread priority */ | |
| /* NOTE: on Win2k/XP priority can be from -7 to 6. All other platforms it | |
| is -2 to 2. However, even on a real Win2k system, using thread | |
| priorities beyond the -2 to 2 range does not work. If you want to try | |
| anyway, enable USE_EXTENDED_PRIORITIES | |
| */ | |
| ok(GetThreadPriority(curthread)==THREAD_PRIORITY_NORMAL, | |
| "GetThreadPriority Failed\n"); | |
| if (pOpenThread) { | |
| /* check that access control is obeyed */ | |
| access_thread=pOpenThread(THREAD_ALL_ACCESS & | |
| (~THREAD_QUERY_INFORMATION) & (~THREAD_SET_INFORMATION), | |
| 0,curthreadId); | |
| ok(access_thread!=NULL,"OpenThread returned an invalid handle\n"); | |
| if (access_thread!=NULL) { | |
| obey_ar(SetThreadPriority(access_thread,1)==0); | |
| obey_ar(GetThreadPriority(access_thread)==THREAD_PRIORITY_ERROR_RETURN); | |
| obey_ar(GetExitCodeThread(access_thread,&exitCode)==0); | |
| ok(CloseHandle(access_thread),"Error Closing thread handle\n"); | |
| } | |
| } | |
| #if USE_EXTENDED_PRIORITIES | |
| min_priority=-7; max_priority=6; | |
| #endif | |
| for(i=min_priority;i<=max_priority;i++) { | |
| ok(SetThreadPriority(curthread,i)!=0, | |
| "SetThreadPriority Failed for priority: %d\n",i); | |
| ok(GetThreadPriority(curthread)==i, | |
| "GetThreadPriority Failed for priority: %d\n",i); | |
| } | |
| ok(SetThreadPriority(curthread,THREAD_PRIORITY_TIME_CRITICAL)!=0, | |
| "SetThreadPriority Failed\n"); | |
| ok(GetThreadPriority(curthread)==THREAD_PRIORITY_TIME_CRITICAL, | |
| "GetThreadPriority Failed\n"); | |
| ok(SetThreadPriority(curthread,THREAD_PRIORITY_IDLE)!=0, | |
| "SetThreadPriority Failed\n"); | |
| ok(GetThreadPriority(curthread)==THREAD_PRIORITY_IDLE, | |
| "GetThreadPriority Failed\n"); | |
| ok(SetThreadPriority(curthread,0)!=0,"SetThreadPriority Failed\n"); | |
| /* Check that the thread priority is not changed if SetThreadPriority | |
| is called with a value outside of the max/min range */ | |
| SetThreadPriority(curthread,min_priority); | |
| SetLastError(0xdeadbeef); | |
| rc = SetThreadPriority(curthread,min_priority-1); | |
| ok(rc == FALSE, "SetThreadPriority passed with a bad argument\n"); | |
| ok(GetLastError() == ERROR_INVALID_PARAMETER, | |
| "SetThreadPriority error %d, expected ERROR_INVALID_PARAMETER (87)\n", GetLastError()); | |
| ok(GetThreadPriority(curthread)==min_priority, | |
| "GetThreadPriority didn't return min_priority\n"); | |
| SetThreadPriority(curthread,max_priority); | |
| SetLastError(0xdeadbeef); | |
| rc = SetThreadPriority(curthread,max_priority+1); | |
| ok(rc == FALSE, "SetThreadPriority passed with a bad argument\n"); | |
| ok(GetLastError() == ERROR_INVALID_PARAMETER, | |
| "SetThreadPriority error %d, expected ERROR_INVALID_PARAMETER (87)\n", GetLastError()); | |
| ok(GetThreadPriority(curthread)==max_priority, | |
| "GetThreadPriority didn't return max_priority\n"); | |
| /* Check thread priority boost */ | |
| if (!pGetThreadPriorityBoost || !pSetThreadPriorityBoost) | |
| return; /* Win9x */ | |
| SetLastError(0xdeadbeef); | |
| rc=pGetThreadPriorityBoost(curthread,&disabled); | |
| if (rc==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED) | |
| return; /* WinME */ | |
| todo_wine | |
| ok(rc!=0,"error=%d\n",GetLastError()); | |
| if (pOpenThread) { | |
| /* check that access control is obeyed */ | |
| access_thread=pOpenThread(THREAD_ALL_ACCESS & | |
| (~THREAD_QUERY_INFORMATION) & (~THREAD_SET_INFORMATION), | |
| 0,curthreadId); | |
| ok(access_thread!=NULL,"OpenThread returned an invalid handle\n"); | |
| if (access_thread!=NULL) { | |
| obey_ar(pSetThreadPriorityBoost(access_thread,1)==0); | |
| obey_ar(pGetThreadPriorityBoost(access_thread,&disabled)==0); | |
| ok(CloseHandle(access_thread),"Error Closing thread handle\n"); | |
| } | |
| } | |
| todo_wine { | |
| rc = pSetThreadPriorityBoost(curthread,1); | |
| ok( rc != 0, "error=%d\n",GetLastError()); | |
| rc=pGetThreadPriorityBoost(curthread,&disabled); | |
| ok(rc!=0 && disabled==1, | |
| "rc=%d error=%d disabled=%d\n",rc,GetLastError(),disabled); | |
| rc = pSetThreadPriorityBoost(curthread,0); | |
| ok( rc != 0, "error=%d\n",GetLastError()); | |
| rc=pGetThreadPriorityBoost(curthread,&disabled); | |
| ok(rc!=0 && disabled==0, | |
| "rc=%d error=%d disabled=%d\n",rc,GetLastError(),disabled); | |
| } | |
| } | |
| /* check the GetThreadTimes function */ | |
| static VOID test_GetThreadTimes(void) | |
| { | |
| HANDLE thread,access_thread=NULL; | |
| FILETIME creationTime,exitTime,kernelTime,userTime; | |
| DWORD threadId; | |
| int error; | |
| thread = CreateThread(NULL,0,threadFunc2,NULL, | |
| CREATE_SUSPENDED,&threadId); | |
| ok(thread!=NULL,"Create Thread failed\n"); | |
| /* check that access control is obeyed */ | |
| if (pOpenThread) { | |
| access_thread=pOpenThread(THREAD_ALL_ACCESS & | |
| (~THREAD_QUERY_INFORMATION), 0,threadId); | |
| ok(access_thread!=NULL, | |
| "OpenThread returned an invalid handle\n"); | |
| } | |
| ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n"); | |
| ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0, | |
| "ResumeThread didn't work\n"); | |
| creationTime.dwLowDateTime=99; creationTime.dwHighDateTime=99; | |
| exitTime.dwLowDateTime=99; exitTime.dwHighDateTime=99; | |
| kernelTime.dwLowDateTime=99; kernelTime.dwHighDateTime=99; | |
| userTime.dwLowDateTime=99; userTime.dwHighDateTime=99; | |
| /* GetThreadTimes should set all of the parameters passed to it */ | |
| error=GetThreadTimes(thread,&creationTime,&exitTime, | |
| &kernelTime,&userTime); | |
| if (error!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) { | |
| ok(error!=0,"GetThreadTimes failed\n"); | |
| ok(creationTime.dwLowDateTime!=99 || creationTime.dwHighDateTime!=99, | |
| "creationTime was invalid\n"); | |
| ok(exitTime.dwLowDateTime!=99 || exitTime.dwHighDateTime!=99, | |
| "exitTime was invalid\n"); | |
| ok(kernelTime.dwLowDateTime!=99 || kernelTime.dwHighDateTime!=99, | |
| "kernelTimewas invalid\n"); | |
| ok(userTime.dwLowDateTime!=99 || userTime.dwHighDateTime!=99, | |
| "userTime was invalid\n"); | |
| ok(CloseHandle(thread)!=0,"CloseHandle failed\n"); | |
| if(access_thread!=NULL) | |
| { | |
| error=GetThreadTimes(access_thread,&creationTime,&exitTime, | |
| &kernelTime,&userTime); | |
| obey_ar(error==0); | |
| } | |
| } | |
| if(access_thread!=NULL) { | |
| ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n"); | |
| } | |
| } | |
| /* Check the processor affinity functions */ | |
| /* NOTE: These functions should also be checked that they obey access control | |
| */ | |
| static VOID test_thread_processor(void) | |
| { | |
| HANDLE curthread,curproc; | |
| DWORD_PTR processMask,systemMask; | |
| SYSTEM_INFO sysInfo; | |
| int error=0; | |
| sysInfo.dwNumberOfProcessors=0; | |
| GetSystemInfo(&sysInfo); | |
| ok(sysInfo.dwNumberOfProcessors>0, | |
| "GetSystemInfo failed to return a valid # of processors\n"); | |
| /* Use the current Thread/process for all tests */ | |
| curthread=GetCurrentThread(); | |
| ok(curthread!=NULL,"GetCurrentThread failed\n"); | |
| curproc=GetCurrentProcess(); | |
| ok(curproc!=NULL,"GetCurrentProcess failed\n"); | |
| /* Check the Affinity Mask functions */ | |
| ok(GetProcessAffinityMask(curproc,&processMask,&systemMask)!=0, | |
| "GetProcessAffinityMask failed\n"); | |
| ok(SetThreadAffinityMask(curthread,processMask)==processMask, | |
| "SetThreadAffinityMask failed\n"); | |
| ok(SetThreadAffinityMask(curthread,processMask+1)==0, | |
| "SetThreadAffinityMask passed for an illegal processor\n"); | |
| /* NOTE: This only works on WinNT/2000/XP) */ | |
| if (pSetThreadIdealProcessor) { | |
| todo_wine { | |
| SetLastError(0); | |
| error=pSetThreadIdealProcessor(curthread,0); | |
| if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) { | |
| ok(error!=-1, "SetThreadIdealProcessor failed\n"); | |
| } | |
| } | |
| if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) { | |
| error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1); | |
| ok(error==-1, | |
| "SetThreadIdealProcessor succeeded with an illegal processor #\n"); | |
| todo_wine { | |
| error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS); | |
| ok(error==0, "SetThreadIdealProcessor returned an incorrect value\n"); | |
| } | |
| } | |
| } | |
| } | |
| static VOID test_GetThreadExitCode(void) | |
| { | |
| DWORD exitCode, threadid; | |
| DWORD GLE, ret; | |
| HANDLE thread; | |
| ret = GetExitCodeThread((HANDLE)0x2bad2bad,&exitCode); | |
| ok(ret==0, "GetExitCodeThread returned non zero value: %d\n", ret); | |
| GLE = GetLastError(); | |
| ok(GLE==ERROR_INVALID_HANDLE, "GetLastError returned %d (expected 6)\n", GLE); | |
| thread = CreateThread(NULL,0,threadFunc2,NULL,0,&threadid); | |
| ret = WaitForSingleObject(thread,100); | |
| ok(ret==WAIT_OBJECT_0, "threadFunc2 did not exit during 100 ms\n"); | |
| ret = GetExitCodeThread(thread,&exitCode); | |
| ok(ret==exitCode || ret==1, | |
| "GetExitCodeThread returned %d (expected 1 or %d)\n", ret, exitCode); | |
| ok(exitCode==99, "threadFunc2 exited with code %d (expected 99)\n", exitCode); | |
| ok(CloseHandle(thread)!=0,"Error closing thread handle\n"); | |
| } | |
| #ifdef __i386__ | |
| static int test_value = 0; | |
| static HANDLE event; | |
| static void WINAPI set_test_val( int val ) | |
| { | |
| test_value += val; | |
| } | |
| static DWORD WINAPI threadFunc6(LPVOID p) | |
| { | |
| SetEvent( event ); | |
| Sleep( 1000 ); | |
| test_value *= (int)p; | |
| return 0; | |
| } | |
| static void test_SetThreadContext(void) | |
| { | |
| CONTEXT ctx; | |
| int *stack; | |
| HANDLE thread; | |
| DWORD threadid; | |
| DWORD prevcount; | |
| SetLastError(0xdeadbeef); | |
| event = CreateEvent( NULL, TRUE, FALSE, NULL ); | |
| thread = CreateThread( NULL, 0, threadFunc6, (void *)2, 0, &threadid ); | |
| ok( thread != NULL, "CreateThread failed : (%d)\n", GetLastError() ); | |
| if (!thread) | |
| { | |
| trace("Thread creation failed, skipping rest of test\n"); | |
| return; | |
| } | |
| WaitForSingleObject( event, INFINITE ); | |
| SuspendThread( thread ); | |
| CloseHandle( event ); | |
| ctx.ContextFlags = CONTEXT_FULL; | |
| SetLastError(0xdeadbeef); | |
| ok( GetThreadContext( thread, &ctx ), "GetThreadContext failed : (%d)\n", GetLastError() ); | |
| /* simulate a call to set_test_val(10) */ | |
| stack = (int *)ctx.Esp; | |
| stack[-1] = 10; | |
| stack[-2] = ctx.Eip; | |
| ctx.Esp -= 2 * sizeof(int *); | |
| ctx.Eip = (DWORD)set_test_val; | |
| SetLastError(0xdeadbeef); | |
| ok( SetThreadContext( thread, &ctx ), "SetThreadContext failed : (%d)\n", GetLastError() ); | |
| SetLastError(0xdeadbeef); | |
| prevcount = ResumeThread( thread ); | |
| ok ( prevcount == 1, "Previous suspend count (%d) instead of 1, last error : (%d)\n", | |
| prevcount, GetLastError() ); | |
| WaitForSingleObject( thread, INFINITE ); | |
| ok( test_value == 20, "test_value %d instead of 20\n", test_value ); | |
| } | |
| #endif /* __i386__ */ | |
| static HANDLE finish_event; | |
| static LONG times_executed; | |
| static DWORD CALLBACK work_function(void *p) | |
| { | |
| LONG executed = InterlockedIncrement(×_executed); | |
| if (executed == 100) | |
| SetEvent(finish_event); | |
| return 0; | |
| } | |
| static void test_QueueUserWorkItem(void) | |
| { | |
| int i; | |
| DWORD wait_result; | |
| DWORD before, after; | |
| /* QueueUserWorkItem not present on win9x */ | |
| if (!pQueueUserWorkItem) return; | |
| finish_event = CreateEvent(NULL, TRUE, FALSE, NULL); | |
| before = GetTickCount(); | |
| for (i = 0; i < 100; i++) | |
| { | |
| BOOL ret = pQueueUserWorkItem(work_function, (void *)i, WT_EXECUTEDEFAULT); | |
| ok(ret, "QueueUserWorkItem failed with error %d\n", GetLastError()); | |
| } | |
| wait_result = WaitForSingleObject(finish_event, 10000); | |
| after = GetTickCount(); | |
| trace("100 QueueUserWorkItem calls took %dms\n", after - before); | |
| ok(wait_result == WAIT_OBJECT_0, "wait failed with error 0x%x\n", wait_result); | |
| ok(times_executed == 100, "didn't execute all of the work items\n"); | |
| } | |
| START_TEST(thread) | |
| { | |
| HINSTANCE lib; | |
| int argc; | |
| char **argv; | |
| argc = winetest_get_mainargs( &argv ); | |
| /* Neither Cygwin nor mingW export OpenThread, so do a dynamic check | |
| so that the compile passes | |
| */ | |
| lib=GetModuleHandleA("kernel32.dll"); | |
| ok(lib!=NULL,"Couldn't get a handle for kernel32.dll\n"); | |
| pGetThreadPriorityBoost=(GetThreadPriorityBoost_t)GetProcAddress(lib,"GetThreadPriorityBoost"); | |
| pOpenThread=(OpenThread_t)GetProcAddress(lib,"OpenThread"); | |
| pQueueUserWorkItem=(QueueUserWorkItem_t)GetProcAddress(lib,"QueueUserWorkItem"); | |
| pSetThreadIdealProcessor=(SetThreadIdealProcessor_t)GetProcAddress(lib,"SetThreadIdealProcessor"); | |
| pSetThreadPriorityBoost=(SetThreadPriorityBoost_t)GetProcAddress(lib,"SetThreadPriorityBoost"); | |
| if (argc >= 3) | |
| { | |
| if (!strcmp(argv[2], "sleep")) | |
| { | |
| HANDLE hAddrEvents[2]; | |
| create_function_addr_events(hAddrEvents); | |
| SetEvent(hAddrEvents[0]); | |
| SetEvent(hAddrEvents[1]); | |
| Sleep(5000); /* spawned process runs for at most 5 seconds */ | |
| return; | |
| } | |
| while (1) | |
| { | |
| HANDLE hThread; | |
| hThread = CreateThread(NULL, 0, threadFunc2, NULL, 0, NULL); | |
| ok(hThread != NULL, "CreateThread failed, error %u\n", | |
| GetLastError()); | |
| ok(WaitForSingleObject(hThread, 200) == WAIT_OBJECT_0, | |
| "Thread did not exit in time\n"); | |
| if (hThread == NULL) break; | |
| CloseHandle(hThread); | |
| } | |
| return; | |
| } | |
| test_CreateRemoteThread(); | |
| test_CreateThread_basic(); | |
| test_CreateThread_suspended(); | |
| test_SuspendThread(); | |
| test_TerminateThread(); | |
| test_CreateThread_stack(); | |
| test_thread_priority(); | |
| test_GetThreadTimes(); | |
| test_thread_processor(); | |
| test_GetThreadExitCode(); | |
| #ifdef __i386__ | |
| test_SetThreadContext(); | |
| #endif | |
| test_QueueUserWorkItem(); | |
| } |