| /* |
| * Unit test suite for thread 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 <assert.h> |
| #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 |
| |
| static BOOL (WINAPI *pGetThreadPriorityBoost)(HANDLE,PBOOL); |
| static HANDLE (WINAPI *pOpenThread)(DWORD,BOOL,DWORD); |
| static BOOL (WINAPI *pQueueUserWorkItem)(LPTHREAD_START_ROUTINE,PVOID,ULONG); |
| static DWORD (WINAPI *pSetThreadIdealProcessor)(HANDLE,DWORD); |
| static BOOL (WINAPI *pSetThreadPriorityBoost)(HANDLE,BOOL); |
| static BOOL (WINAPI *pRegisterWaitForSingleObject)(PHANDLE,HANDLE,WAITORTIMERCALLBACK,PVOID,ULONG,ULONG); |
| static BOOL (WINAPI *pUnregisterWait)(HANDLE); |
| static BOOL (WINAPI *pIsWow64Process)(HANDLE,PBOOL); |
| static BOOL (WINAPI *pSetThreadErrorMode)(DWORD,PDWORD); |
| static DWORD (WINAPI *pGetThreadErrorMode)(void); |
| static DWORD (WINAPI *pRtlGetThreadErrorMode)(void); |
| |
| static HANDLE create_target_process(const char *arg) |
| { |
| char **argv; |
| char cmdline[MAX_PATH]; |
| PROCESS_INFORMATION pi; |
| BOOL ret; |
| STARTUPINFO si = { 0 }; |
| si.cb = sizeof(si); |
| |
| winetest_get_mainargs( &argv ); |
| sprintf(cmdline, "%s %s %s", argv[0], argv[1], arg); |
| ret = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); |
| ok(ret, "error: %u\n", GetLastError()); |
| ret = CloseHandle(pi.hThread); |
| ok(ret, "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 |
| */ |
| |
| /* Functions to ensure that from a group of threads, only one executes |
| certain chunks of code at a time, and we know which one is executing |
| it. It basically makes multithreaded execution linear, which defeats |
| the purpose of multiple threads, but makes testing easy. */ |
| static HANDLE start_event, stop_event; |
| static LONG num_synced; |
| |
| static void init_thread_sync_helpers(void) |
| { |
| start_event = CreateEvent(NULL, TRUE, FALSE, NULL); |
| ok(start_event != NULL, "CreateEvent failed\n"); |
| stop_event = CreateEvent(NULL, TRUE, FALSE, NULL); |
| ok(stop_event != NULL, "CreateEvent failed\n"); |
| num_synced = -1; |
| } |
| |
| static BOOL sync_threads_and_run_one(DWORD sync_id, DWORD my_id) |
| { |
| LONG num = InterlockedIncrement(&num_synced); |
| assert(-1 <= num && num <= 1); |
| if (num == 1) |
| { |
| ResetEvent( stop_event ); |
| SetEvent( start_event ); |
| } |
| else |
| { |
| DWORD ret = WaitForSingleObject(start_event, 10000); |
| ok(ret == WAIT_OBJECT_0, "WaitForSingleObject failed %x\n",ret); |
| } |
| return sync_id == my_id; |
| } |
| |
| static void resync_after_run(void) |
| { |
| LONG num = InterlockedDecrement(&num_synced); |
| assert(-1 <= num && num <= 1); |
| if (num == -1) |
| { |
| ResetEvent( start_event ); |
| SetEvent( stop_event ); |
| } |
| else |
| { |
| DWORD ret = WaitForSingleObject(stop_event, 10000); |
| ok(ret == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); |
| } |
| } |
| |
| static void cleanup_thread_sync_helpers(void) |
| { |
| CloseHandle(start_event); |
| CloseHandle(stop_event); |
| } |
| |
| static 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. |
| */ |
| static 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 = p; |
| int i; |
| /* write our thread # into shared memory */ |
| tstruct->threadmem[tstruct->threadnum]=GetCurrentThreadId(); |
| ok(TlsSetValue(tlsIndex,(LPVOID)(INT_PTR)(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_PTR)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 = p; |
| if(event != NULL) { |
| SetEvent(event); |
| } |
| Sleep(99000); |
| return 0; |
| } |
| |
| #if CHECK_STACK |
| static DWORD WINAPI threadFunc5(LPVOID p) |
| { |
| DWORD *exitCode = 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(p); |
| return 0; |
| } |
| |
| static DWORD WINAPI threadFunc_CloseHandle(LPVOID p) |
| { |
| CloseHandle(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; |
| } |
| ok(ret == WAIT_OBJECT_0 || broken(ret == WAIT_OBJECT_0+1 /* nt4,w2k */), "WaitForAllObjects 2 events %d\n", ret); |
| |
| 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) |
| { |
| win_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, 1000); |
| 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, 1000); |
| ok(ret == WAIT_OBJECT_0, "object not signaled, ret=%u\n", ret); |
| |
| /* wait for thread end */ |
| ret = WaitForSingleObject(hThread, 1000); |
| 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, 1000); |
| 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, 1000); |
| 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 functionality 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; |
| DWORD tid; |
| BOOL bRet; |
| |
| /* 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 choose 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"); |
| } |
| |
| SetLastError(0xCAFEF00D); |
| bRet = TlsFree(tlsIndex); |
| ok(bRet, "TlsFree failed: %08x\n", GetLastError()); |
| ok(GetLastError()==0xCAFEF00D, |
| "GetLastError: expected 0xCAFEF00D, got %08x\n", GetLastError()); |
| |
| /* Test freeing an already freed TLS index */ |
| SetLastError(0xCAFEF00D); |
| ok(TlsFree(tlsIndex)==0,"TlsFree succeeded\n"); |
| ok(GetLastError()==ERROR_INVALID_PARAMETER, |
| "GetLastError: expected ERROR_INVALID_PARAMETER, got %08x\n", GetLastError()); |
| |
| /* Test how passing NULL as a pointer to threadid works */ |
| SetLastError(0xFACEaBAD); |
| thread[0] = CreateThread(NULL,0,threadFunc2,NULL,0,&tid); |
| 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; |
| DWORD suspend_count; |
| 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); |
| } |
| |
| suspend_count = SuspendThread(thread); |
| ok(suspend_count == -1, "SuspendThread returned %d, expected -1\n", suspend_count); |
| |
| suspend_count = ResumeThread(thread); |
| ok(suspend_count == 0 || |
| broken(suspend_count == -1), /* win9x */ |
| "ResumeThread returned %d, expected 0\n", suspend_count); |
| |
| 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,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 || |
| GetLastError() == ERROR_INVALID_PRIORITY /* Win9x */, |
| "SetThreadPriority error %d, expected ERROR_INVALID_PARAMETER or ERROR_INVALID_PRIORITY\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 || |
| GetLastError() == ERROR_INVALID_PRIORITY /* Win9x */, |
| "SetThreadPriority error %d, expected ERROR_INVALID_PARAMETER or ERROR_INVALID_PRIORITY\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) |
| { |
| win_skip("GetThreadPriorityBoost is not implemented on WinME\n"); |
| return; |
| } |
| |
| 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); |
| todo_wine 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) |
| win_skip("GetThreadTimes is not implemented\n"); |
| else { |
| 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,retMask; |
| SYSTEM_INFO sysInfo; |
| int error=0; |
| BOOL is_wow64; |
| |
| if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; |
| |
| 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: Pre-Vista does not recognize the "all processors" flag (all bits set) */ |
| retMask = SetThreadAffinityMask(curthread,~0); |
| ok(broken(retMask==0) || retMask==processMask, |
| "SetThreadAffinityMask(thread,-1) failed to request all processors.\n"); |
| if (retMask == processMask && sizeof(ULONG_PTR) > sizeof(ULONG)) |
| { |
| /* only the low 32-bits matter */ |
| retMask = SetThreadAffinityMask(curthread,~(ULONG_PTR)0); |
| ok(retMask == processMask, "SetThreadAffinityMask failed\n"); |
| retMask = SetThreadAffinityMask(curthread,~(ULONG_PTR)0 >> 3); |
| ok(retMask == processMask, "SetThreadAffinityMask failed\n"); |
| retMask = SetThreadAffinityMask(curthread,~(ULONG_PTR)1); |
| ok(retMask == 0, "SetThreadAffinityMask succeeded\n"); |
| } |
| /* NOTE: This only works on WinNT/2000/XP) */ |
| if (pSetThreadIdealProcessor) { |
| SetLastError(0xdeadbeef); |
| error=pSetThreadIdealProcessor(curthread,0); |
| if (GetLastError()==ERROR_CALL_NOT_IMPLEMENTED) |
| { |
| win_skip("SetThreadIdealProcessor is not implemented\n"); |
| return; |
| } |
| ok(error!=-1, "SetThreadIdealProcessor failed\n"); |
| |
| if (is_wow64) |
| { |
| SetLastError(0xdeadbeef); |
| error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1); |
| todo_wine |
| ok(error!=-1, "SetThreadIdealProcessor failed for %u on Wow64\n", MAXIMUM_PROCESSORS+1); |
| |
| SetLastError(0xdeadbeef); |
| error=pSetThreadIdealProcessor(curthread,65); |
| ok(error==-1, "SetThreadIdealProcessor succeeded with an illegal processor #\n"); |
| ok(GetLastError()==ERROR_INVALID_PARAMETER, |
| "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); |
| } |
| else |
| { |
| SetLastError(0xdeadbeef); |
| error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1); |
| ok(error==-1, "SetThreadIdealProcessor succeeded with an illegal processor #\n"); |
| ok(GetLastError()==ERROR_INVALID_PARAMETER, |
| "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); |
| } |
| |
| error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS); |
| ok(error!=-1, "SetThreadIdealProcessor failed\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; |
| ExitThread(0); |
| } |
| |
| 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; |
| BOOL ret; |
| |
| 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); |
| ret = GetThreadContext( thread, &ctx ); |
| ok( ret, "GetThreadContext failed : (%u)\n", GetLastError() ); |
| |
| if (ret) |
| { |
| /* 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); |
| ret = SetThreadContext( thread, &ctx ); |
| ok( ret, "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 == 10, "test_value %d\n", test_value ); |
| |
| ctx.ContextFlags = CONTEXT_FULL; |
| SetLastError(0xdeadbeef); |
| ret = GetThreadContext( thread, &ctx ); |
| ok( !ret, "GetThreadContext succeeded\n" ); |
| ok( GetLastError() == ERROR_GEN_FAILURE || broken(GetLastError() == ERROR_INVALID_HANDLE), /* win2k */ |
| "wrong error %u\n", GetLastError() ); |
| |
| SetLastError(0xdeadbeef); |
| ret = SetThreadContext( thread, &ctx ); |
| ok( !ret, "SetThreadContext succeeded\n" ); |
| ok( GetLastError() == ERROR_GEN_FAILURE || GetLastError() == ERROR_ACCESS_DENIED || |
| broken(GetLastError() == ERROR_INVALID_HANDLE), /* win2k */ |
| "wrong error %u\n", GetLastError() ); |
| |
| CloseHandle( thread ); |
| } |
| |
| #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_PTR 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"); |
| } |
| |
| static void CALLBACK signaled_function(PVOID p, BOOLEAN TimerOrWaitFired) |
| { |
| HANDLE event = p; |
| SetEvent(event); |
| ok(!TimerOrWaitFired, "wait shouldn't have timed out\n"); |
| } |
| |
| static void CALLBACK timeout_function(PVOID p, BOOLEAN TimerOrWaitFired) |
| { |
| HANDLE event = p; |
| SetEvent(event); |
| ok(TimerOrWaitFired, "wait should have timed out\n"); |
| } |
| |
| static void test_RegisterWaitForSingleObject(void) |
| { |
| BOOL ret; |
| HANDLE wait_handle; |
| HANDLE handle; |
| HANDLE complete_event; |
| |
| if (!pRegisterWaitForSingleObject || !pUnregisterWait) |
| { |
| win_skip("RegisterWaitForSingleObject or UnregisterWait not implemented\n"); |
| return; |
| } |
| |
| /* test signaled case */ |
| |
| handle = CreateEvent(NULL, TRUE, TRUE, NULL); |
| complete_event = CreateEvent(NULL, FALSE, FALSE, NULL); |
| |
| ret = pRegisterWaitForSingleObject(&wait_handle, handle, signaled_function, complete_event, INFINITE, WT_EXECUTEONLYONCE); |
| ok(ret, "RegisterWaitForSingleObject failed with error %d\n", GetLastError()); |
| |
| WaitForSingleObject(complete_event, INFINITE); |
| /* give worker thread chance to complete */ |
| Sleep(100); |
| |
| ret = pUnregisterWait(wait_handle); |
| ok(ret, "UnregisterWait failed with error %d\n", GetLastError()); |
| |
| /* test cancel case */ |
| |
| ResetEvent(handle); |
| |
| ret = pRegisterWaitForSingleObject(&wait_handle, handle, signaled_function, complete_event, INFINITE, WT_EXECUTEONLYONCE); |
| ok(ret, "RegisterWaitForSingleObject failed with error %d\n", GetLastError()); |
| |
| ret = pUnregisterWait(wait_handle); |
| ok(ret, "UnregisterWait failed with error %d\n", GetLastError()); |
| |
| /* test timeout case */ |
| |
| ret = pRegisterWaitForSingleObject(&wait_handle, handle, timeout_function, complete_event, 0, WT_EXECUTEONLYONCE); |
| ok(ret, "RegisterWaitForSingleObject failed with error %d\n", GetLastError()); |
| |
| WaitForSingleObject(complete_event, INFINITE); |
| /* give worker thread chance to complete */ |
| Sleep(100); |
| |
| ret = pUnregisterWait(wait_handle); |
| ok(ret, "UnregisterWait failed with error %d\n", GetLastError()); |
| } |
| |
| static DWORD TLS_main; |
| static DWORD TLS_index0, TLS_index1; |
| |
| static DWORD WINAPI TLS_InheritanceProc(LPVOID p) |
| { |
| /* We should NOT inherit the TLS values from our parent or from the |
| main thread. */ |
| LPVOID val; |
| |
| val = TlsGetValue(TLS_main); |
| ok(val == NULL, "TLS inheritance failed\n"); |
| |
| val = TlsGetValue(TLS_index0); |
| ok(val == NULL, "TLS inheritance failed\n"); |
| |
| val = TlsGetValue(TLS_index1); |
| ok(val == NULL, "TLS inheritance failed\n"); |
| |
| return 0; |
| } |
| |
| /* Basic TLS usage test. Make sure we can create slots and the values we |
| store in them are separate among threads. Also test TLS value |
| inheritance with TLS_InheritanceProc. */ |
| static DWORD WINAPI TLS_ThreadProc(LPVOID p) |
| { |
| LONG_PTR id = (LONG_PTR) p; |
| LPVOID val; |
| BOOL ret; |
| |
| if (sync_threads_and_run_one(0, id)) |
| { |
| TLS_index0 = TlsAlloc(); |
| ok(TLS_index0 != TLS_OUT_OF_INDEXES, "TlsAlloc failed\n"); |
| } |
| resync_after_run(); |
| |
| if (sync_threads_and_run_one(1, id)) |
| { |
| TLS_index1 = TlsAlloc(); |
| ok(TLS_index1 != TLS_OUT_OF_INDEXES, "TlsAlloc failed\n"); |
| |
| /* Slot indices should be different even if created in different |
| threads. */ |
| ok(TLS_index0 != TLS_index1, "TlsAlloc failed\n"); |
| |
| /* Both slots should be initialized to NULL */ |
| val = TlsGetValue(TLS_index0); |
| ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n"); |
| ok(val == NULL, "TLS slot not initialized correctly\n"); |
| |
| val = TlsGetValue(TLS_index1); |
| ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n"); |
| ok(val == NULL, "TLS slot not initialized correctly\n"); |
| } |
| resync_after_run(); |
| |
| if (sync_threads_and_run_one(0, id)) |
| { |
| val = TlsGetValue(TLS_index0); |
| ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n"); |
| ok(val == NULL, "TLS slot not initialized correctly\n"); |
| |
| val = TlsGetValue(TLS_index1); |
| ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n"); |
| ok(val == NULL, "TLS slot not initialized correctly\n"); |
| |
| ret = TlsSetValue(TLS_index0, (LPVOID) 1); |
| ok(ret, "TlsSetValue failed\n"); |
| |
| ret = TlsSetValue(TLS_index1, (LPVOID) 2); |
| ok(ret, "TlsSetValue failed\n"); |
| |
| val = TlsGetValue(TLS_index0); |
| ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n"); |
| ok(val == (LPVOID) 1, "TLS slot not initialized correctly\n"); |
| |
| val = TlsGetValue(TLS_index1); |
| ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n"); |
| ok(val == (LPVOID) 2, "TLS slot not initialized correctly\n"); |
| } |
| resync_after_run(); |
| |
| if (sync_threads_and_run_one(1, id)) |
| { |
| val = TlsGetValue(TLS_index0); |
| ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n"); |
| ok(val == NULL, "TLS slot not initialized correctly\n"); |
| |
| val = TlsGetValue(TLS_index1); |
| ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n"); |
| ok(val == NULL, "TLS slot not initialized correctly\n"); |
| |
| ret = TlsSetValue(TLS_index0, (LPVOID) 3); |
| ok(ret, "TlsSetValue failed\n"); |
| |
| ret = TlsSetValue(TLS_index1, (LPVOID) 4); |
| ok(ret, "TlsSetValue failed\n"); |
| |
| val = TlsGetValue(TLS_index0); |
| ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n"); |
| ok(val == (LPVOID) 3, "TLS slot not initialized correctly\n"); |
| |
| val = TlsGetValue(TLS_index1); |
| ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n"); |
| ok(val == (LPVOID) 4, "TLS slot not initialized correctly\n"); |
| } |
| resync_after_run(); |
| |
| if (sync_threads_and_run_one(0, id)) |
| { |
| HANDLE thread; |
| DWORD waitret, tid; |
| |
| val = TlsGetValue(TLS_index0); |
| ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n"); |
| ok(val == (LPVOID) 1, "TLS slot not initialized correctly\n"); |
| |
| val = TlsGetValue(TLS_index1); |
| ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n"); |
| ok(val == (LPVOID) 2, "TLS slot not initialized correctly\n"); |
| |
| thread = CreateThread(NULL, 0, TLS_InheritanceProc, 0, 0, &tid); |
| ok(thread != NULL, "CreateThread failed\n"); |
| waitret = WaitForSingleObject(thread, 60000); |
| ok(waitret == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); |
| CloseHandle(thread); |
| |
| ret = TlsFree(TLS_index0); |
| ok(ret, "TlsFree failed\n"); |
| } |
| resync_after_run(); |
| |
| if (sync_threads_and_run_one(1, id)) |
| { |
| ret = TlsFree(TLS_index1); |
| ok(ret, "TlsFree failed\n"); |
| } |
| resync_after_run(); |
| |
| return 0; |
| } |
| |
| static void test_TLS(void) |
| { |
| HANDLE threads[2]; |
| LONG_PTR i; |
| DWORD ret; |
| BOOL suc; |
| |
| init_thread_sync_helpers(); |
| |
| /* Allocate a TLS slot in the main thread to test for inheritance. */ |
| TLS_main = TlsAlloc(); |
| ok(TLS_main != TLS_OUT_OF_INDEXES, "TlsAlloc failed\n"); |
| suc = TlsSetValue(TLS_main, (LPVOID) 4114); |
| ok(suc, "TlsSetValue failed\n"); |
| |
| for (i = 0; i < 2; ++i) |
| { |
| DWORD tid; |
| |
| threads[i] = CreateThread(NULL, 0, TLS_ThreadProc, (LPVOID) i, 0, &tid); |
| ok(threads[i] != NULL, "CreateThread failed\n"); |
| } |
| |
| ret = WaitForMultipleObjects(2, threads, TRUE, 60000); |
| ok(ret == WAIT_OBJECT_0 || broken(ret == WAIT_OBJECT_0+1 /* nt4,w2k */), "WaitForAllObjects 2 threads %d\n",ret); |
| |
| for (i = 0; i < 2; ++i) |
| CloseHandle(threads[i]); |
| |
| suc = TlsFree(TLS_main); |
| ok(suc, "TlsFree failed\n"); |
| cleanup_thread_sync_helpers(); |
| } |
| |
| static void test_ThreadErrorMode(void) |
| { |
| DWORD oldmode; |
| DWORD mode; |
| DWORD rtlmode; |
| BOOL ret; |
| |
| if (!pSetThreadErrorMode || !pGetThreadErrorMode) |
| { |
| win_skip("SetThreadErrorMode and/or GetThreadErrorMode unavailable (added in Windows 7)\n"); |
| return; |
| } |
| |
| if (!pRtlGetThreadErrorMode) { |
| win_skip("RtlGetThreadErrorMode not available\n"); |
| return; |
| } |
| |
| oldmode = pGetThreadErrorMode(); |
| |
| ret = pSetThreadErrorMode(0, &mode); |
| ok(ret, "SetThreadErrorMode failed\n"); |
| ok(mode == oldmode, |
| "SetThreadErrorMode returned old mode 0x%x, expected 0x%x\n", |
| mode, oldmode); |
| mode = pGetThreadErrorMode(); |
| ok(mode == 0, "GetThreadErrorMode returned mode 0x%x, expected 0\n", mode); |
| rtlmode = pRtlGetThreadErrorMode(); |
| ok(rtlmode == 0, |
| "RtlGetThreadErrorMode returned mode 0x%x, expected 0\n", mode); |
| |
| ret = pSetThreadErrorMode(SEM_FAILCRITICALERRORS, &mode); |
| ok(ret, "SetThreadErrorMode failed\n"); |
| ok(mode == 0, |
| "SetThreadErrorMode returned old mode 0x%x, expected 0\n", mode); |
| mode = pGetThreadErrorMode(); |
| ok(mode == SEM_FAILCRITICALERRORS, |
| "GetThreadErrorMode returned mode 0x%x, expected SEM_FAILCRITICALERRORS\n", |
| mode); |
| rtlmode = pRtlGetThreadErrorMode(); |
| ok(rtlmode == 0x10, |
| "RtlGetThreadErrorMode returned mode 0x%x, expected 0x10\n", mode); |
| |
| ret = pSetThreadErrorMode(SEM_NOGPFAULTERRORBOX, &mode); |
| ok(ret, "SetThreadErrorMode failed\n"); |
| ok(mode == SEM_FAILCRITICALERRORS, |
| "SetThreadErrorMode returned old mode 0x%x, expected SEM_FAILCRITICALERRORS\n", |
| mode); |
| mode = pGetThreadErrorMode(); |
| ok(mode == SEM_NOGPFAULTERRORBOX, |
| "GetThreadErrorMode returned mode 0x%x, expected SEM_NOGPFAULTERRORBOX\n", |
| mode); |
| rtlmode = pRtlGetThreadErrorMode(); |
| ok(rtlmode == 0x20, |
| "RtlGetThreadErrorMode returned mode 0x%x, expected 0x20\n", mode); |
| |
| ret = pSetThreadErrorMode(SEM_NOOPENFILEERRORBOX, NULL); |
| ok(ret, "SetThreadErrorMode failed\n"); |
| mode = pGetThreadErrorMode(); |
| ok(mode == SEM_NOOPENFILEERRORBOX, |
| "GetThreadErrorMode returned mode 0x%x, expected SEM_NOOPENFILEERRORBOX\n", |
| mode); |
| rtlmode = pRtlGetThreadErrorMode(); |
| ok(rtlmode == 0x40, |
| "RtlGetThreadErrorMode returned mode 0x%x, expected 0x40\n", rtlmode); |
| |
| for (mode = 1; mode; mode <<= 1) |
| { |
| ret = pSetThreadErrorMode(mode, NULL); |
| if (mode & (SEM_FAILCRITICALERRORS | |
| SEM_NOGPFAULTERRORBOX | |
| SEM_NOOPENFILEERRORBOX)) |
| { |
| ok(ret, |
| "SetThreadErrorMode(0x%x,NULL) failed with error %d\n", |
| mode, GetLastError()); |
| } |
| else |
| { |
| DWORD GLE = GetLastError(); |
| ok(!ret, |
| "SetThreadErrorMode(0x%x,NULL) succeeded, expected failure\n", |
| mode); |
| ok(GLE == ERROR_INVALID_PARAMETER, |
| "SetThreadErrorMode(0x%x,NULL) failed with %d, " |
| "expected ERROR_INVALID_PARAMETER\n", |
| mode, GLE); |
| } |
| } |
| |
| pSetThreadErrorMode(oldmode, NULL); |
| } |
| |
| #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) |
| static inline void set_fpu_cw(WORD cw) |
| { |
| __asm__ volatile ("fnclex; fldcw %0" : : "m" (cw)); |
| } |
| |
| static inline WORD get_fpu_cw(void) |
| { |
| WORD cw = 0; |
| __asm__ volatile ("fnstcw %0" : "=m" (cw)); |
| return cw; |
| } |
| |
| struct fpu_thread_ctx |
| { |
| WORD cw; |
| HANDLE finished; |
| }; |
| |
| static DWORD WINAPI fpu_thread(void *param) |
| { |
| struct fpu_thread_ctx *ctx = param; |
| BOOL ret; |
| |
| ctx->cw = get_fpu_cw(); |
| |
| ret = SetEvent(ctx->finished); |
| ok(ret, "SetEvent failed, last error %#x.\n", GetLastError()); |
| |
| return 0; |
| } |
| |
| static WORD get_thread_fpu_cw(void) |
| { |
| struct fpu_thread_ctx ctx; |
| DWORD tid, res; |
| HANDLE thread; |
| |
| ctx.finished = CreateEvent(NULL, FALSE, FALSE, NULL); |
| ok(!!ctx.finished, "Failed to create event, last error %#x.\n", GetLastError()); |
| |
| thread = CreateThread(NULL, 0, fpu_thread, &ctx, 0, &tid); |
| ok(!!thread, "Failed to create thread, last error %#x.\n", GetLastError()); |
| |
| res = WaitForSingleObject(ctx.finished, INFINITE); |
| ok(res == WAIT_OBJECT_0, "Wait failed (%#x), last error %#x.\n", res, GetLastError()); |
| |
| res = CloseHandle(ctx.finished); |
| ok(!!res, "Failed to close event handle, last error %#x.\n", GetLastError()); |
| |
| return ctx.cw; |
| } |
| |
| static void test_thread_fpu_cw(void) |
| { |
| WORD initial_cw, cw; |
| |
| initial_cw = get_fpu_cw(); |
| ok(initial_cw == 0x27f, "Expected FPU control word 0x27f, got %#x.\n", initial_cw); |
| |
| cw = get_thread_fpu_cw(); |
| ok(cw == 0x27f, "Expected FPU control word 0x27f, got %#x.\n", cw); |
| |
| set_fpu_cw(0xf60); |
| cw = get_fpu_cw(); |
| ok(cw == 0xf60, "Expected FPU control word 0xf60, got %#x.\n", cw); |
| |
| cw = get_thread_fpu_cw(); |
| ok(cw == 0x27f, "Expected FPU control word 0x27f, got %#x.\n", cw); |
| |
| cw = get_fpu_cw(); |
| ok(cw == 0xf60, "Expected FPU control word 0xf60, got %#x.\n", cw); |
| |
| set_fpu_cw(initial_cw); |
| cw = get_fpu_cw(); |
| ok(cw == initial_cw, "Expected FPU control word %#x, got %#x.\n", initial_cw, cw); |
| } |
| #endif |
| |
| START_TEST(thread) |
| { |
| HINSTANCE lib; |
| HINSTANCE ntdll; |
| 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=(void *)GetProcAddress(lib,"GetThreadPriorityBoost"); |
| pOpenThread=(void *)GetProcAddress(lib,"OpenThread"); |
| pQueueUserWorkItem=(void *)GetProcAddress(lib,"QueueUserWorkItem"); |
| pSetThreadIdealProcessor=(void *)GetProcAddress(lib,"SetThreadIdealProcessor"); |
| pSetThreadPriorityBoost=(void *)GetProcAddress(lib,"SetThreadPriorityBoost"); |
| pRegisterWaitForSingleObject=(void *)GetProcAddress(lib,"RegisterWaitForSingleObject"); |
| pUnregisterWait=(void *)GetProcAddress(lib,"UnregisterWait"); |
| pIsWow64Process=(void *)GetProcAddress(lib,"IsWow64Process"); |
| pSetThreadErrorMode=(void *)GetProcAddress(lib,"SetThreadErrorMode"); |
| pGetThreadErrorMode=(void *)GetProcAddress(lib,"GetThreadErrorMode"); |
| |
| ntdll=GetModuleHandleA("ntdll.dll"); |
| if (ntdll) |
| { |
| pRtlGetThreadErrorMode=(void *)GetProcAddress(ntdll,"RtlGetThreadErrorMode"); |
| } |
| |
| 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; |
| DWORD tid; |
| hThread = CreateThread(NULL, 0, threadFunc2, NULL, 0, &tid); |
| 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(); |
| test_RegisterWaitForSingleObject(); |
| test_TLS(); |
| test_ThreadErrorMode(); |
| #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) |
| test_thread_fpu_cw(); |
| #endif |
| } |