diff --git a/dlls/kernel/Makefile.in b/dlls/kernel/Makefile.in index 6c2d5379a3e..6ca156a12cf 100644 --- a/dlls/kernel/Makefile.in +++ b/dlls/kernel/Makefile.in @@ -41,7 +41,8 @@ EXTRASUBDIRS = \ CTESTS = \ tests/alloc.c \ - tests/directory.c + tests/directory.c \ + tests/thread.c PLTESTS = \ tests/atom.pl diff --git a/dlls/kernel/kernel32.spec b/dlls/kernel/kernel32.spec index ba07c8e2aef..48dc90794c2 100644 --- a/dlls/kernel/kernel32.spec +++ b/dlls/kernel/kernel32.spec @@ -940,7 +940,7 @@ debug_channels (comm console debugstr dll int resource stress thunk toolhelp @ stdcall SetConsoleInputExeNameW(ptr) SetConsoleInputExeNameW @ stdcall SetProcessAffinityMask(long long) SetProcessAffinityMask @ stdcall SetProcessPriorityBoost(long long) SetProcessPriorityBoost -@ stub SetThreadIdealProcessor +@ stdcall SetThreadIdealProcessor(long long) SetThreadIdealProcessor @ stdcall SetThreadPriorityBoost(long long) SetThreadPriorityBoost @ stdcall SetWaitableTimer(long ptr long ptr ptr long) SetWaitableTimer @ stub SignalObjectAndWait diff --git a/dlls/kernel/tests/.cvsignore b/dlls/kernel/tests/.cvsignore index 3925a6fa4d0..1c137671c4e 100644 --- a/dlls/kernel/tests/.cvsignore +++ b/dlls/kernel/tests/.cvsignore @@ -1,5 +1,6 @@ alloc.ok atom.ok directory.ok +thread.ok kernel32_test.spec.c testlist.c diff --git a/dlls/kernel/tests/thread.c b/dlls/kernel/tests/thread.c new file mode 100644 index 00000000000..08799c614e7 --- /dev/null +++ b/dlls/kernel/tests/thread.c @@ -0,0 +1,564 @@ +/* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "wine/test.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 the TerminateThread tests should be skipped */ +#define SKIP_TERMINATE 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 HANDLE WINAPI (*OPENTHREADPTR)(DWORD,BOOL,DWORD); +OPENTHREADPTR OpenThreadPtr; +HANDLE WINAPI OpenThreadDefault(DWORD dwDesiredAccess, + BOOL bInheritHandle, + DWORD dwThreadId) +{ + return (HANDLE)NULL; +} +/* define a check for whether we are running in Win2k or XP */ +#define WIN2K_PLUS(version) (version < 0x80000000 && (version & 0xFF) >= 5) + +/* Functions not tested yet: + AttachThreadInput + CreateRemoteThread + 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; + +/* Basic test that simulatneous threads can access shared memory, + that the thread local storage routines work correctly, and that + threads actually run concurrently +*/ +VOID WINAPI threadFunc1(t1Struct *tstruct) +{ + int i; +/* write our thread # into shared memory */ + tstruct->threadmem[tstruct->threadnum]=GetCurrentThreadId(); + ok(TlsSetValue(tlsIndex,(LPVOID)(tstruct->threadnum+1))!=0, + "TlsSetValue failed"); +/* 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;ithreadmem[i]==0) ; + } +/* Check that noone cahnged our tls memory */ + ok((DWORD)TlsGetValue(tlsIndex)-1==tstruct->threadnum, + "TlsGetValue failed"); + ExitThread(NUM_THREADS+tstruct->threadnum); +} + +VOID WINAPI threadFunc2() +{ + ExitThread(99); +} + +VOID WINAPI threadFunc3() +{ + HANDLE thread; + thread=GetCurrentThread(); + SuspendThread(thread); + ExitThread(99); +} + +VOID WINAPI threadFunc4() +{ + Sleep(99000); + ExitThread(0); +} + +#if CHECK_STACK +VOID WINAPI threadFunc5(DWORD *exitCode) +{ + SYSTEM_INFO sysInfo; + sysInfo.dwPageSize=0; + GetSystemInfo(&sysInfo); + *exitCode=0; + __TRY + { + alloca(2*sysInfo.dwPageSize); + } + __EXCEPT(1) { + *exitCode=1; + } + __ENDTRY + ExitThread(0); +} +#endif + +/* Check basic funcationality of CreateThread and Tls* functions */ +VOID test_CreateThread_basic(DWORD version) +{ + HANDLE thread[NUM_THREADS],event[NUM_THREADS]; + DWORD threadid[NUM_THREADS],curthreadId; + DWORD threadmem[NUM_THREADS]; + DWORD exitCode; + t1Struct tstruct[NUM_THREADS]; + int error; + int i,j; +/* Retrieve current Thread ID for later comparisons */ + curthreadId=GetCurrentThreadId(); +/* Allocate some local storage */ + ok((tlsIndex=TlsAlloc())!=TLS_OUT_OF_INDEXES,"TlsAlloc failed"); +/* Create events for thread synchronization */ + for(i=0;i0,"GetSystemInfo should return a valid page size"); + thread = CreateThread(NULL,sysInfo.dwPageSize, + (LPTHREAD_START_ROUTINE)threadFunc5,&exitCode, + 0,&threadId); + ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0, + "TerminateThread didn't work"); + ok(exitCode==1,"CreateThread did not obey stack-size-limit"); + ok(CloseHandle(thread)!=0,"CloseHandle failed"); +#endif +} + +/* Check whether setting/retreiving thread priorities works */ +VOID test_thread_priority(DWORD version) +{ + HANDLE curthread,access_thread; + DWORD curthreadId,exitCode; + int min_priority=-2,max_priority=2; + int i,error; + char msg1[80],msg2[80]; + + 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"); + + if(WIN2K_PLUS(version)) { +/* check that access control is obeyed */ + access_thread=OpenThreadPtr(THREAD_ALL_ACCESS & + (~THREAD_QUERY_INFORMATION) & (~THREAD_SET_INFORMATION), + 0,curthreadId); + todo_wine { + ok(access_thread!=(HANDLE)NULL,"OpenThread returned an invalid handle"); + if(access_thread!=(HANDLE)NULL) { + ok(SetThreadPriority(access_thread,1)==0, + "SetThreadPriority did not obey access restrictions"); + ok(GetThreadPriority(access_thread)==THREAD_PRIORITY_ERROR_RETURN, + "GetThreadPriority did not obey access restrictions"); + ok(SetThreadPriorityBoost(access_thread,1)==0, + "SetThreadPriorityBoost did not obey access restrictions"); + ok(GetThreadPriorityBoost(access_thread,&error)==0, + "GetThreadPriorityBoost did not obey access restrictions"); + ok(GetExitCodeThread(access_thread,&exitCode)==0, + "GetExitCodeThread did not obey access restrictions"); + ok(CloseHandle(access_thread),"Error Closing thread handle"); + } + } +#if USE_EXTENDED_PRIORITIES + min_priority=-7; max_priority=6; +#endif + } + for(i=min_priority;i<=max_priority;i++) { + sprintf(msg1,"SetThreadPriority Failed for priority: %d",i); + sprintf(msg2,"GetThreadPriority Failed for priority: %d",i); + ok(SetThreadPriority(curthread,i)!=0,msg1); + ok(GetThreadPriority(curthread)==i,msg2); + } + ok(SetThreadPriority(curthread,THREAD_PRIORITY_TIME_CRITICAL)!=0, + "SetThreadPriority Failed"); + ok(GetThreadPriority(curthread)==THREAD_PRIORITY_TIME_CRITICAL, + "GetThreadPriority Failed"); + ok(SetThreadPriority(curthread,THREAD_PRIORITY_IDLE)!=0, + "SetThreadPriority Failed"); + ok(GetThreadPriority(curthread)==THREAD_PRIORITY_IDLE, + "GetThreadPriority Failed"); + ok(SetThreadPriority(curthread,0)!=0,"SetThreadPriority Failed"); + +/* Check thread priority boost */ +/* NOTE: This only works on WinNT/2000/XP) */ + if(version < 0x80000000) { + todo_wine { + ok(SetThreadPriorityBoost(curthread,1)!=0, + "SetThreadPriorityBoost Failed"); + ok(GetThreadPriorityBoost(curthread,&error)!=0 && error==1, + "GetThreadPriorityBoost Failed"); + ok(SetThreadPriorityBoost(curthread,0)!=0, + "SetThreadPriorityBoost Failed"); + ok(GetThreadPriorityBoost(curthread,&error)!=0 && error==0, + "GetThreadPriorityBoost Failed"); + } + } +} + +/* check the GetThreadTimes function */ +VOID test_GetThreadTimes(DWORD version) +{ + HANDLE thread,access_thread=(HANDLE)NULL; + FILETIME creationTime,exitTime,kernelTime,userTime; + DWORD threadId; + int error; + + thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunc2,NULL, + CREATE_SUSPENDED,&threadId); + + ok(thread!=(HANDLE)NULL,"Create Thread failed."); +/* check that access control is obeyed */ + if(WIN2K_PLUS(version)) { + access_thread=OpenThreadPtr(THREAD_ALL_ACCESS & + (~THREAD_QUERY_INFORMATION), 0,threadId); + todo_wine { + ok(access_thread!=(HANDLE)NULL, + "OpenThread returned an invalid handle"); + } + } + ok(ResumeThread(thread)==1,"Resume thread returned an invalid value"); + ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0, + "ResumeThread didn't work"); + if(WIN2K_PLUS(version)) { + if(access_thread!=(HANDLE)NULL) { + error=GetThreadTimes(access_thread,&creationTime,&exitTime, + &kernelTime,&userTime); + ok(error==0, "GetThreadTimes did not obey access restrictions"); + ok(CloseHandle(access_thread)!=0,"CloseHandle Failed"); + } + } + 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 */ + todo_wine { + error=GetThreadTimes(thread,&creationTime,&exitTime, + &kernelTime,&userTime); + ok(error!=0,"GetThreadTimes failed"); + ok(creationTime.dwLowDateTime!=99 || creationTime.dwHighDateTime!=99, + "creationTime was invalid"); + ok(exitTime.dwLowDateTime!=99 || exitTime.dwHighDateTime!=99, + "exitTime was invalid"); + ok(kernelTime.dwLowDateTime!=99 || kernelTime.dwHighDateTime!=99, + "kernelTime was invalid"); + ok(userTime.dwLowDateTime!=99 || userTime.dwHighDateTime!=99, + "userTime was invalid"); + } + ok(CloseHandle(thread)!=0,"ClosewHandle failed"); +} + +/* Check the processor affinity functions */ +/* NOTE: These functions should also be checked that they obey access control +*/ +VOID test_thread_processor(DWORD version) +{ + HANDLE curthread,curproc; + DWORD 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"); +/* Use the current Thread/process for all tests */ + curthread=GetCurrentThread(); + ok(curthread!=(HANDLE)NULL,"GetCurrentThread failed"); + curproc=GetCurrentProcess(); + ok(curproc!=(HANDLE)NULL,"GetCurrentProcess failed"); +/* Check the Affinity Mask functions */ + ok(GetProcessAffinityMask(curproc,&processMask,&systemMask)!=0, + "GetProcessAffinityMask failed"); + ok(SetThreadAffinityMask(curthread,processMask)==1, + "SetThreadAffinityMask failed"); + ok(SetThreadAffinityMask(curthread,processMask+1)==0, + "SetThreadAffinityMask passed for an illegal processor"); +/* NOTE: This only works on WinNT/2000/XP) */ + if(version < 0x80000000) { + todo_wine { + error=SetThreadIdealProcessor(curthread,0); + ok(error!=-1, "SetThreadIdealProcessor failed"); + error=SetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1); + } + ok(error==-1, + "SetThreadIdealProccesor succeded with an illegal processor #"); + todo_wine { + error=SetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS); + ok(error==0, "SetThreadIdealProccesor returned an incorrect value"); + } + } +} + +START_TEST(thread) +{ + DWORD version; + HINSTANCE lib; + version=GetVersion(); +/* Neither Cygwin nor mingW export OpenThread, so do a dynamic check + so that the compile passes +*/ + lib=LoadLibraryA("kernel32"); + ok(lib!=(HANDLE)NULL,"Couldn't load kernel32.dll"); + OpenThreadPtr=(OPENTHREADPTR)GetProcAddress(lib,"OpenThread"); + if(OpenThreadPtr==NULL) { + OpenThreadPtr=&OpenThreadDefault; + } + test_CreateThread_basic(version); + test_CreateThread_suspended(version); + test_SuspendThread(version); +#if ! SKIP_TERMINATE + test_TerminateThread(version); +#endif + test_CreateThread_stack(version); + test_thread_priority(version); + test_GetThreadTimes(version); + test_thread_processor(version); +} diff --git a/include/winbase.h b/include/winbase.h index 038ee1e9301..559a3775248 100644 --- a/include/winbase.h +++ b/include/winbase.h @@ -1255,6 +1255,7 @@ DWORD WINAPI GetLongPathNameW(LPCWSTR,LPWSTR,DWORD); BOOL WINAPI GetNumberOfEventLogRecords(HANDLE,PDWORD); BOOL WINAPI GetOldestEventLogRecord(HANDLE,PDWORD); DWORD WINAPI GetPriorityClass(HANDLE); +BOOL WINAPI GetProcessAffinityMask(HANDLE,PDWORD,PDWORD); BOOL WINAPI GetProcessTimes(HANDLE,LPFILETIME,LPFILETIME,LPFILETIME,LPFILETIME); DWORD WINAPI GetProcessVersion(DWORD); BOOL WINAPI GetSecurityDescriptorControl(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR_CONTROL,LPDWORD); @@ -1416,6 +1417,7 @@ DWORD WINAPI SetTapePosition(HANDLE,DWORD,DWORD,DWORD,DWORD,BOOL); DWORD WINAPI SetThreadAffinityMask(HANDLE,DWORD); BOOL WINAPI SetThreadContext(HANDLE,const CONTEXT *); DWORD WINAPI SetThreadExecutionState(EXECUTION_STATE); +DWORD WINAPI SetThreadIdealProcessor(HANDLE,DWORD); BOOL WINAPI SetThreadPriority(HANDLE,INT); BOOL WINAPI SetThreadPriorityBoost(HANDLE,BOOL); BOOL WINAPI SetThreadToken(PHANDLE,HANDLE); diff --git a/include/winnt.h b/include/winnt.h index 76976e8c120..d39cce18d24 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -460,6 +460,7 @@ typedef HANDLE *PHANDLE, *LPHANDLE; #define PROCESSOR_ARM920 2336 /* 0x920 */ #define PROCESSOR_ARM_7TDMI 70001 +#define MAXIMUM_PROCESSORS 32 typedef struct _MEMORY_BASIC_INFORMATION { LPVOID BaseAddress; diff --git a/scheduler/thread.c b/scheduler/thread.c index 3df08075505..a8f1407d8df 100644 --- a/scheduler/thread.c +++ b/scheduler/thread.c @@ -549,6 +549,21 @@ DWORD WINAPI SetThreadAffinityMask( HANDLE hThread, DWORD dwThreadAffinityMask ) return ret; } +/********************************************************************** + * SetThreadIdealProcessor [KERNEL32.@] Obtains timing information. + * + * RETURNS + * Success: Value of last call to SetThreadIdealProcessor + * Failure: -1 + */ +DWORD WINAPI SetThreadIdealProcessor( + HANDLE hThread, /* [in] Specifies the thread of interest */ + DWORD dwIdealProcessor) /* [in] Specifies the new preferred processor */ +{ + FIXME("(0x%08x): stub\n",hThread); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return -1L; +} /********************************************************************** * TerminateThread [KERNEL32.@] Terminates a thread