summaryrefslogtreecommitdiff
path: root/src/pal/tests/palsuite/threading
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/tests/palsuite/threading')
-rw-r--r--src/pal/tests/palsuite/threading/CMakeLists.txt44
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/CMakeLists.txt6
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test1/test1.c95
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test2/test2.c84
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test2/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test3/test3.c219
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test3/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/CMakeLists.txt6
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test1/test1.c93
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test2/test2.c85
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test2/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test3/test3.c233
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test3/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/CreateMutexA.c345
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/testinfo.dat33
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/CreateMutexA.c331
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/testinfo.dat24
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CreateMutexW.c345
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/testinfo.dat33
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CreateMutexW.c340
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/testinfo.dat24
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test1/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test1/childProcess.c131
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test1/parentProcess.c201
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test1/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/childprocess.c69
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/parentprocess.c243
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/test2.h72
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/testinfo.dat20
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test1/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test1/childProcess.c150
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test1/parentProcess.c210
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test1/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test2/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test2/childprocess.c78
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.c245
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test2/test2.h31
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test2/testinfo.dat20
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/CMakeLists.txt6
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/CreateSemaphore.c322
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/testinfo.dat18
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/CreateSemaphore.c313
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/testinfo.dat18
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/createsemaphore.c200
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/testinfo.dat20
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/CMakeLists.txt6
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.c323
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/testinfo.dat18
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CreateSemaphore.c314
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/testinfo.dat18
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/createsemaphore.c201
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/testinfo.dat20
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/CMakeLists.txt6
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test1/test1.c119
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test2/test2.c184
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test2/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test3/test3.c101
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test3/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/CMakeLists.txt11
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/InitializeCriticalSection.c235
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/testinfo.dat21
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/test2.c226
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/testinfo.dat18
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/test3.c376
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/testinfo.dat29
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/test4.c241
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/test5.c187
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/testinfo.dat12
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/test6.c190
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/testinfo.dat12
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/test7.c188
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/testinfo.dat12
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/test8.c217
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/thistest.dat2
-rw-r--r--src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/test1.c145
-rw-r--r--src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/testlib.c47
-rw-r--r--src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/dllmain1.c66
-rw-r--r--src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/dllmain2.c66
-rw-r--r--src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/test2.c237
-rw-r--r--src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/testinfo.dat18
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/CMakeLists.txt15
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test1/test1.c152
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test1/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test10/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test10/test10.c239
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test10/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test11/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test11/childprocess.c74
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test11/myexitcode.h13
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test11/test11.c364
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test11/testinfo.dat19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test12/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test12/test12.c129
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test12/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test2/test2.c96
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test2/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test3/test3.c123
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test3/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test4/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test4/test4.c239
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test4/testinfo.dat19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test5/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test5/test5.c145
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test5/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test6/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test6/test6.c146
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test6/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test7/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test7/test7.c149
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test7/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test8/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test8/test8.c164
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test8/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test9/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test9/test9.c127
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test9/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/CMakeLists.txt6
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test1/ExitProcess.c32
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test2/test2.c30
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test2/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test3/test3.c40
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test3/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test1/test1.c114
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test2/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test2/childprocess.c41
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test2/myexitcode.h14
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test2/test2.c168
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test2/testinfo.dat19
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test3/dllmain.c65
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test3/test3.c162
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test3/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentProcess/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentProcess/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentProcess/test1/process.c40
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentProcess/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentProcessId/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/processId.c41
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThread/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThread/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThread/test1/testinfo.dat12
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThread/test1/thread.c93
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThread/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThread/test2/test2.c144
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThread/test2/testinfo.dat18
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThreadId/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/threadId.c82
-rw-r--r--src/pal/tests/palsuite/threading/GetExitCodeProcess/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/childProcess.c31
-rw-r--r--src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/myexitcode.h14
-rw-r--r--src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/test1.c163
-rw-r--r--src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/threading/GetProcessTimes/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/GetProcessTimes/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/GetProcessTimes/test2/test2.c122
-rw-r--r--src/pal/tests/palsuite/threading/GetProcessTimes/test2/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/GetThreadTimes/CMakeLists.txt3
-rw-r--r--src/pal/tests/palsuite/threading/GetThreadTimes/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/GetThreadTimes/test1/test1.c102
-rw-r--r--src/pal/tests/palsuite/threading/NamedMutex/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/NamedMutex/test1/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/NamedMutex/test1/namedmutex.cpp1058
-rw-r--r--src/pal/tests/palsuite/threading/NamedMutex/test1/nopal.cpp65
-rw-r--r--src/pal/tests/palsuite/threading/NamedMutex/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/CMakeLists.txt8
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test1/test1.c134
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test1/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test2/test2.c194
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test2/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test3/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test3/childprocess.c81
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test3/test3.c187
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test3/testinfo.dat21
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test4/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test4/test4.c112
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test4/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test5/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test5/test5.c197
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test5/testinfo.dat19
-rw-r--r--src/pal/tests/palsuite/threading/OpenProcess/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/OpenProcess/test1/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.c75
-rw-r--r--src/pal/tests/palsuite/threading/OpenProcess/test1/myexitcode.h14
-rw-r--r--src/pal/tests/palsuite/threading/OpenProcess/test1/test1.c282
-rw-r--r--src/pal/tests/palsuite/threading/OpenProcess/test1/testinfo.dat19
-rw-r--r--src/pal/tests/palsuite/threading/QueryThreadCycleTime/CMakeLists.txt3
-rw-r--r--src/pal/tests/palsuite/threading/QueryThreadCycleTime/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/QueryThreadCycleTime/test1/test1.c98
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/CMakeLists.txt10
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test1/test1.c313
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test1/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test2/test2.c224
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test2/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test3/test3.c33
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test3/testinfo.dat12
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test4/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test4/test4.c72
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test4/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test5/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test5/test5.c200
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test5/testinfo.dat20
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test6/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test6/test6.c129
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test6/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test7/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test7/test7.c253
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test7/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/ReleaseMutex/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/ReleaseMutex/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/ReleaseMutex/test3/ReleaseMutex.c103
-rw-r--r--src/pal/tests/palsuite/threading/ReleaseMutex/test3/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/CMakeLists.txt7
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test1/test1.c99
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test2/test2.c89
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test2/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test3/test3.c86
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test3/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test4/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test4/test4.c161
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test4/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/ResumeThread/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/ResumeThread/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/ResumeThread/test1/test1.c141
-rw-r--r--src/pal/tests/palsuite/threading/ResumeThread/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/SetErrorMode/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/SetErrorMode/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/SetErrorMode/test1/test1.c50
-rw-r--r--src/pal/tests/palsuite/threading/SetErrorMode/test1/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/CMakeLists.txt7
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test1/test1.c94
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test2/test2.c125
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test2/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test3/test3.c85
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test3/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test4/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test4/test4.c161
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test4/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/Sleep/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/threading/Sleep/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/Sleep/test1/Sleep.c80
-rw-r--r--src/pal/tests/palsuite/threading/Sleep/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/Sleep/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/Sleep/test2/sleep.c73
-rw-r--r--src/pal/tests/palsuite/threading/Sleep/test2/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/SleepEx/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/threading/SleepEx/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/SleepEx/test1/test1.c89
-rw-r--r--src/pal/tests/palsuite/threading/SleepEx/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/SleepEx/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/SleepEx/test2/test2.c183
-rw-r--r--src/pal/tests/palsuite/threading/SleepEx/test2/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/SwitchToThread/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/SwitchToThread/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/SwitchToThread/test1/test1.c97
-rw-r--r--src/pal/tests/palsuite/threading/SwitchToThread/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/TLS/CMakeLists.txt9
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test1/TLS.c182
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test2/TLS.c66
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test2/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test3/TLS.c90
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test3/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test4/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test4/test4.c137
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test4/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test5/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test5/test5.c108
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test5/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/test.c190
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/TerminateProcess/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/TerminateProcess/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/TerminateProcess/test1/TerminateProcess.c41
-rw-r--r--src/pal/tests/palsuite/threading/TerminateProcess/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/ThreadPriority/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/ThreadPriority/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/ThreadPriority/test1/ThreadPriority.c83
-rw-r--r--src/pal/tests/palsuite/threading/ThreadPriority/test1/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjects/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/test1.c224
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/CMakeLists.txt9
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/test1.c226
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/test2.c188
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/test3.c106
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/test4.c101
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/commonconsts.h42
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/helper.c122
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.c506
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/testinfo.dat18
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/child6.c211
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/test6.c709
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/thistest.dat2
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/CMakeLists.txt11
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/WFSOExMutexTest.c214
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/WFSOExSemaphoreTest.c184
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/WFSOExThreadTest.c204
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/WFSOMutexTest.c184
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/ChildProcess.c50
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.c119
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOSemaphoreTest/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOSemaphoreTest/WFSOSemaphoreTest.c183
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/WFSOThreadTest.c179
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/test1/test1.c121
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/YieldProcessor/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/YieldProcessor/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/YieldProcessor/test1/test1.c92
-rw-r--r--src/pal/tests/palsuite/threading/YieldProcessor/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/releasesemaphore/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/releasesemaphore/test1/CMakeLists.txt19
-rw-r--r--src/pal/tests/palsuite/threading/releasesemaphore/test1/test.c68
-rw-r--r--src/pal/tests/palsuite/threading/releasesemaphore/test1/testinfo.dat15
399 files changed, 26394 insertions, 0 deletions
diff --git a/src/pal/tests/palsuite/threading/CMakeLists.txt b/src/pal/tests/palsuite/threading/CMakeLists.txt
new file mode 100644
index 0000000000..bd31d17579
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CMakeLists.txt
@@ -0,0 +1,44 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(CreateEventA)
+add_subdirectory(CreateEventW)
+add_subdirectory(CreateMutexA_ReleaseMutex)
+add_subdirectory(CreateMutexW_ReleaseMutex)
+add_subdirectory(CreateProcessA)
+add_subdirectory(CreateProcessW)
+add_subdirectory(CreateSemaphoreA_ReleaseSemaphore)
+add_subdirectory(CreateSemaphoreW_ReleaseSemaphore)
+add_subdirectory(CreateThread)
+add_subdirectory(CriticalSectionFunctions)
+add_subdirectory(DuplicateHandle)
+add_subdirectory(ExitProcess)
+add_subdirectory(ExitThread)
+add_subdirectory(GetCurrentProcess)
+add_subdirectory(GetCurrentProcessId)
+add_subdirectory(GetCurrentThread)
+add_subdirectory(GetCurrentThreadId)
+add_subdirectory(GetExitCodeProcess)
+add_subdirectory(GetProcessTimes)
+add_subdirectory(GetThreadTimes)
+add_subdirectory(NamedMutex)
+add_subdirectory(OpenEventW)
+add_subdirectory(OpenProcess)
+add_subdirectory(QueryThreadCycleTime)
+add_subdirectory(QueueUserAPC)
+add_subdirectory(ReleaseMutex)
+add_subdirectory(releasesemaphore)
+add_subdirectory(ResetEvent)
+add_subdirectory(ResumeThread)
+add_subdirectory(SetErrorMode)
+add_subdirectory(SetEvent)
+add_subdirectory(Sleep)
+add_subdirectory(SleepEx)
+add_subdirectory(SwitchToThread)
+add_subdirectory(TerminateProcess)
+add_subdirectory(ThreadPriority)
+add_subdirectory(TLS)
+add_subdirectory(WaitForMultipleObjects)
+add_subdirectory(WaitForMultipleObjectsEx)
+add_subdirectory(WaitForSingleObject)
+add_subdirectory(YieldProcessor)
+
diff --git a/src/pal/tests/palsuite/threading/CreateEventA/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateEventA/CMakeLists.txt
new file mode 100644
index 0000000000..1962ade358
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+
diff --git a/src/pal/tests/palsuite/threading/CreateEventA/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateEventA/test1/CMakeLists.txt
new file mode 100644
index 0000000000..da1232eea4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_createeventa_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createeventa_test1 coreclrpal)
+
+target_link_libraries(paltest_createeventa_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateEventA/test1/test1.c b/src/pal/tests/palsuite/threading/CreateEventA/test1/test1.c
new file mode 100644
index 0000000000..d8ef0f58a3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test1/test1.c
@@ -0,0 +1,95 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test1.c
+**
+** Purpose: Test for CreateEvent. Create an event, ensure the
+** HANDLE is valid. Then check to ensure that the object is in the
+** signaled state. Close the HANDLE and done.
+**
+**
+**=========================================================*/
+
+/*
+ Note: From the rotor_pal documentation:
+
+ lpEventAttributes will always be NULL, bManualReset can be either
+ TRUE or FALSE, bInitialState can be either TRUE or FALSE, the lpName
+ may be non-NULL
+
+*/
+
+
+#include <palsuite.h>
+
+BOOL CreateEventTest()
+{
+ BOOL bRet = FALSE;
+ DWORD dwRet = 0;
+
+ LPSECURITY_ATTRIBUTES lpEventAttributes = NULL;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = TRUE;
+
+ /* Call CreateEvent, and check to ensure the returned HANDLE is a
+ valid event HANDLE
+ */
+
+ HANDLE hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ NULL);
+
+ if (hEvent != NULL)
+ {
+ /* Wait for the Object (for 0 time) and ensure that it returns
+ the value indicating that the event is signaled.
+ */
+ dwRet = WaitForSingleObject(hEvent,0);
+
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Trace("CreateEventTest:WaitForSingleObject failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ /* If we make it here, and CloseHandle succeeds, then the
+ entire test has passed. Otherwise bRet will still show
+ failure
+ */
+ bRet = CloseHandle(hEvent);
+
+ if (!bRet)
+ {
+ Trace("CreateEventTest:CloseHandle failed (%x)\n", GetLastError());
+ }
+ }
+ }
+ else
+ {
+ Trace("CreateEventTest:CreateEvent failed (%x)\n", GetLastError());
+ }
+
+ return bRet;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!CreateEventTest())
+ {
+ Fail ("Test failed\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateEventA/test1/testinfo.dat b/src/pal/tests/palsuite/threading/CreateEventA/test1/testinfo.dat
new file mode 100644
index 0000000000..c0d169ccdc
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test1/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateEvent
+Name = Positive Test for CreateEvent
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Test for CreateEvent. Create an event, ensure the
+= HANDLE is valid. Then check to ensure that the object is in the
+= signaled state. Close the HANDLE and done.
diff --git a/src/pal/tests/palsuite/threading/CreateEventA/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateEventA/test2/CMakeLists.txt
new file mode 100644
index 0000000000..f87e5bea87
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_createeventa_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createeventa_test2 coreclrpal)
+
+target_link_libraries(paltest_createeventa_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateEventA/test2/test2.c b/src/pal/tests/palsuite/threading/CreateEventA/test2/test2.c
new file mode 100644
index 0000000000..a24d20eeea
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test2/test2.c
@@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test2.c
+**
+** Purpose: Test for CreateEvent. Create the event with the
+** initial state being not signaled. Check to ensure that it
+** times out when the event is triggered.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+BOOL CreateEventTest()
+{
+ BOOL bRet = FALSE;
+ DWORD dwRet = 0;
+
+ LPSECURITY_ATTRIBUTES lpEventAttributes = 0;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = FALSE;
+
+ /* Create an event with the Initial State set to FALSE */
+
+ HANDLE hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ NULL);
+
+ if (hEvent != NULL)
+ {
+ /* This should ensure that the object is reset, or
+ non-signaled.
+ */
+
+ dwRet = WaitForSingleObject(hEvent,0);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("CloseEventTest:WaitForSingleObject failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ /* At this point, we've tested the function with success.
+ So long as the HANDLE can be closed, this test should
+ pass.
+ */
+
+ bRet = CloseHandle(hEvent);
+
+ if (!bRet)
+ {
+ Trace("CloseEventTest:CloseHandle failed (%x)\n", GetLastError());
+ }
+ }
+ }
+ else
+ {
+ Trace("CloseEventTest:CreateEvent failed (%x)\n", GetLastError());
+ }
+
+ return bRet;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!CreateEventTest())
+ {
+ Fail ("Test failed\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateEventA/test2/testinfo.dat b/src/pal/tests/palsuite/threading/CreateEventA/test2/testinfo.dat
new file mode 100644
index 0000000000..4af65f18d8
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test2/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateEvent
+Name = Positive Test for CreateEvent
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Test for CreateEvent. Create the event with the
+= initial state being not signaled. Check to ensure that it
+= times out when the event is triggered.
diff --git a/src/pal/tests/palsuite/threading/CreateEventA/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateEventA/test3/CMakeLists.txt
new file mode 100644
index 0000000000..e33c404a79
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test3.c
+)
+
+add_executable(paltest_createeventa_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createeventa_test3 coreclrpal)
+
+target_link_libraries(paltest_createeventa_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateEventA/test3/test3.c b/src/pal/tests/palsuite/threading/CreateEventA/test3/test3.c
new file mode 100644
index 0000000000..56d107b22d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test3/test3.c
@@ -0,0 +1,219 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test3.c
+**
+** Purpose: Tests for CreateEventA. Create an event with an empty name,
+** create an event with the same name as an already created event
+** object.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+#define SWAPPTR ((VOID *)(-1))
+
+struct testCase
+{
+ LPSECURITY_ATTRIBUTES lpEventAttributes;
+ BOOL bManualReset;
+ BOOL bInitialState;
+ char lpName[MAX_PATH + 2];
+ DWORD dwNameLen;
+ DWORD lastError;
+ BOOL bResult;
+};
+
+struct testCase testCases[]=
+{
+ {0, TRUE, FALSE, "", 0, ERROR_SUCCESS, PASS},
+ {0, TRUE, FALSE, "", 5, ERROR_SUCCESS, PASS},
+ {0, TRUE, FALSE, "", 5, ERROR_ALREADY_EXISTS, PASS},
+ {0, TRUE, FALSE, "", 6, ERROR_INVALID_HANDLE, PASS},
+ {0, TRUE, FALSE, "", MAX_PATH - 1 - 60, ERROR_SUCCESS, PASS},
+ {0, TRUE, FALSE, "", MAX_PATH + 1, ERROR_FILENAME_EXCED_RANGE, PASS}
+};
+
+static HANDLE hEvent[sizeof(testCases)/sizeof(struct testCase)];
+
+DWORD result[sizeof(testCases)/sizeof(struct testCase)];
+
+int __cdecl main(int argc, char **argv)
+{
+
+ BOOL bRet = TRUE;
+ const char *nonEventName = "aaaaaa";
+ HANDLE hUnnamedEvent;
+ HANDLE hFMap;
+ DWORD dwRet;
+ int i;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ hUnnamedEvent = CreateEventA(0, TRUE, FALSE, NULL);
+
+ if ( NULL == hUnnamedEvent )
+ {
+ bRet = FALSE;
+ Trace ( "PALSUITE ERROR: CreateEventA (%d, %d, %d, NULL) call "
+ "returned NULL.\nGetLastError returned %u.\n", 0, TRUE, FALSE,
+ GetLastError());
+ }
+
+ if (!CloseHandle(hUnnamedEvent))
+ {
+ bRet = FALSE;
+ Trace("PALSUITE ERROR: CreateEventA: CloseHandle(%lp); call "
+ "failed\nGetLastError returned '%u'.\n", hUnnamedEvent,
+ GetLastError());
+ }
+
+ /* Create non-event with the same name as one of the testCases */
+ hFMap = CreateFileMappingA( SWAPPTR, NULL, PAGE_READONLY, 0, 1,
+ nonEventName );
+
+ if ( NULL == hFMap )
+ {
+ bRet = FALSE;
+ Trace ( "PALSUITE ERROR: CreateFileMapping (%p, %p, %d, %d, %d, %s)"
+ " call returned NULL.\nGetLastError returned %u.\n",
+ SWAPPTR, NULL, PAGE_READONLY, 0, 0, nonEventName,
+ GetLastError());
+ goto done;
+ }
+
+ /* Create Events */
+ for (i = 0; i < sizeof(testCases)/sizeof(struct testCase); i++)
+ {
+ /* create name */
+ memset (testCases[i].lpName, '\0', (MAX_PATH + 2));
+ memset (testCases[i].lpName, 'a', testCases[i].dwNameLen );
+
+ SetLastError(ERROR_SUCCESS);
+
+ hEvent[i] = CreateEventA( testCases[i].lpEventAttributes,
+ testCases[i].bManualReset,
+ testCases[i].bInitialState,
+ testCases[i].lpName);
+
+ if (hEvent[i] != INVALID_HANDLE_VALUE)
+ {
+ DWORD dwError = GetLastError();
+
+ if (dwError != testCases[i].lastError)
+ {
+ bRet = FALSE;
+ Trace ("PALSUITE ERROR:\nCreateEventA(%lp, %d, %d, %s)"
+ "\nGetLastError returned '%u', it should have returned"
+ "'%d' at index '%d'.\n", testCases[i].lpEventAttributes,
+ testCases[i].bManualReset, testCases[i].bInitialState,
+ testCases[i].lpName, dwError,
+ testCases[i].lastError, i);
+ }
+ if ( ERROR_FILENAME_EXCED_RANGE == testCases[i].lastError )
+ {
+ result [i] = 1;
+ }
+ if ( ERROR_INVALID_HANDLE == testCases[i].lastError )
+ {
+ result [i] = 1;
+ }
+ /*
+ * If we expected the testcase to FAIL and it passed,
+ * report an error.
+ */
+ if (testCases[i].bResult == FAIL)
+ {
+ bRet = FALSE;
+ Trace ("PALSUITE ERROR:\nCreateEventA(%lp, %d, %d, %s)"
+ "\nShould have returned INVALID_HANDLE_VALUE but "
+ "didn't at index '%d'.\n",
+ testCases[i].lpEventAttributes,
+ testCases[i].bManualReset,
+ testCases[i].bInitialState,
+ testCases[i].lpName, i);
+ }
+ /*
+ * If result hasn't been set already set it to 0 so all the
+ * resources will be freed.
+ */
+ if (!result[i] )
+ {
+ result[i] = 0;
+ }
+ }
+ else
+ {
+ /*
+ * If we get an INVALID_HANDLE_VALUE and we expected the
+ * test case to pass, report an error.
+ */
+ result[i] = 1;
+
+ if (testCases[i].bResult == PASS)
+ {
+ bRet = FALSE;
+ Trace ("PALSUITE ERROR:\nCreateEventA(%lp, %d, %d, %s);"
+ "\nReturned INVALID_HANDLE_VALUE at index '%d'.\n",
+ testCases[i].lpEventAttributes,
+ testCases[i].bManualReset, testCases[i].bInitialState,
+ testCases[i].lpName, i);
+ }
+ }
+ }
+
+ /* cleanup */
+ for (i = 0; i < sizeof(testCases)/sizeof(struct testCase); i++)
+ {
+ if (result[i])
+ {
+ continue;
+ }
+ dwRet = WaitForSingleObject ( hEvent[i], 0 );
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ bRet = FALSE;
+ Trace("PALSUITE ERROR: CreateEventA:\nWaitForSingleObject (%lp, "
+ "%d) call failed at index %d .\nGetLastError returned "
+ "'%u'.\n", hEvent[i], 0, i, GetLastError());
+ }
+
+ if (!CloseHandle(hEvent[i]))
+ {
+ bRet = FALSE;
+ Trace("PALSUITE ERROR: CreateEventA: CloseHandle(%lp) call "
+ "failed at index %d\nGetLastError returned '%u'.\n",
+ hEvent[i], i, GetLastError());
+ }
+ }
+
+done:
+ if (!CloseHandle(hFMap))
+ {
+ bRet = FALSE;
+ Trace("PALSUITE ERROR: CreateEventW: CloseHandle(%p) call "
+ "failed\nGetLastError returned '%u'.\n", hFMap,
+ GetLastError());
+ }
+
+ if (FALSE == bRet)
+ {
+ bRet = FAIL;
+ }
+ else
+ {
+ bRet = PASS;
+ }
+
+ PAL_TerminateEx(bRet);
+
+ return(bRet);
+}
diff --git a/src/pal/tests/palsuite/threading/CreateEventA/test3/testinfo.dat b/src/pal/tests/palsuite/threading/CreateEventA/test3/testinfo.dat
new file mode 100644
index 0000000000..c3a344a2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test3/testinfo.dat
@@ -0,0 +1,16 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateEventA
+Name = Positive Test for CreateEventA
+TYPE = DEFAULT
+EXE1 = test3
+Description
+= Tests for CreateEventA. Create an unnamed event, create
+= an event with an empty name, create an event with a name longer than
+= MAX_PATH, MAX_PATH + 1, create an event with a name already taken
+= by a non-event object, create an event with a name already taken
+= by an event object.
diff --git a/src/pal/tests/palsuite/threading/CreateEventW/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateEventW/CMakeLists.txt
new file mode 100644
index 0000000000..1962ade358
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+
diff --git a/src/pal/tests/palsuite/threading/CreateEventW/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateEventW/test1/CMakeLists.txt
new file mode 100644
index 0000000000..0e8ba5bc2a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_createeventw_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createeventw_test1 coreclrpal)
+
+target_link_libraries(paltest_createeventw_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateEventW/test1/test1.c b/src/pal/tests/palsuite/threading/CreateEventW/test1/test1.c
new file mode 100644
index 0000000000..8d99e41934
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test1/test1.c
@@ -0,0 +1,93 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test1.c
+**
+** Purpose: Test for CreateEventW
+**
+**
+**=========================================================*/
+
+/*
+ * Note: From the rotor_pal documentation: lpEventAttributes will
+ * always be NULL, bManualReset can be either TRUE or FALSE,
+ * bInitialState can be either TRUE or FALSE, the lpName may be
+ * non-NULL.
+*/
+#define UNICODE
+#include <palsuite.h>
+
+BOOL CreateEventTest()
+{
+ BOOL bRet = FALSE;
+ DWORD dwRet = 0;
+
+ LPSECURITY_ATTRIBUTES lpEventAttributes = NULL;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = TRUE;
+
+ /*
+ * Call CreateEvent, and check to ensure the returned HANDLE is a
+ * valid event HANDLE
+ */
+
+ HANDLE hEvent = CreateEventW(lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ NULL);
+
+ if (hEvent != NULL)
+ {
+ /*
+ * Wait for the Object (for 0 time) and ensure that it returns
+ * the value indicating that the event is signaled.
+ */
+ dwRet = WaitForSingleObject(hEvent,0);
+
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Trace("CreateEventTest:WaitForSingleObject failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ /*
+ * If we make it here, and CloseHandle succeeds, then the
+ * entire test has passed. Otherwise bRet will still show
+ * failure
+ */
+ bRet = CloseHandle(hEvent);
+
+ if (!bRet)
+ {
+ Trace("CreateEventTest:CloseHandle failed (%x)\n", GetLastError());
+ }
+ }
+ }
+ else
+ {
+ Trace("CreateEventTest:CreateEvent failed (%x)\n", GetLastError());
+ }
+
+ return bRet;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!CreateEventTest())
+ {
+ Fail ("Test failed\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateEventW/test1/testinfo.dat b/src/pal/tests/palsuite/threading/CreateEventW/test1/testinfo.dat
new file mode 100644
index 0000000000..204ad1f4d4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test1/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateEventW
+Name = Positive Test for CreateEventW
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Test for CreateEventW. Create an event, ensure the
+= HANDLE is valid. Then check to ensure that the object is in the
+= signaled state. Close the HANDLE and done.
diff --git a/src/pal/tests/palsuite/threading/CreateEventW/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateEventW/test2/CMakeLists.txt
new file mode 100644
index 0000000000..f624377474
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_createeventw_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createeventw_test2 coreclrpal)
+
+target_link_libraries(paltest_createeventw_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateEventW/test2/test2.c b/src/pal/tests/palsuite/threading/CreateEventW/test2/test2.c
new file mode 100644
index 0000000000..4df218995a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test2/test2.c
@@ -0,0 +1,85 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test2.c
+**
+** Purpose: Test for CreateEventW. Create the event with the
+** initial state being not signaled. Check to ensure that it
+** times out when the event is triggered.
+**
+**
+**=========================================================*/
+#define UNICODE
+#include <palsuite.h>
+
+BOOL CreateEventTest()
+{
+ BOOL bRet = FALSE;
+ DWORD dwRet = 0;
+
+ LPSECURITY_ATTRIBUTES lpEventAttributes = 0;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = FALSE;
+
+
+ /* Create an event with the Initial State set to FALSE */
+
+ HANDLE hEvent = CreateEventW(lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ NULL);
+
+ if (hEvent != NULL)
+ {
+ /* This should ensure that the object is reset, or
+ non-signaled.
+ */
+
+ dwRet = WaitForSingleObject(hEvent,0);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("CloseEventTest:WaitForSingleObject failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ /* At this point, we've tested the function with success.
+ So long as the HANDLE can be closed, this test should
+ pass.
+ */
+
+ bRet = CloseHandle(hEvent);
+
+ if (!bRet)
+ {
+ Trace("CloseEventTest:CloseHandle failed (%x)\n", GetLastError());
+ }
+ }
+ }
+ else
+ {
+ Trace("CloseEventTest:CreateEvent failed (%x)\n", GetLastError());
+ }
+
+ return bRet;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!CreateEventTest())
+ {
+ Fail ("Test failed\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateEventW/test2/testinfo.dat b/src/pal/tests/palsuite/threading/CreateEventW/test2/testinfo.dat
new file mode 100644
index 0000000000..01f7519ae3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test2/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateEventW
+Name = Positive Test for CreateEventW
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Test for CreateEvent. Create the event with the
+= initial state being not signaled. Check to ensure that it
+= times out when the event is triggered.
diff --git a/src/pal/tests/palsuite/threading/CreateEventW/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateEventW/test3/CMakeLists.txt
new file mode 100644
index 0000000000..4493ba3872
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test3.c
+)
+
+add_executable(paltest_createeventw_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createeventw_test3 coreclrpal)
+
+target_link_libraries(paltest_createeventw_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateEventW/test3/test3.c b/src/pal/tests/palsuite/threading/CreateEventW/test3/test3.c
new file mode 100644
index 0000000000..22f0fcfc49
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test3/test3.c
@@ -0,0 +1,233 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test3.c
+**
+** Purpose: Tests for CreateEvent. Create an unnamed event, create
+** an event with an empty name, create an event with a name longer than
+** MAX_PATH, MAX_PATH + 1, create an event with a name already taken
+** by a non-event object, create an event with a name already taken
+** by an event object.
+**
+**
+**=========================================================*/
+#include <palsuite.h>
+
+#define SWAPPTR ((VOID *) (-1))
+
+struct testCase
+{
+ LPSECURITY_ATTRIBUTES lpEventAttributes;
+ BOOL bManualReset;
+ BOOL bInitialState;
+ WCHAR lpName[MAX_PATH + 2];
+ DWORD dwNameLen;
+ DWORD lastError;
+ BOOL bResult;
+};
+
+struct testCase testCases[]=
+{
+ {0, TRUE, FALSE, {'\0'}, 0, ERROR_SUCCESS, PASS},
+ {0, TRUE, FALSE, {'\0'}, 5, ERROR_SUCCESS, PASS},
+ {0, TRUE, FALSE, {'\0'}, 5, ERROR_ALREADY_EXISTS, PASS},
+ {0, TRUE, FALSE, {'\0'}, 6, ERROR_INVALID_HANDLE, PASS},
+ {0, TRUE, FALSE, {'\0'}, MAX_PATH - 1 - 60, ERROR_SUCCESS, PASS},
+ {0, TRUE, FALSE, {'\0'}, MAX_PATH - 60, ERROR_SUCCESS, PASS},
+};
+
+static HANDLE hEvent[sizeof(testCases)/sizeof(struct testCase)];
+
+DWORD result[sizeof(testCases)/sizeof(struct testCase)];
+
+int __cdecl main(int argc, char **argv)
+{
+
+ BOOL bRet = TRUE;
+ WCHAR nonEventName[] = {'a','a','a','a','a','a','\0'};
+ char name[MAX_PATH + 2];
+ WCHAR *wName;
+ HANDLE hFMap = NULL;
+ HANDLE hUnnamedEvent;
+ DWORD dwRet;
+ int i;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ hUnnamedEvent = CreateEventW(0, TRUE, FALSE, NULL);
+
+ if ( NULL == hUnnamedEvent )
+ {
+ bRet = FALSE;
+ Trace ( "PALSUITE ERROR: CreateEventW (%d, %d, %d, NULL) call "
+ "returned NULL.\nGetLastError returned %u.\n", 0, TRUE, FALSE,
+ GetLastError());
+ goto done;
+ }
+
+ if (!CloseHandle(hUnnamedEvent))
+ {
+ bRet = FALSE;
+ Trace("PALSUITE ERROR: CreateEventW: CloseHandle(%lp); call "
+ "failed\nGetLastError returned '%u'.\n", hUnnamedEvent,
+ GetLastError());
+ }
+
+ /* Create non-event with the same name as one of the testCases */
+ hFMap = CreateFileMappingW( SWAPPTR, NULL, PAGE_READONLY, 0, 1,
+ nonEventName );
+
+ if ( NULL == hFMap )
+ {
+ bRet = FALSE;
+ Trace ( "PALSUITE ERROR: CreateFileMapping (%p, %p, %d, %d, %d, %S)"
+ " call returned NULL.\nGetLastError returned %u\n",
+ SWAPPTR, NULL, PAGE_READONLY, 0, 0, nonEventName,
+ GetLastError());
+ }
+
+ /* Create Events */
+ for (i = 0; i < sizeof(testCases)/sizeof(struct testCase); i++)
+ {
+ /* create name */
+ memset (name, '\0', MAX_PATH + 2);
+ memset (name, 'a', testCases[i].dwNameLen );
+
+ wName = convert(name);
+
+ wcsncpy(testCases[i].lpName, wName,
+ testCases[i].dwNameLen);
+
+ free(wName);
+
+ SetLastError(ERROR_SUCCESS);
+
+ hEvent[i] = CreateEventW( testCases[i].lpEventAttributes,
+ testCases[i].bManualReset,
+ testCases[i].bInitialState,
+ testCases[i].lpName);
+
+ if (hEvent[i] != INVALID_HANDLE_VALUE)
+ {
+ DWORD dwError = GetLastError();
+
+ if (dwError != testCases[i].lastError)
+ {
+ bRet = FALSE;
+ Trace ("PALSUITE ERROR:\nCreateEvent(%lp, %d, %d, %S)"
+ "\nGetLastError returned '%u', it should have returned"
+ "'%d' at index '%d'.\n", testCases[i].lpEventAttributes,
+ testCases[i].bManualReset, testCases[i].bInitialState,
+ testCases[i].lpName, dwError,
+ testCases[i].lastError, i);
+ }
+ if ( ERROR_FILENAME_EXCED_RANGE == testCases[i].lastError )
+ {
+ result [i] = 1;
+ }
+ if ( ERROR_INVALID_HANDLE == testCases[i].lastError )
+ {
+ result [i] = 1;
+ }
+ /*
+ * If we expected the testcase to FAIL and it passed,
+ * report an error.
+ */
+ if (testCases[i].bResult == FAIL)
+ {
+ bRet = FALSE;
+ Trace ("PALSUITE ERROR:\nCreateEvent(%lp, %d, %d, %S)"
+ "\nShould have returned INVALID_HANDLE_VALUE but "
+ "didn't at index '%d'.\n",
+ testCases[i].lpEventAttributes,
+ testCases[i].bManualReset,
+ testCases[i].bInitialState,
+ testCases[i].lpName, i);
+ }
+ /*
+ * If result hasn't been set already set it to 0 so all the
+ * resources will be freed.
+ */
+ if (!result[i])
+ {
+ result[i] = 0;
+ }
+ }
+ else
+ {
+ /*
+ * If we get an INVALID_HANDLE_VALUE and we expected the
+ * test case to pass, report an error.
+ */
+ result[i] = 1;
+
+ if (testCases[i].bResult == PASS)
+ {
+ bRet = FALSE;
+ Trace ("PALSUITE ERROR:\nCreateEvent(%lp, %d, %d, %S);"
+ "\nReturned INVALID_HANDLE_VALUE at index '%d'.\n",
+ testCases[i].lpEventAttributes,
+ testCases[i].bManualReset, testCases[i].bInitialState,
+ testCases[i].lpName, i);
+ }
+ }
+ }
+
+ /* cleanup */
+ for (i = 0; i < sizeof(testCases)/sizeof(struct testCase); i++)
+ {
+ if (result[i])
+ {
+ continue;
+ }
+ dwRet = WaitForSingleObject ( hEvent[i], 0 );
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ bRet = FALSE;
+ Trace("PALSUITE ERROR: CreateEventW:\nWaitForSingleObject (%lp, "
+ "%d) call failed at index %d .\nGetLastError returned "
+ "'%u'.\n", hEvent[i], 0, i, GetLastError());
+ }
+
+ if (!CloseHandle(hEvent[i]))
+ {
+ bRet = FALSE;
+ Trace("PALSUITE ERROR: CreateEventW: CloseHandle(%lp) call "
+ "failed at index %d\nGetLastError returned '%u'.\n",
+ hEvent[i], i, GetLastError());
+ }
+ }
+
+done:
+ if (hFMap != NULL && !CloseHandle(hFMap))
+ {
+ bRet = FALSE;
+ Trace("PALSUITE ERROR: CreateEventW: CloseHandle(%p) call "
+ "failed\nGetLastError returned '%u'.\n", hFMap,
+ GetLastError());
+ }
+
+ if (FALSE == bRet)
+ {
+ bRet = FAIL;
+ }
+ else
+ {
+ bRet = PASS;
+ }
+
+ PAL_TerminateEx(bRet);
+
+ return(bRet);
+
+}
+
+
+
diff --git a/src/pal/tests/palsuite/threading/CreateEventW/test3/testinfo.dat b/src/pal/tests/palsuite/threading/CreateEventW/test3/testinfo.dat
new file mode 100644
index 0000000000..4776ed239f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test3/testinfo.dat
@@ -0,0 +1,16 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateEventW
+Name = Positive Test for CreateEventW
+TYPE = DEFAULT
+EXE1 = test3
+Description
+= Tests for CreateEventW. Create an unnamed event, create
+= an event with an empty name, create an event with a name longer than
+= MAX_PATH, MAX_PATH + 1, create an event with a name already taken
+= by a non-event object, create an event with a name already taken
+= by an event object.
diff --git a/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/CMakeLists.txt
new file mode 100644
index 0000000000..ef14ea5352
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+
diff --git a/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/CMakeLists.txt
new file mode 100644
index 0000000000..ffdf13228e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ CreateMutexA.c
+)
+
+add_executable(paltest_createmutexa_releasemutex_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createmutexa_releasemutex_test1 coreclrpal)
+
+target_link_libraries(paltest_createmutexa_releasemutex_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/CreateMutexA.c b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/CreateMutexA.c
new file mode 100644
index 0000000000..52bab351fb
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/CreateMutexA.c
@@ -0,0 +1,345 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CreateMutexA_ReleaseMutex/test1/CreateMutexA.c
+**
+** Purpose: This test case tests whether a Mutex object created
+** with CreateMutex really works by mutually excluding
+** threads from accessing a data structure at the same
+** time. Here we have a buffer that can be filled or
+** emptied, we use a Mutex object to ensure that one
+** operation cannot be started until the other is
+** finished. If one operation detects that the other
+** has not finished, it fails. There is a Producer
+** thread which will try to fill the buffer 25 times,
+** and a consumer thread which try to empty the buffer
+** 25 times. If either the fill or empty operations
+** fails because the Mutex failed to mutually exclude
+** them, the corresponding thread will set an error
+** flag and return. This will cause the test case to
+** fail.
+**
+** To increase the probability of identifying problems,
+** the Fill opeartion has been slowed down with a call
+** to Sleep. This ensures that one operation will try
+** to access the shared buffer while the other is in
+** progress.
+**
+** NOTE: this test case also serves as a test case for
+** WaitForSingleObject.
+**
+**
+** Dependencies: CreateThread
+** ReleaseMutex
+** WaitForSingleObject
+** WaitForMultipleObjects
+** Sleep
+** memset
+**
+
+**
+**=========================================================*/
+
+#define UNICODE
+#include <palsuite.h>
+
+/* Define some values that we will using many times */
+#define MAIN_BUF_SIZE 40
+#define NUM_OF_CYCLES 40
+
+/* Buffer Operation return codes */
+#define OP_OK 0
+#define OP_ERR 1
+#define OP_NONE 2
+
+
+HANDLE hMutex; /* handle to mutex */
+
+BOOL bProdErr; /* Producer error Flag */
+BOOL bConErr; /* Consumer error Flag */
+
+/* Test Buffer */
+char Buffer[MAIN_BUF_SIZE];
+
+/*
+ * EmptyBuffer implements the empty operation for test buffer.
+ */
+int
+EmptyBuffer()
+{
+ int i;
+
+ if ( WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED)
+ {
+ Fail("ERROR: WaitForSingleObject failed.\n");
+ }
+
+ /* Check to see if the buffer is already completely empty */
+ for (i=0; i<MAIN_BUF_SIZE && Buffer[i] == 0; i++);
+ if (i == MAIN_BUF_SIZE)
+ {
+ /* Its empty so just return */
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Fail("ERROR: ReleaseMutex Failed.\n");
+ }
+ return OP_NONE;
+ }
+
+ /* Its not empty so we must empty it. */
+ for (i=0; i<MAIN_BUF_SIZE; i++)
+ {
+ /* Check for empty slots if we find one then the */
+ /* fill operation did no finish. return an error */
+ if (Buffer[i] == 0)
+ {
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Fail("ERROR: ReleaseMutex Failed.\n");
+ }
+ return OP_ERR;
+ }
+
+ Buffer[i] = 0;
+ }
+
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Fail("ERROR: ReleaseMutex Failed.\n");
+ }
+ return OP_OK;
+}
+
+/*
+ * FillBuffer implements the fill operation for test buffer.
+ */
+int
+FillBuffer()
+{
+ int i;
+
+ if ( WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED)
+ {
+ Fail("ERROR: WaitForSingleObject failed.\n");
+ }
+
+ /* Check to see if the buffer is already completely full */
+ for (i=0; i<MAIN_BUF_SIZE && Buffer[i] != 0; i++);
+ if (i == MAIN_BUF_SIZE)
+ {
+ /* Its full so just return */
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Fail("ERROR: ReleaseMutex Failed.\n");
+ }
+ return OP_NONE;
+ }
+
+ /* Its not full so we must fill it. */
+ for (i=0; i<MAIN_BUF_SIZE; i++)
+ {
+ /* Check for filled slots if we find one then the */
+ /* empty operation did not finish. return an error */
+ if (Buffer[i] == 1)
+ {
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Fail("ERROR: ReleaseMutex Failed.\n");
+ }
+ return OP_ERR;
+ }
+
+ Buffer[i] = 1;
+ Sleep(10);
+ }
+
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Fail("ERROR: ReleaseMutex Failed.\n");
+ }
+ return OP_OK;
+}
+
+
+
+
+/*
+ * Producer thread function.
+ */
+DWORD
+Producer(LPVOID lpParam)
+{
+ int n = 0;
+ int ret;
+
+ while (n < NUM_OF_CYCLES)
+ {
+ if (bConErr == TRUE)
+ {
+ /* The consumer ran into an error so we'll stop */
+ return 0;
+ }
+
+ ret = FillBuffer();
+
+ if (ret == OP_OK)
+ {
+ n++;
+ }
+ else if (ret == OP_ERR)
+ {
+ bProdErr = TRUE;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Consumer thread function.
+ */
+DWORD Consumer( LPVOID lpParam )
+{
+ int n = 0;
+ int ret;
+
+ while (n < NUM_OF_CYCLES)
+ {
+ if (bProdErr == TRUE)
+ {
+ /* The consumer ran into an error so we'll stop */
+ return 0;
+ }
+
+ ret = EmptyBuffer();
+
+ if (ret == OP_OK)
+ {
+ n++;
+ }
+ else if (ret == OP_ERR)
+ {
+ bConErr = TRUE;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+
+int __cdecl main (int argc, char **argv)
+{
+ DWORD dwThreadId;
+ DWORD dwWaitRet;
+
+ HANDLE hThread1; /* handle to consumer thread */
+ HANDLE hThread2; /* handle to producer thread */
+ HANDLE handleArray[2];
+
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ /* Initialize our error flags */
+ bProdErr = FALSE;
+ bConErr = FALSE;
+
+ /*
+ * Initialize the Buffer to be empty
+ */
+ memset(Buffer, 0, MAIN_BUF_SIZE);
+
+ /*
+ * Create Mutex
+ */
+ hMutex = CreateMutexA (NULL, FALSE, NULL);
+
+ if (NULL == hMutex)
+ {
+ Fail("hMutex = CreateMutexA() - returned NULL\n"
+ "Failing Test.\nGetLastError returned %u\n", GetLastError());
+ }
+
+
+ /*
+ * Create the Producer thread
+ */
+ hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Producer,
+ 0, 0, &dwThreadId);
+
+ if ( NULL == hThread1 )
+ {
+ CloseHandle(hMutex);
+
+ Fail("CreateThread() returned NULL. Failing test.\n"
+ "GetLastError returned %u\n", GetLastError());
+ }
+
+ /*
+ * Create the Consumer thread
+ */
+ hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Consumer,
+ 0, 0, &dwThreadId);
+
+ if ( NULL == hThread2 )
+ {
+ CloseHandle(hMutex);
+
+ /* Set the error flag and give thread1 some time to exit */
+ bConErr = FALSE;
+ Sleep(250);
+
+ Fail("CreateThread() returned NULL. Failing test.\n"
+ "GetLastError returned %u\n", GetLastError());
+ }
+
+ /*
+ * Wait for both threads to complete (Max 45 Seconds)
+ */
+ handleArray[0] = hThread1;
+ handleArray[1] = hThread2;
+ dwWaitRet = WaitForMultipleObjects (2, handleArray, TRUE, 450000);
+ if (dwWaitRet == WAIT_FAILED)
+ {
+ Fail("ERROR: WaitForMultipleObjects failed.\n");
+ }
+ else if (dwWaitRet == WAIT_TIMEOUT)
+ {
+ /* Set the error flags and give the threads some time to exit */
+ bProdErr = FALSE;
+ bConErr = FALSE;
+ Sleep(250);
+
+ Fail("ERROR: Timeout interval exceeded.\n");
+ }
+
+ /*
+ * Clean up
+ */
+ if (CloseHandle(hThread1) == FALSE ||
+ CloseHandle(hThread2) == FALSE ||
+ CloseHandle(hMutex) == FALSE)
+ {
+ Fail("ERROR: CloseHandle failed.\n");
+ }
+
+
+ /*
+ * Check our error flags
+ */
+ if (bProdErr == TRUE || bConErr == TRUE)
+ {
+ Fail("ERROR: A collision occurred, so the mutex failed.\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/testinfo.dat b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/testinfo.dat
new file mode 100644
index 0000000000..829b7159ac
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/testinfo.dat
@@ -0,0 +1,33 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateMutexA / ReleaseMutex
+Name = Positive Test for CreateMutexA and ReleaseMutex
+TYPE = DEFAULT
+EXE1 = createmutexa
+Description
+= This test cases test whether a Mutex object created
+= with CreateMutexA really works by mutually excluding
+= threads from accessing a data structure at the same
+= time. Here we have a buffer that can be filled or
+= emptied, we use a Mutex object to ensure that one
+= operation cannot be started until the other is
+= finished. If one operation detects that the other
+= has not finished, it fails. There is a Producer
+= thread which will try to fill the buffer 25 times,
+= and a consumer thread which try to empty the buffer
+= 25 times. If either the fill or empty operations
+= fails because the Mutex failed to mutually exclude
+= then, the corresponding thread will set an error
+= flag and return. This will cause the test case to
+= fail.
+= To increase the probability of identifying problems,
+= the Fill opeartion has been slowed dowm with a call
+= to Sleep. This ensures that one operation will try
+= to access the shared buffer while the other is in
+= progress.
+= NOTE: this test case also serves as a test case for
+= WaitForSingleObject.
diff --git a/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/CMakeLists.txt
new file mode 100644
index 0000000000..925c5d41f6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ CreateMutexA.c
+)
+
+add_executable(paltest_createmutexa_releasemutex_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createmutexa_releasemutex_test2 coreclrpal)
+
+target_link_libraries(paltest_createmutexa_releasemutex_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/CreateMutexA.c b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/CreateMutexA.c
new file mode 100644
index 0000000000..3b83ba9674
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/CreateMutexA.c
@@ -0,0 +1,331 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CreateMutexA_ReleaseMutex/test2/CreateMutexA.c
+**
+** Purpose: This test case tests the following things
+** - Creation of named Mutexes
+** - Creating multiple handles to a single named Mutex
+** - Ensuring that these handles work interchangeably
+** - Setting bInitialOwnerFlag to TRUE will cause the
+** initial call to a Wait function on the same Mutex
+** to actually wait.
+** - Waiting on a Mutex that a thread already owns does
+** not block.
+** - Create Named mutex with empty string ("")
+** - Create Named mutex with string of MAX_LONGPATH length
+** - Calling RelaseMutex with invalid Mutex handles and
+** valid but unowned Mutexes.
+**
+** Dependencies: CreateThread
+** ReleaseMutex
+** WaitForSingleObject
+** CloseHandle
+** Sleep
+** memset
+**
+
+**
+**=========================================================*/
+
+#define UNICODE
+#include <palsuite.h>
+
+const char *szMutex = "MyMutex";
+const char *szEmpty = "";
+
+/* Function Prototypes */
+BOOL TestNamedMutex(const char *szMutexName);
+DWORD NamedMutexThread(LPVOID lpParam);
+BOOL NegativeReleaseMutexTests();
+
+struct ThreadData
+{
+ HANDLE hMutex;
+ BOOL bReturnCode;
+};
+typedef struct ThreadData THREADDATA;
+
+
+int __cdecl main (int argc, char **argv)
+{
+ BOOL bFailures = FALSE;
+ char *szMaxPath;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+
+ /*
+ * Test named Mutexes with ordinary string
+ */
+
+ if (!TestNamedMutex(szMutex))
+ {
+ bFailures = TRUE;
+ }
+
+
+ /*
+ * Test named Mutexes with empty ("") string
+ */
+
+ if (!TestNamedMutex(szEmpty))
+ {
+ bFailures = TRUE;
+ }
+
+
+ /*
+ * Test named Mutexes with string of length MAX_LONGPATHPATH
+ */
+
+ szMaxPath = (char *)malloc(MAX_LONGPATH+2);
+ memset(szMaxPath, 'A', MAX_LONGPATH-60);
+ szMaxPath[MAX_LONGPATH-60] = 0;
+
+ if (!TestNamedMutex(szMaxPath))
+ {
+ bFailures = TRUE;
+ }
+
+ free(szMaxPath);
+
+
+ /*
+ * Run some negative tests on ReleaseMutex
+ */
+
+ if (!NegativeReleaseMutexTests())
+ {
+ bFailures = TRUE;
+ }
+
+
+ /*
+ * If there were any failures, then abort with a call to Fail
+ */
+
+ if (bFailures == TRUE)
+ {
+ Fail("ERROR: There some failures in the Mutex tests.\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+}
+
+
+/*
+ * Testing Function
+ *
+ * Try to get multiple handles to a named Mutex and test
+ * to make sure they actually refer to same Mutex object.
+ */
+BOOL TestNamedMutex(const char *szMutexName)
+{
+ DWORD dwData;
+ HANDLE hMutex1;
+ HANDLE hMutex2;
+ HANDLE hThread;
+ THREADDATA threadData;
+
+ /* Create a mutex and take ownership immediately */
+ hMutex1 = CreateMutexA (NULL, TRUE, szMutexName);
+
+ if (NULL == hMutex1)
+ {
+ Trace("ERROR: CreateMutexA #1 failed. GetLastError returned %u\n",
+ GetLastError());
+ return FALSE;
+ }
+
+ /* Try to wait on the Mutex we just created. We should not block. */
+ if (WaitForSingleObject(hMutex1, 1000) == WAIT_TIMEOUT)
+ {
+ Trace("WaitForSingleObject blocked on a Mutex that we owned.\n");
+ return FALSE;
+ }
+ /* We have to call ReleaseMutex here because of the Wait */
+ if (ReleaseMutex(hMutex1) == FALSE)
+ {
+ Trace("ReleaseMutex Failed.\n");
+ return FALSE;
+ }
+
+ /* Get a second handle to the same mutex */
+ hMutex2 = CreateMutexA (NULL, FALSE, szMutexName);
+
+ if (NULL == hMutex2)
+ {
+ Trace("ERROR: CreateMutex #2 failed. GetLastError returned %u\n",
+ GetLastError());
+ free(szMutexName);
+ return FALSE;
+ }
+
+ /*
+ * Create a thread that will Wait on the second handle.
+ */
+ threadData.hMutex = hMutex2;
+ hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)NamedMutexThread,
+ (LPVOID)&threadData, 0, &dwData);
+
+ if (NULL == hThread)
+ {
+ Trace("ERROR: CreateThread failed. GetLastError returned %u\n",
+ GetLastError());
+ return FALSE;
+ }
+
+ /* Give the thread a little time to execute & wait*/
+ Sleep(500);
+
+ /* Signal the the first handle */
+ if (ReleaseMutex(hMutex1) == FALSE)
+ {
+ Trace("ReleaseMutex Failed.\n");
+ return FALSE;
+ }
+
+ /* Give the thread some time to finish */
+ Sleep(2000);
+
+ /* Clean Up */
+ if (CloseHandle(hMutex1) == FALSE ||
+ CloseHandle(hMutex2) == FALSE ||
+ CloseHandle(hThread) == FALSE)
+ {
+ Trace("ERROR: CloseHandle failed.\n");
+ return FALSE;
+ }
+
+ /* Check the return code to see if signalling the first */
+ /* Mutex handle woke up the thread which was Waiting on */
+ /* the second handle. */
+ if (threadData.bReturnCode != FALSE)
+ {
+ Trace("ERROR: The handles did not refer to the same Mutex object.\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Thread function used with above testing function.
+ */
+DWORD NamedMutexThread(LPVOID lpParam)
+{
+ BOOL bTimedOut = FALSE;
+ THREADDATA *lpThreadData = (THREADDATA *)lpParam;
+
+ /* Wait on the Mutex that was passed to us */
+ if (WaitForSingleObject(lpThreadData->hMutex, 10000) == WAIT_TIMEOUT)
+ {
+ /* The Mutex was not signaled in the allotted time */
+ bTimedOut = TRUE;
+ }
+ if (ReleaseMutex(lpThreadData->hMutex) == FALSE)
+ {
+ Trace("ERROR: ReleaseMutex failed.\n");
+ lpThreadData->bReturnCode = FALSE;
+ return 0;
+ }
+
+ /* Indicate whether we timed out Waiting on the Mutex */
+ lpThreadData->bReturnCode = bTimedOut;
+
+ return 0;
+}
+
+
+/*
+ * Testing Function
+ *
+ * Try some negative tests on ReleaseMutex
+ */
+BOOL NegativeReleaseMutexTests()
+{
+ HANDLE hMutex;
+ BOOL bRet;
+ BOOL bResults = TRUE;
+
+
+ /*
+ * Try calling ReleaseMutex on a null handle
+ */
+ hMutex = 0;
+ bRet = ReleaseMutex(hMutex);
+
+ if (bRet != 0)
+ {
+ Trace("Error: ReleaseMutex accepted null handle.\n");
+ bResults = FALSE;
+ }
+
+
+ /*
+ * Try calling ReleaseMutex on an handle that we don't own
+ */
+ hMutex = CreateMutexA (NULL, TRUE, NULL);
+ if (hMutex == 0)
+ {
+ Trace("Error: CreateMutex failed.\n");
+ bResults = FALSE;
+ }
+
+ bRet = ReleaseMutex(hMutex);
+ bRet = ReleaseMutex(hMutex);
+
+ if (bRet != FALSE)
+ {
+ Trace("Error: ReleaseMutex accepted unowned handle.\n");
+ bResults = FALSE;
+ }
+
+ if (CloseHandle(hMutex) == FALSE)
+ {
+ Trace("Error: CloseHandle failed.\n");
+ bResults = FALSE;
+ }
+
+
+
+ /*
+ * Try calling ReleaseMutex on an handle that has been closed
+ */
+ hMutex = CreateMutexA (NULL, TRUE, NULL);
+ if (hMutex == 0)
+ {
+ Trace("Error: CreateMutex failed.\n");
+ bResults = FALSE;
+ }
+
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Trace("Error: ReleaseMutex failed.\n");
+ bResults = FALSE;
+ }
+ if (CloseHandle(hMutex) == FALSE)
+ {
+ Trace("Error: CloseHandle failed.\n");
+ bResults = FALSE;
+ }
+
+ bRet = ReleaseMutex(hMutex);
+
+ if (bRet != FALSE)
+ {
+ Trace("Error: ReleaseMutex accepted invalid handle.\n");
+ bResults = FALSE;
+ }
+
+ return bResults;
+}
diff --git a/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/testinfo.dat b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/testinfo.dat
new file mode 100644
index 0000000000..7e37528c15
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/testinfo.dat
@@ -0,0 +1,24 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateMutexA / ReleaseMutex
+Name = Basic validity Tests for CreateMutexA / ReleaseMutex
+TYPE = DEFAULT
+EXE1 = createmutexa
+Description
+= This test case tests the following things
+= - Creation of named Mutexes
+= - Creating multiple handles to a single named Mutex
+= - Ensuring that these handles work interchangeably
+= - Setting bInitialOwnerFlag to TRUE will cause the
+= initial call to a Wait function on the same Mutex
+= to actually wait.
+= - Waiting on a Mutex that a thread already owns should
+= not block.
+= - Create Named mutex with empty string ("")
+= - Create Named mutex with string of MAX_PATH length
+= - Calling RelaseMutex with invalid Mutex handles and
+= valid but unowned Mutexes.
diff --git a/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/CMakeLists.txt
new file mode 100644
index 0000000000..ef14ea5352
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+
diff --git a/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CMakeLists.txt
new file mode 100644
index 0000000000..a73ee045a3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ CreateMutexW.c
+)
+
+add_executable(paltest_createmutexw_releasemutex_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createmutexw_releasemutex_test1 coreclrpal)
+
+target_link_libraries(paltest_createmutexw_releasemutex_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CreateMutexW.c b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CreateMutexW.c
new file mode 100644
index 0000000000..c21bfb6a50
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CreateMutexW.c
@@ -0,0 +1,345 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CreateMutexW_ReleaseMutex/test1/CreateMutexW.c
+**
+** Purpose: This test case tests whether a Mutex object created
+** with CreateMutex really works by mutually excluding
+** threads from accessing a data structure at the same
+** time. Here we have a buffer that can be filled or
+** emptied, we use a Mutex object to ensure that one
+** operation cannot be started until the other is
+** finished. If one operation detects that the other
+** has not finished, it fails. There is a Producer
+** thread which will try to fill the buffer 25 times,
+** and a consumer thread which try to empty the buffer
+** 25 times. If either the fill or empty operations
+** fails because the Mutex failed to mutually exclude
+** them, the corresponding thread will set an error
+** flag and return. This will cause the test case to
+** fail.
+**
+** To increase the probability of identifying problems,
+** the Fill opeartion has been slowed dowm with a call
+** to Sleep. This ensures that one operation will try
+** to access the shared buffer while the other is in
+** progress.
+**
+** NOTE: this test case also serves as a test case for
+** WaitForSingleObject.
+**
+**
+** Dependencies: CreateThread
+** ReleaseMutex
+** WaitForSingleObject
+** WaitForMultipleObjects
+** Sleep
+** memset
+**
+
+**
+**=========================================================*/
+
+#define UNICODE
+#include <palsuite.h>
+
+/* Define some values that we will using many times */
+#define MAIN_BUF_SIZE 40
+#define NUM_OF_CYCLES 40
+
+/* Buffer Operation return codes */
+#define OP_OK 0
+#define OP_ERR 1
+#define OP_NONE 2
+
+
+HANDLE hMutex; /* handle to mutex */
+
+BOOL bProdErr; /* Producer error Flag */
+BOOL bConErr; /* Consumer error Flag */
+
+/* Test Buffer */
+char Buffer[MAIN_BUF_SIZE];
+
+/*
+ * EmptyBuffer implements the empty operation for test buffer.
+ */
+int
+EmptyBuffer()
+{
+ int i;
+
+ if ( WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED)
+ {
+ Fail("ERROR: WaitForSingleObject failed.\n");
+ }
+
+ /* Check to see if the buffer is already completely empty */
+ for (i=0; i<MAIN_BUF_SIZE && Buffer[i] == 0; i++);
+ if (i == MAIN_BUF_SIZE)
+ {
+ /* Its empty so just return */
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Fail("ERROR: ReleaseMutex Failed.\n");
+ }
+ return OP_NONE;
+ }
+
+ /* Its not empty so we must empty it. */
+ for (i=0; i<MAIN_BUF_SIZE; i++)
+ {
+ /* Check for empty slots if we find one then the */
+ /* fill operation did no finish. return an error */
+ if (Buffer[i] == 0)
+ {
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Fail("ERROR: ReleaseMutex Failed.\n");
+ }
+ return OP_ERR;
+ }
+
+ Buffer[i] = 0;
+ }
+
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Fail("ERROR: ReleaseMutex Failed.\n");
+ }
+ return OP_OK;
+}
+
+/*
+ * FillBuffer implements the fill operation for test buffer.
+ */
+int
+FillBuffer()
+{
+ int i;
+
+ if ( WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED)
+ {
+ Fail("ERROR: WaitForSingleObject failed.\n");
+ }
+
+ /* Check to see if the buffer is already completely full */
+ for (i=0; i<MAIN_BUF_SIZE && Buffer[i] != 0; i++);
+ if (i == MAIN_BUF_SIZE)
+ {
+ /* Its full so just return */
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Fail("ERROR: ReleaseMutex Failed.\n");
+ }
+ return OP_NONE;
+ }
+
+ /* Its not full so we must fill it. */
+ for (i=0; i<MAIN_BUF_SIZE; i++)
+ {
+ /* Check for filled slots if we find one then the */
+ /* empty operation did not finish. return an error */
+ if (Buffer[i] == 1)
+ {
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Fail("ERROR: ReleaseMutex Failed.\n");
+ }
+ return OP_ERR;
+ }
+
+ Buffer[i] = 1;
+ Sleep(10);
+ }
+
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Fail("ERROR: ReleaseMutex Failed.\n");
+ }
+ return OP_OK;
+}
+
+
+
+
+/*
+ * Producer thread function.
+ */
+DWORD
+Producer(LPVOID lpParam)
+{
+ int n = 0;
+ int ret;
+
+ while (n < NUM_OF_CYCLES)
+ {
+ if (bConErr == TRUE)
+ {
+ /* The consumer ran into an error so we'll stop */
+ return 0;
+ }
+
+ ret = FillBuffer();
+
+ if (ret == OP_OK)
+ {
+ n++;
+ }
+ else if (ret == OP_ERR)
+ {
+ bProdErr = TRUE;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Consumer thread function.
+ */
+DWORD Consumer( LPVOID lpParam )
+{
+ int n = 0;
+ int ret;
+
+ while (n < NUM_OF_CYCLES)
+ {
+ if (bProdErr == TRUE)
+ {
+ /* The consumer ran into an error so we'll stop */
+ return 0;
+ }
+
+ ret = EmptyBuffer();
+
+ if (ret == OP_OK)
+ {
+ n++;
+ }
+ else if (ret == OP_ERR)
+ {
+ bConErr = TRUE;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+
+int __cdecl main (int argc, char **argv)
+{
+ DWORD dwThreadId;
+ DWORD dwWaitRet;
+
+ HANDLE hThread1; /* handle to consumer thread */
+ HANDLE hThread2; /* handle to producer thread */
+ HANDLE handleArray[2];
+
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ /* Initialize our error flags */
+ bProdErr = FALSE;
+ bConErr = FALSE;
+
+ /*
+ * Initialize the Buffer to be empty
+ */
+ memset(Buffer, 0, MAIN_BUF_SIZE);
+
+ /*
+ * Create Mutex
+ */
+ hMutex = CreateMutexW (NULL, FALSE, NULL);
+
+ if (NULL == hMutex)
+ {
+ Fail("hMutex = CreateMutexW() - returned NULL\n"
+ "Failing Test.\nGetLastError returned %u\n", GetLastError());
+ }
+
+
+ /*
+ * Create the Producer thread
+ */
+ hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Producer,
+ 0, 0, &dwThreadId);
+
+ if ( NULL == hThread1 )
+ {
+ CloseHandle(hMutex);
+
+ Fail("CreateThread() returned NULL. Failing test.\n"
+ "GetLastError returned %u\n", GetLastError());
+ }
+
+ /*
+ * Create the Consumer thread
+ */
+ hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Consumer,
+ 0, 0, &dwThreadId);
+
+ if ( NULL == hThread2 )
+ {
+ CloseHandle(hMutex);
+
+ /* Set the error flag and give thread1 some time to exit */
+ bConErr = FALSE;
+ Sleep(250);
+
+ Fail("CreateThread() returned NULL. Failing test.\n"
+ "GetLastError returned %u\n", GetLastError());
+ }
+
+ /*
+ * Wait for both threads to complete (Max 45 Seconds)
+ */
+ handleArray[0] = hThread1;
+ handleArray[1] = hThread2;
+ dwWaitRet = WaitForMultipleObjects (2, handleArray, TRUE, 450000);
+ if (dwWaitRet == WAIT_FAILED)
+ {
+ Fail("ERROR: WaitForMultipleObjects failed.\n");
+ }
+ else if (dwWaitRet == WAIT_TIMEOUT)
+ {
+ /* Set the error flags and give the threads some time to exit */
+ bProdErr = FALSE;
+ bConErr = FALSE;
+ Sleep(250);
+
+ Fail("ERROR: Timeout interval exceeded.\n");
+ }
+
+ /*
+ * Clean up
+ */
+ if (CloseHandle(hThread1) == FALSE ||
+ CloseHandle(hThread2) == FALSE ||
+ CloseHandle(hMutex) == FALSE)
+ {
+ Fail("ERROR: CloseHandle failed.\n");
+ }
+
+
+ /*
+ * Check our error flags
+ */
+ if (bProdErr == TRUE || bConErr == TRUE)
+ {
+ Fail("ERROR: A collision occurred, so the mutex failed.\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/testinfo.dat b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/testinfo.dat
new file mode 100644
index 0000000000..19ea934dff
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/testinfo.dat
@@ -0,0 +1,33 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateMutexW / ReleaseMutex
+Name = Positive Test for CreateMutexW and ReleaseMutex
+TYPE = DEFAULT
+EXE1 = createmutexw
+Description
+= This test cases test whether a Mutex object created
+= with CreateMutexW really works by mutually excluding
+= threads from accessing a data structure at the same
+= time. Here we have a buffer that can be filled or
+= emptied, we use a Mutex object to ensure that one
+= operation cannot be started until the other is
+= finished. If one operation detects that the other
+= has not finished, it fails. There is a Producer
+= thread which will try to fill the buffer 25 times,
+= and a consumer thread which try to empty the buffer
+= 25 times. If either the fill or empty operations
+= fails because the Mutex failed to mutually exclude
+= then, the corresponding thread will set an error
+= flag and return. This will cause the test case to
+= fail.
+= To increase the probability of identifying problems,
+= the Fill opeartion has been slowed dowm with a call
+= to Sleep. This ensures that one operation will try
+= to access the shared buffer while the other is in
+= progress.
+= NOTE: this test case also serves as a test case for
+= WaitForSingleObject.
diff --git a/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CMakeLists.txt
new file mode 100644
index 0000000000..b40569b3f5
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ CreateMutexW.c
+)
+
+add_executable(paltest_createmutexw_releasemutex_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createmutexw_releasemutex_test2 coreclrpal)
+
+target_link_libraries(paltest_createmutexw_releasemutex_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CreateMutexW.c b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CreateMutexW.c
new file mode 100644
index 0000000000..41b7798a6e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CreateMutexW.c
@@ -0,0 +1,340 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CreateMutexW_ReleaseMutex/test2/CreateMutexW.c
+**
+** Purpose: This test case tests the following things
+** - Creation of named Mutexes
+** - Creating multiple handles to a single named Mutex
+** - Ensuring that these handles work interchangeably
+** - Setting bInitialOwnerFlag to TRUE will cause the
+** initial call to a Wait function on the same Mutex
+** to actually wait.
+** - Waiting on a Mutex that a thread already owns does
+** not block.
+** - Create Named mutex with empty string ("")
+** - Create Named mutex with string of MAX_LONGPATH length
+** - Calling RelaseMutex with invalid Mutex handles and
+** valid but unowned Mutexes.
+**
+** Dependencies: CreateThread
+** ReleaseMutex
+** WaitForSingleObject
+** CloseHandle
+** Sleep
+** memset
+**
+
+**
+**=========================================================*/
+
+#define UNICODE
+#include <palsuite.h>
+
+const char *szMutex = "MyMutex";
+const char *szEmpty = "";
+
+/* Function Prototypes */
+BOOL TestNamedMutex(const char *szMutexName);
+DWORD NamedMutexThread(LPVOID lpParam);
+BOOL NegativeReleaseMutexTests();
+
+struct ThreadData
+{
+ HANDLE hMutex;
+ BOOL bReturnCode;
+};
+typedef struct ThreadData THREADDATA;
+
+
+int __cdecl main (int argc, char **argv)
+{
+ BOOL bFailures = FALSE;
+ char *szMaxPath;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+
+ /*
+ * Test named Mutexes with ordinary string
+ */
+
+ if (!TestNamedMutex(szMutex))
+ {
+ bFailures = TRUE;
+ }
+
+
+ /*
+ * Test named Mutexes with empty ("") string
+ */
+
+ if (!TestNamedMutex(szEmpty))
+ {
+ bFailures = TRUE;
+ }
+
+
+ /*
+ * Test named Mutexes with string of length MAX_LONGPATH
+ */
+
+ szMaxPath = (char *)malloc(MAX_LONGPATH+2);
+ memset(szMaxPath, 'A', MAX_LONGPATH-60);
+ szMaxPath[MAX_LONGPATH-60] = 0;
+
+ if (!TestNamedMutex(szMaxPath))
+ {
+ bFailures = TRUE;
+ }
+
+ free(szMaxPath);
+
+
+ /*
+ * Run some negative tests on ReleaseMutex
+ */
+
+ if (!NegativeReleaseMutexTests())
+ {
+ bFailures = TRUE;
+ }
+
+
+ /*
+ * If there were any failures, then abort with a call to Fail
+ */
+
+ if (bFailures == TRUE)
+ {
+ Fail("ERROR: There some failures in the Mutex tests.\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+}
+
+
+/*
+ * Testing Function
+ *
+ * Try to get multiple handles to a named Mutex and test
+ * to make sure they actually refer to same Mutex object.
+ */
+BOOL TestNamedMutex(const char *szMutexName)
+{
+ DWORD dwData;
+ HANDLE hMutex1;
+ HANDLE hMutex2;
+ HANDLE hThread;
+ WCHAR *swzMutexName;
+ THREADDATA threadData;
+
+ /* Convert the Mutex name to wide characters */
+ swzMutexName = convert((char *)szMutexName);
+
+ /* Create a mutex and take ownership immediately */
+ hMutex1 = CreateMutexW (NULL, TRUE, swzMutexName);
+
+ if (NULL == hMutex1)
+ {
+ Trace("ERROR: CreateMutex #1 failed. GetLastError returned %u\n",
+ GetLastError());
+ free(swzMutexName);
+ return FALSE;
+ }
+
+ /* Try to wait on the Mutex we just created. We should not block. */
+ if (WaitForSingleObject(hMutex1, 1000) == WAIT_TIMEOUT)
+ {
+ Trace("WaitForSingleObject blocked on a Mutex that we owned.\n");
+ free(swzMutexName);
+ return FALSE;
+ }
+ /* We have to call ReleaseMutex here because of the Wait */
+ if (ReleaseMutex(hMutex1) == FALSE)
+ {
+ Trace("ReleaseMutex Failed.\n");
+ return FALSE;
+ }
+
+ /* Get a second handle to the same mutex */
+ hMutex2 = CreateMutexW (NULL, FALSE, swzMutexName);
+
+ if (NULL == hMutex2)
+ {
+ Trace("ERROR: CreateMutex #2 failed. GetLastError returned %u\n",
+ GetLastError());
+ free(swzMutexName);
+ return FALSE;
+ }
+
+ /* Get rid of the wide character string */
+ free(swzMutexName);
+
+ /*
+ * Create a thread that will Wait on the second handle.
+ */
+ threadData.hMutex = hMutex2;
+ hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)NamedMutexThread,
+ (LPVOID)&threadData, 0, &dwData);
+
+ if (NULL == hThread)
+ {
+ Trace("ERROR: CreateThread failed. GetLastError returned %u\n",
+ GetLastError());
+ return FALSE;
+ }
+
+ /* Give the thread a little time to execute & wait*/
+ Sleep(500);
+
+ /* Signal the the first handle */
+ if (ReleaseMutex(hMutex1) == FALSE)
+ {
+ Trace("ReleaseMutex Failed.\n");
+ return FALSE;
+ }
+
+ /* Give the thread some time to finish */
+ Sleep(2000);
+
+ /* Clean Up */
+ if (CloseHandle(hMutex1) == FALSE ||
+ CloseHandle(hMutex2) == FALSE ||
+ CloseHandle(hThread) == FALSE)
+ {
+ Trace("ERROR: CloseHandle failed.\n");
+ return FALSE;
+ }
+
+ /* Check the return code to see if signalling the first */
+ /* Mutex handle woke up the thread which was Waiting on */
+ /* the second handle. */
+ if (threadData.bReturnCode != FALSE)
+ {
+ Trace("ERROR: The handles did not refer to the same Mutex object.\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Thread function used with above testing function.
+ */
+DWORD NamedMutexThread(LPVOID lpParam)
+{
+ BOOL bTimedOut = FALSE;
+ THREADDATA *lpThreadData = (THREADDATA *)lpParam;
+
+ /* Wait on the Mutex that was passed to us */
+ if (WaitForSingleObject(lpThreadData->hMutex, 10000) == WAIT_TIMEOUT)
+ {
+ /* The Mutex was not signaled in the allotted time */
+ bTimedOut = TRUE;
+ }
+ if (ReleaseMutex(lpThreadData->hMutex) == FALSE)
+ {
+ Trace("ERROR: ReleaseMutex failed.\n");
+ lpThreadData->bReturnCode = FALSE;
+ return 0;
+ }
+
+ /* Indicate whether we timed out Waiting on the Mutex */
+ lpThreadData->bReturnCode = bTimedOut;
+
+ return 0;
+}
+
+
+/*
+ * Testing Function
+ *
+ * Try some negative tests on ReleaseMutex
+ */
+BOOL NegativeReleaseMutexTests()
+{
+ HANDLE hMutex;
+ BOOL bRet;
+ BOOL bResults = TRUE;
+
+
+ /*
+ * Try calling ReleaseMutex on a null handle
+ */
+ hMutex = 0;
+ bRet = ReleaseMutex(hMutex);
+
+ if (bRet != 0)
+ {
+ Trace("Error: ReleaseMutex accepted null handle.\n");
+ bResults = FALSE;
+ }
+
+
+ /*
+ * Try calling ReleaseMutex on an handle that we don't own
+ */
+ hMutex = CreateMutexW (NULL, TRUE, NULL);
+ if (hMutex == 0)
+ {
+ Trace("Error: CreateMutex failed.\n");
+ bResults = FALSE;
+ }
+
+ bRet = ReleaseMutex(hMutex);
+ bRet = ReleaseMutex(hMutex);
+
+ if (bRet != FALSE)
+ {
+ Trace("Error: ReleaseMutex accepted unowned handle.\n");
+ bResults = FALSE;
+ }
+
+ if (CloseHandle(hMutex) == FALSE)
+ {
+ Trace("Error: CloseHandle failed.\n");
+ bResults = FALSE;
+ }
+
+
+
+ /*
+ * Try calling ReleaseMutex on an handle that has been closed
+ */
+ hMutex = CreateMutexW (NULL, TRUE, NULL);
+ if (hMutex == 0)
+ {
+ Trace("Error: CreateMutex failed.\n");
+ bResults = FALSE;
+ }
+
+ if (ReleaseMutex(hMutex) == FALSE)
+ {
+ Trace("Error: ReleaseMutex failed.\n");
+ bResults = FALSE;
+ }
+ if (CloseHandle(hMutex) == FALSE)
+ {
+ Trace("Error: CloseHandle failed.\n");
+ bResults = FALSE;
+ }
+
+ bRet = ReleaseMutex(hMutex);
+
+ if (bRet != FALSE)
+ {
+ Trace("Error: ReleaseMutex accepted invalid handle.\n");
+ bResults = FALSE;
+ }
+
+ return bResults;
+}
diff --git a/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/testinfo.dat b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/testinfo.dat
new file mode 100644
index 0000000000..c5769e3ad3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/testinfo.dat
@@ -0,0 +1,24 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateMutexW / ReleaseMutex
+Name = Basic validity Tests for CreateMutexW / ReleaseMutex
+TYPE = DEFAULT
+EXE1 = createmutexw
+Description
+= This test case tests the following things
+= - Creation of named Mutexes
+= - Creating multiple handles to a single named Mutex
+= - Ensuring that these handles work interchangeably
+= - Setting bInitialOwnerFlag to TRUE will cause the
+= initial call to a Wait function on the same Mutex
+= to actually wait.
+= - Waiting on a Mutex that a thread already owns should
+= not block.
+= - Create Named mutex with empty string ("")
+= - Create Named mutex with string of MAX_PATH length
+= - Calling RelaseMutex with invalid Mutex handles and
+= valid but unowned Mutexes.
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateProcessA/CMakeLists.txt
new file mode 100644
index 0000000000..ef14ea5352
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateProcessA/test1/CMakeLists.txt
new file mode 100644
index 0000000000..67e53edccd
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test1/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ parentProcess.c
+)
+
+add_executable(paltest_createprocessa_test1
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_createprocessa_test1 coreclrpal)
+
+target_link_libraries(paltest_createprocessa_test1
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ childProcess.c
+)
+
+add_executable(paltest_createprocessa_test1_child
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_createprocessa_test1_child coreclrpal)
+
+target_link_libraries(paltest_createprocessa_test1_child
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test1/childProcess.c b/src/pal/tests/palsuite/threading/CreateProcessA/test1/childProcess.c
new file mode 100644
index 0000000000..ccbb050c04
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test1/childProcess.c
@@ -0,0 +1,131 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CreateProcessA/test1/childprocess.c
+**
+** Purpose: Test to ensure CreateProcessA starts a new process. This test
+** launches a child process, and examines a file written by the child.
+** This code is the child code.
+**
+** Dependencies: GetCurrentDirectory
+** strlen
+** fopen
+** fclose
+** fprintf
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+const char *szCommonFileA = "childdata.tmp";
+
+const char *szPathDelimA = "\\";
+
+const char *szCommonStringA = "058d2d057111a313aa82401c2e856002\0";
+
+/*
+ * Take two wide strings representing file and directory names
+ * (dirName, fileName), join the strings with the appropriate path
+ * delimiter and populate a wide character buffer (absPathName) with
+ * the resulting string.
+ *
+ * Returns: The number of wide characters in the resulting string.
+ * 0 is returned on Error.
+ */
+int
+mkAbsoluteFilenameA (
+ LPSTR dirName,
+ DWORD dwDirLength,
+ LPCSTR fileName,
+ DWORD dwFileLength,
+ LPSTR absPathName )
+{
+ extern const char *szPathDelimA;
+
+ DWORD sizeDN, sizeFN, sizeAPN;
+
+ sizeDN = strlen( dirName );
+ sizeFN = strlen( fileName );
+ sizeAPN = (sizeDN + 1 + sizeFN + 1);
+
+ /* insure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */
+ if ( sizeAPN > _MAX_PATH )
+ {
+ return ( 0 );
+ }
+
+ strncpy(absPathName, dirName, dwDirLength +1);
+ strncpy(absPathName, szPathDelimA, 2);
+ strncpy(absPathName, fileName, dwFileLength +1);
+
+ return (sizeAPN);
+
+}
+
+int __cdecl main( int argc, char **argv )
+{
+
+ static FILE * fp;
+
+ DWORD dwFileLength;
+ DWORD dwDirLength;
+ DWORD dwSize;
+
+ char szDirNameA[_MAX_DIR];
+ char szAbsPathNameA[_MAX_PATH];
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ dwDirLength = GetCurrentDirectory( _MAX_PATH, szDirNameA );
+
+ if (0 == dwDirLength)
+ {
+ Fail ("GetCurrentDirectory call failed. Could not get "
+ "current working directory\n. Exiting.\n");
+ }
+
+ dwFileLength = strlen( szCommonFileA );
+
+ dwSize = mkAbsoluteFilenameA( szDirNameA, dwDirLength, szCommonFileA,
+ dwFileLength, szAbsPathNameA );
+
+ if (0 == dwSize)
+ {
+ Fail ("Palsuite Code: mkAbsoluteFilename() call failed. Could "
+ "not build absolute path name to file\n. Exiting.\n");
+ }
+
+ if ( NULL == ( fp = fopen ( szAbsPathNameA , "w+" ) ) )
+ {
+ /*
+ * A return value of NULL indicates an error condition or an
+ * EOF condition
+ */
+ Fail ("%s unable to open %s for writing. Exiting.\n", argv[0]
+ , szAbsPathNameA );
+ }
+
+ if ( 0 >= ( fprintf ( fp, "%s", szCommonStringA )))
+ {
+ Fail("%s unable to write to %s. Exiting.\n", argv[0]
+ , szAbsPathNameA );
+ }
+
+ if (0 != (fclose ( fp )))
+ {
+ Fail ("%s unable to close file %s. Pid may not be "
+ "written to file. Exiting.\n", argv[0], szAbsPathNameA );
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test1/parentProcess.c b/src/pal/tests/palsuite/threading/CreateProcessA/test1/parentProcess.c
new file mode 100644
index 0000000000..b0c5808a7e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test1/parentProcess.c
@@ -0,0 +1,201 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CreateProcessA/test1/parentprocess.c
+**
+** Purpose: Test to ensure CreateProcessA starts a new process. This test
+** launches a child process, and examines a file written by the child.
+** This process (the parent process) reads the file created by the child and
+** compares the value the child wrote to the file. (a const char *)
+**
+** Dependencies: GetCurrentDirectory
+** strlen
+** WaitForSingleObject
+** fopen
+** fclose
+** Fail
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+const char *szCommonFileA = "childdata.tmp";
+
+const char *szChildFileA = "paltest_createprocessa_test1_child";
+
+const char *szPathDelimA = "\\";
+
+const char *szCommonStringA = "058d2d057111a313aa82401c2e856002\0";
+
+/*
+ * Take two wide strings representing file and directory names
+ * (dirName, fileName), join the strings with the appropriate path
+ * delimiter and populate a wide character buffer (absPathName) with
+ * the resulting string.
+ *
+ * Returns: The number of wide characters in the resulting string.
+ * 0 is returned on Error.
+ */
+int
+mkAbsoluteFilenameA (
+ LPSTR dirName,
+ DWORD dwDirLength,
+ LPCSTR fileName,
+ DWORD dwFileLength,
+ LPSTR absPathName )
+{
+ extern const char *szPathDelimA;
+
+ DWORD sizeDN, sizeFN, sizeAPN;
+
+ sizeDN = strlen( dirName );
+ sizeFN = strlen( fileName );
+ sizeAPN = (sizeDN + 1 + sizeFN + 1);
+
+ /* insure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */
+ if ( sizeAPN > _MAX_PATH )
+ {
+ return ( 0 );
+ }
+
+ strncpy(absPathName, dirName, dwDirLength +1);
+ strncpy(absPathName, szPathDelimA, 2);
+ strncpy(absPathName, fileName, dwFileLength +1);
+
+ return (sizeAPN);
+
+}
+
+int __cdecl main( int argc, char **argv )
+
+{
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ static FILE * fp;
+
+ DWORD dwFileLength;
+ DWORD dwDirLength;
+ DWORD dwSize;
+
+ size_t cslen;
+
+ char szReadStringA[256];
+
+ char szDirNameA[_MAX_DIR];
+ char absPathBuf[_MAX_PATH];
+ char *szAbsPathNameA;
+
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ ZeroMemory ( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ ZeroMemory ( &pi, sizeof(pi) );
+
+ szAbsPathNameA=&absPathBuf[0];
+ dwFileLength = strlen( szChildFileA );
+
+ dwDirLength = GetCurrentDirectory(_MAX_PATH, szDirNameA);
+
+ if (0 == dwDirLength)
+ {
+ Fail ("GetCurrentDirectory call failed. Could not get "
+ "current working directory\n. Exiting.\n");
+ }
+
+ dwSize = mkAbsoluteFilenameA( szDirNameA, dwDirLength, szChildFileA,
+ dwFileLength, szAbsPathNameA );
+
+ if (0 == dwSize)
+ {
+ Fail ("Palsuite Code: mkAbsoluteFilename() call failed. Could "
+ "not build absolute path name to file\n. Exiting.\n");
+ }
+
+ if ( !CreateProcessA ( NULL,
+ szAbsPathNameA,
+ NULL,
+ NULL,
+ FALSE,
+ CREATE_NEW_CONSOLE,
+ NULL,
+ NULL,
+ &si,
+ &pi )
+ )
+ {
+ Fail ( "CreateProcess call failed. GetLastError returned %d\n",
+ GetLastError() );
+ }
+
+ WaitForSingleObject ( pi.hProcess, INFINITE );
+
+ szAbsPathNameA=&absPathBuf[0];
+
+ dwFileLength = strlen( szCommonFileA );
+
+ dwSize = mkAbsoluteFilenameA( szDirNameA, dwDirLength, szCommonFileA,
+ dwFileLength, szAbsPathNameA );
+
+ /* set the string length for the open call*/
+
+ if (0 == dwSize)
+ {
+ Fail ("Palsuite Code: mkAbsoluteFilename() call failed. Could "
+ "not build absolute path name to file\n. Exiting.\n");
+ }
+
+ if ( NULL == ( fp = fopen ( szAbsPathNameA , "r" ) ) )
+ {
+ Fail ("%s\nunable to open %s\nfor reading. Exiting.\n", argv[0],
+ szAbsPathNameA );
+ }
+
+ cslen = strlen ( szCommonStringA );
+
+ if ( NULL == fgets( szReadStringA, (cslen + 1), fp ))
+ {
+ /*
+ * A return value of NULL indicates an error condition or an
+ * EOF condition
+ */
+ Fail ("%s\nunable to read file\n%s\nszReadStringA is %s\n"
+ "Exiting.\n", argv[0], szAbsPathNameA,
+ szReadStringA );
+
+ }
+ if ( 0 != strncmp( szReadStringA, szCommonStringA, cslen ))
+ {
+ Fail ("string comparison failed.\n szReadStringA is %s and\n"
+ "szCommonStringA is %s\n", szReadStringA,
+ szCommonStringA );
+ }
+ else
+ {
+ Trace ("string comparison passed.\n");
+ }
+
+ if (0 != (fclose ( fp )))
+ {
+ Trace ("%s unable to close file %s. This may cause a file pointer "
+ "leak. Continuing.\n", argv[0], szAbsPathNameA );
+ }
+
+ /* Close process and thread handle */
+ CloseHandle ( pi.hProcess );
+ CloseHandle ( pi.hThread );
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test1/testinfo.dat b/src/pal/tests/palsuite/threading/CreateProcessA/test1/testinfo.dat
new file mode 100644
index 0000000000..02c25444fe
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test1/testinfo.dat
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateProcessA
+Name = Positive Test for CreateProcessA
+TYPE = DEFAULT
+EXE1 = parentprocess
+EXE2 = childprocess
+Description
+= Test the CreateProcessA function. The test executes the childprocess
+= program. The childprocess program launches and writes a const char string
+= to a file childdata. The parent waits for the completion of childprocess
+= and then reads the string from the childdata file. If the string in the
+= file matches it's copy of the const char string, then the test succeeds.
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateProcessA/test2/CMakeLists.txt
new file mode 100644
index 0000000000..b81ea2e978
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ parentprocess.c
+)
+
+add_executable(paltest_createprocessa_test2
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_createprocessa_test2 coreclrpal)
+
+target_link_libraries(paltest_createprocessa_test2
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ childprocess.c
+)
+
+add_executable(paltest_createprocessa_test2_child
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_createprocessa_test2_child coreclrpal)
+
+target_link_libraries(paltest_createprocessa_test2_child
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test2/childprocess.c b/src/pal/tests/palsuite/threading/CreateProcessA/test2/childprocess.c
new file mode 100644
index 0000000000..baa20c2d3c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/childprocess.c
@@ -0,0 +1,69 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: createprocessa/test2/childprocess.c
+**
+** Purpose: This child process reads a string from stdin
+** and writes it out to stdout & stderr
+**
+** Dependencies: memset
+** fgets
+** gputs
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+#include "test2.h"
+
+
+
+int __cdecl main( int argc, char **argv )
+{
+ int iRetCode = EXIT_OK_CODE; /* preset exit code to OK */
+ char szBuf[BUF_LEN];
+
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ if (argc != 4)
+ {
+ return EXIT_ERR_CODE3;
+ }
+
+ if (strcmp(argv[1], szArg1) != 0
+ || strcmp(argv[2], szArg2) != 0
+ || strcmp(argv[3], szArg3) != 0)
+ {
+ return EXIT_ERR_CODE4;
+ }
+
+
+ memset(szBuf, 0, BUF_LEN);
+
+ /* Read the string that was written by the parent */
+ if (fgets(szBuf, BUF_LEN, stdin) == NULL)
+ {
+ return EXIT_ERR_CODE1;
+ }
+
+ /* Write the string out to the stdout & stderr pipes */
+ if (fputs(szBuf, stdout) == EOF
+ || fputs(szBuf, stderr) == EOF)
+ {
+ return EXIT_ERR_CODE2;
+ }
+
+ /* The exit code will indicate success or failure */
+ PAL_TerminateEx(iRetCode);
+
+ /* Return special exit code to indicate success or failure */
+ return iRetCode;
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test2/parentprocess.c b/src/pal/tests/palsuite/threading/CreateProcessA/test2/parentprocess.c
new file mode 100644
index 0000000000..ef3340c5d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/parentprocess.c
@@ -0,0 +1,243 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: createprocessa/test2/parentprocess.c
+**
+** Purpose: Test the following features of CreateProcessA:
+** - Check to see if hProcess & hThread are set in
+** return PROCESS_INFORMATION structure
+** - Check to see if stdin, stdout, & stderr handles
+** are used when STARTF_USESTDHANDLES is specified
+** in STARUPINFO flags and bInheritHandles = TRUE
+** - Check to see that proper arguments are passed to
+** child process
+**
+** Dependencies: CreatePipe
+** strcpy, strlen, strncmp, memset
+** WaitForSingleObject
+** WriteFile, ReadFile
+** GetExitCodeProcess
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+#include "test2.h"
+
+
+
+int __cdecl main( int argc, char **argv )
+{
+
+ /*******************************************
+ * Declarations
+ *******************************************/
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ HANDLE hTestStdInR = NULL;
+ HANDLE hTestStdInW = NULL;
+ HANDLE hTestStdOutR = NULL;
+ HANDLE hTestStdOutW = NULL;
+ HANDLE hTestStdErrR = NULL;
+ HANDLE hTestStdErrW = NULL;
+
+ BOOL bRetVal = FALSE;
+ DWORD dwBytesWritten = 0;
+ DWORD dwBytesRead = 0;
+ DWORD dwExitCode = 0;
+
+ SECURITY_ATTRIBUTES pipeAttributes;
+
+ char szStdOutBuf[BUF_LEN];
+ char szStdErrBuf[BUF_LEN];
+ char szFullPathNameA[_MAX_PATH];
+
+
+ /*******************************************
+ * Initialization
+ *******************************************/
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ /*Setup SECURITY_ATTRIBUTES structure for CreatePipe*/
+ pipeAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+ pipeAttributes.lpSecurityDescriptor = NULL;
+ pipeAttributes.bInheritHandle = TRUE;
+
+
+ /*Create a StdIn pipe for child*/
+ bRetVal = CreatePipe(&hTestStdInR, /* read handle*/
+ &hTestStdInW, /* write handle */
+ &pipeAttributes, /* security attributes*/
+ 1024); /* pipe size*/
+
+ if (bRetVal == FALSE)
+ {
+ Fail("ERROR: %ld :Unable to create stdin pipe\n", GetLastError());
+ }
+
+
+ /*Create a StdOut pipe for child*/
+ bRetVal = CreatePipe(&hTestStdOutR, /* read handle*/
+ &hTestStdOutW, /* write handle */
+ &pipeAttributes, /* security attributes*/
+ 0); /* pipe size*/
+
+ if (bRetVal == FALSE)
+ {
+ Fail("ERROR: %ld :Unable to create stdout pipe\n", GetLastError());
+ }
+
+
+ /*Create a StdErr pipe for child*/
+ bRetVal = CreatePipe(&hTestStdErrR, /* read handle*/
+ &hTestStdErrW, /* write handle */
+ &pipeAttributes, /* security attributes*/
+ 0); /* pipe size*/
+
+ if (bRetVal == FALSE)
+ {
+ Fail("ERROR: %ld :Unable to create stderr pipe\n", GetLastError());
+ }
+
+ /* Zero the data structure space */
+ ZeroMemory ( &pi, sizeof(pi) );
+ ZeroMemory ( &si, sizeof(si) );
+
+ /* Set the process flags and standard io handles */
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = hTestStdInR;
+ si.hStdOutput = hTestStdOutW;
+ si.hStdError = hTestStdErrW;
+
+ strcpy(szFullPathNameA, szChildFileA);
+ strcat(szFullPathNameA, szArgs);
+
+ /*******************************************
+ * Start Testing
+ *******************************************/
+
+ /* Launch the child */
+ if ( !CreateProcessA (NULL, szFullPathNameA, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ))
+ {
+ Fail("ERROR: CreateProcess call failed. GetLastError returned %d\n",
+ GetLastError() );
+ }
+
+ /* Check the returned process information for validity */
+ if (pi.hProcess == 0 || pi.hThread == 0)
+ {
+ Fail("ERROR: CreateProcess Error: Process Handle = %u, Thread Handle = %u\n",
+ pi.hProcess, pi.hThread);
+ }
+
+ /* Write the Constructed string to stdin pipe for the child process */
+ if (WriteFile(hTestStdInW, szTestString, strlen(szTestString), &dwBytesWritten, NULL) == FALSE
+ || WriteFile(hTestStdInW, "\n", strlen("\n"), &dwBytesWritten, NULL) == FALSE)
+ {
+ Fail("ERROR: %ld :unable to write to write pipe handle "
+ "hTestStdInW=0x%lx\n", GetLastError(), hTestStdInW);
+ }
+
+ /* Wait for the child to finish, Max 20 seconds */
+ dwExitCode = WaitForSingleObject(pi.hProcess, 20000);
+
+ /* If the child failed then whole thing fails */
+ if (dwExitCode != WAIT_OBJECT_0)
+ {
+ TerminateProcess(pi.hProcess, 0);
+ Fail("ERROR: The child failed to run properly.\n");
+ }
+
+ /* Check for problems in the child process */
+ if (GetExitCodeProcess(pi.hProcess, &dwExitCode) == FALSE)
+ {
+ Fail("ERROR: Call to GetExitCodeProcess failed.\n");
+ }
+ else if (dwExitCode == EXIT_ERR_CODE1)
+ {
+ Fail("ERROR: The Child process could not reead the string "
+ "written to the stdin pipe.\n");
+ }
+ else if (dwExitCode == EXIT_ERR_CODE2)
+ {
+ Fail("ERROR: The Child process could not write the string "
+ "the stdout pipe or stderr pipe.\n");
+ }
+ else if (dwExitCode == EXIT_ERR_CODE3)
+ {
+ Fail("ERROR: The Child received the wrong number of "
+ "command line arguments.\n");
+ }
+ else if (dwExitCode == EXIT_ERR_CODE4)
+ {
+ Fail("ERROR: The Child received the wrong "
+ "command line arguments.\n");
+ }
+ else if (dwExitCode != EXIT_OK_CODE)
+ {
+ Fail("ERROR: Unexpected exit code returned: %u. Child process "
+ "did not complete its part of the test.\n", dwExitCode);
+ }
+
+
+ /* The child ran ok, so check to see if we received the proper */
+ /* strings through the pipes. */
+
+ /* clear our buffers */
+ memset(szStdOutBuf, 0, BUF_LEN);
+ memset(szStdErrBuf, 0, BUF_LEN);
+
+ /* Read the data back from the child process stdout */
+ bRetVal = ReadFile(hTestStdOutR, /* handle to read pipe*/
+ szStdOutBuf, /* buffer to write to*/
+ BUF_LEN, /* number of bytes to read*/
+ &dwBytesRead, /* number of bytes read*/
+ NULL); /* overlapped buffer*/
+
+ /*Read the data back from the child process stderr */
+ bRetVal = ReadFile(hTestStdErrR, /* handle to read pipe*/
+ szStdErrBuf, /* buffer to write to*/
+ BUF_LEN, /* number of bytes to read*/
+ &dwBytesRead, /* number of bytes read*/
+ NULL); /* overlapped buffer*/
+
+
+ /* Confirm that we recieved the same string that we originally */
+ /* wrote to the child and was received on both stdout & stderr.*/
+ if (strncmp(szTestString, szStdOutBuf, strlen(szTestString)) != 0
+ || strncmp(szTestString, szStdErrBuf, strlen(szTestString)) != 0)
+ {
+ Fail("ERROR: The data read back from child does not match "
+ "what was written. STDOUT: %s STDERR: %s\n",
+ szStdOutBuf, szStdErrBuf);
+ }
+
+
+ /*******************************************
+ * Clean Up
+ *******************************************/
+
+ /* Close process and thread handle */
+ CloseHandle ( pi.hProcess );
+ CloseHandle ( pi.hThread );
+
+ CloseHandle(hTestStdInR);
+ CloseHandle(hTestStdInW);
+ CloseHandle(hTestStdOutR);
+ CloseHandle(hTestStdOutW);
+ CloseHandle(hTestStdErrR);
+ CloseHandle(hTestStdErrW);
+
+ PAL_Terminate();
+ return ( PASS );
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test2/test2.h b/src/pal/tests/palsuite/threading/CreateProcessA/test2/test2.h
new file mode 100644
index 0000000000..8cdff3b939
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/test2.h
@@ -0,0 +1,72 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test2.h
+**
+
+**
+**===========================================================*/
+
+
+const char *szChildFileA = "paltest_createprocessa_test2_child";
+const char *szArgs = " A B C";
+const char *szArg1 = "A";
+const char *szArg2 = "B";
+const char *szArg3 = "C";
+
+const char *szPathDelimA = "\\";
+
+const char *szTestString = "Copyright (c) Microsoft";
+
+const DWORD EXIT_OK_CODE = 100;
+const DWORD EXIT_ERR_CODE1 = 101;
+const DWORD EXIT_ERR_CODE2 = 102;
+const DWORD EXIT_ERR_CODE3 = 103;
+const DWORD EXIT_ERR_CODE4 = 104;
+const DWORD EXIT_ERR_CODE5 = 105;
+
+#define BUF_LEN 64
+
+/*
+ * Take two wide strings representing file and directory names
+ * (dirName, fileName), join the strings with the appropriate path
+ * delimiter and populate a wide character buffer (absPathName) with
+ * the resulting string.
+ *
+ * Returns: The number of wide characters in the resulting string.
+ * 0 is returned on Error.
+ */
+int
+mkAbsoluteFilenameA (
+ LPSTR dirName,
+ DWORD dwDirLength,
+ LPCSTR fileName,
+ DWORD dwFileLength,
+ LPSTR absPathName )
+{
+ extern const char *szPathDelimA;
+
+ DWORD sizeDN;
+ DWORD sizeFN;
+ DWORD sizeAPN;
+
+ sizeDN = strlen( dirName );
+ sizeFN = strlen( fileName );
+ sizeAPN = (sizeDN + 1 + sizeFN + 1);
+
+ /* insure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */
+ if ( sizeAPN > _MAX_PATH )
+ {
+ return ( 0 );
+ }
+
+ strncpy(absPathName, dirName, dwDirLength +1);
+ strcat(absPathName, szPathDelimA);
+ strcat(absPathName, fileName);
+
+ return (sizeAPN);
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test2/testinfo.dat b/src/pal/tests/palsuite/threading/CreateProcessA/test2/testinfo.dat
new file mode 100644
index 0000000000..23fcdf93ae
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/testinfo.dat
@@ -0,0 +1,20 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateProcessA
+Name = PROCESS_INFORMATION and HANDLE Inheritance
+TYPE = DEFAULT
+EXE1 = parentprocess
+EXE2 = childprocess
+Description
+= Test the following features of CreateProcessA:
+= - Check to see if hProcess & hThread are set in
+= return PROCESS_INFORMATION structure
+= - Check to see if stdin, stdout, & stderr handles
+= are used when STARTF_USESTDHANDLES is specified
+= in STARUPINFO flags and bInheritHandles = TRUE
+= - Check to see that proper arguments are passed to
+= child process
diff --git a/src/pal/tests/palsuite/threading/CreateProcessW/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateProcessW/CMakeLists.txt
new file mode 100644
index 0000000000..ef14ea5352
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+
diff --git a/src/pal/tests/palsuite/threading/CreateProcessW/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateProcessW/test1/CMakeLists.txt
new file mode 100644
index 0000000000..394b124526
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test1/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ parentProcess.c
+)
+
+add_executable(paltest_createprocessw_test1
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_createprocessw_test1 coreclrpal)
+
+target_link_libraries(paltest_createprocessw_test1
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ childProcess.c
+)
+
+add_executable(paltest_createprocessw_test1_child
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_createprocessw_test1_child coreclrpal)
+
+target_link_libraries(paltest_createprocessw_test1_child
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateProcessW/test1/childProcess.c b/src/pal/tests/palsuite/threading/CreateProcessW/test1/childProcess.c
new file mode 100644
index 0000000000..c71f967b65
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test1/childProcess.c
@@ -0,0 +1,150 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CreateProcessW/test1/childprocess.c
+**
+** Purpose: Test to ensure CreateProcessW starts a new process. This test
+** launches a child process, and examines a file written by the child.
+** This code is the child code.
+**
+** Dependencies: GetCurrentDirectory
+** MultiByteToWideChar
+** wcslen
+** strlen
+** WideCharToMultiByte
+** fopen
+** fclose
+** fprintf
+**
+
+**
+**=========================================================*/
+
+#define UNICODE
+#include <palsuite.h>
+
+const WCHAR szCommonFileW[] =
+ {'c','h','i','l','d','d','a','t','a','.','t','m','p','\0'};
+
+const WCHAR szPathDelimW[] = {'\\','\0'};
+
+const char *szCommonStringA = "058d2d057111a313aa82401c2e856002\0";
+
+/*
+ * Take two wide strings representing file and directory names
+ * (dirName, fileName), join the strings with the appropriate path
+ * delimiter and populate a wide character buffer (absPathName) with
+ * the resulting string.
+ *
+ * Returns: The number of wide characters in the resulting string.
+ * 0 is returned on Error.
+ */
+int
+mkAbsoluteFilenameW (
+ LPWSTR dirName,
+ DWORD dwDirLength,
+ LPCWSTR fileName,
+ DWORD dwFileLength,
+ LPWSTR absPathName )
+{
+ extern const WCHAR szPathDelimW[];
+
+ DWORD sizeDN, sizeFN, sizeAPN;
+
+ sizeDN = wcslen( dirName );
+ sizeFN = wcslen( fileName );
+ sizeAPN = (sizeDN + 1 + sizeFN + 1);
+
+ /* insure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */
+ if ( sizeAPN > _MAX_PATH )
+ {
+ return ( 0 );
+ }
+
+ wcsncpy(absPathName, dirName, dwDirLength +1);
+ wcsncpy(absPathName, szPathDelimW, 2);
+ wcsncpy(absPathName, fileName, dwFileLength +1);
+
+ return (sizeAPN);
+
+}
+
+int __cdecl main( int argc, char **argv )
+{
+
+ static FILE * fp;
+
+ DWORD dwFileLength;
+ DWORD dwDirLength;
+ DWORD dwSize;
+
+ char *szAbsPathNameA;
+ WCHAR szDirNameW[_MAX_DIR];
+ WCHAR szAbsPathNameW[_MAX_PATH];
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ dwDirLength = GetCurrentDirectory( _MAX_PATH, szDirNameW );
+
+ if (0 == dwDirLength)
+ {
+ Fail ("GetCurrentDirectory call failed. Could not get "
+ "current working directory\n. Exiting.\n");
+ }
+
+ dwFileLength = wcslen( szCommonFileW );
+
+ dwSize = mkAbsoluteFilenameW( szDirNameW, dwDirLength, szCommonFileW,
+ dwFileLength, szAbsPathNameW );
+
+ if (0 == dwSize)
+ {
+ Fail ("Palsuite Code: mkAbsoluteFilename() call failed. Could "
+ "not build absolute path name to file\n. Exiting.\n");
+ }
+
+ /* set the string length for the open call */
+ szAbsPathNameA = malloc (dwSize +1);
+
+ if (NULL == szAbsPathNameA)
+ {
+ Fail ("Unable to malloc (%d) bytes. Exiting\n", (dwSize +1) );
+ }
+
+ WideCharToMultiByte (CP_ACP, 0, szAbsPathNameW, -1, szAbsPathNameA,
+ (dwSize + 1), NULL, NULL);
+
+ if ( NULL == ( fp = fopen ( szAbsPathNameA , "w+" ) ) )
+ {
+ /*
+ * A return value of NULL indicates an error condition or an
+ * EOF condition
+ */
+ Fail ("%s unable to open %s for writing. Exiting.\n", argv[0]
+ , szAbsPathNameA );
+ }
+
+ free (szAbsPathNameA);
+
+ if ( 0 >= ( fprintf ( fp, "%s", szCommonStringA )))
+ {
+ Fail("%s unable to write to %s. Exiting.\n", argv[0]
+ , szAbsPathNameA );
+ }
+
+ if (0 != (fclose ( fp )))
+ {
+ Fail ("%s unable to close file %s. Pid may not be "
+ "written to file. Exiting.\n", argv[0], szAbsPathNameA );
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessW/test1/parentProcess.c b/src/pal/tests/palsuite/threading/CreateProcessW/test1/parentProcess.c
new file mode 100644
index 0000000000..db1fb6356d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test1/parentProcess.c
@@ -0,0 +1,210 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CreateProcessW/test1/parentprocess.c
+**
+** Purpose: Test to ensure CreateProcessW starts a new process. This test
+** launches a child process, and examines a file written by the child.
+** This process (the parent process) reads the file created by the child and
+** compares the value the child wrote to the file. (a const char *)
+**
+** Dependencies: GetCurrentDirectory
+** MultiByteToWideChar
+** wcslen
+** strlen
+** WideCharToMultiByte
+** WaitForSingleObject
+** fopen
+** fclose
+** Fail
+**
+
+**
+**=========================================================*/
+
+#define UNICODE
+#include <palsuite.h>
+
+const WCHAR szCommonFileW[] =
+ {'c','h','i','l','d','d','a','t','a','.','t','m','p','\0'};
+
+const WCHAR szChildFileW[] = u"paltest_createprocessw_test1_child";
+
+const WCHAR szPathDelimW[] = {'\\','\0'};
+
+const char *szCommonStringA = "058d2d057111a313aa82401c2e856002\0";
+
+/*
+ * Take two wide strings representing file and directory names
+ * (dirName, fileName), join the strings with the appropriate path
+ * delimiter and populate a wide character buffer (absPathName) with
+ * the resulting string.
+ *
+ * Returns: The number of wide characters in the resulting string.
+ * 0 is returned on Error.
+ */
+int
+mkAbsoluteFilenameW (
+ LPWSTR dirName,
+ DWORD dwDirLength,
+ LPCWSTR fileName,
+ DWORD dwFileLength,
+ LPWSTR absPathName )
+{
+ extern const WCHAR szPathDelimW[];
+
+ DWORD sizeDN, sizeFN, sizeAPN;
+
+ sizeDN = wcslen( dirName );
+ sizeFN = wcslen( fileName );
+ sizeAPN = (sizeDN + 1 + sizeFN + 1);
+
+ /* insure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */
+ if ( sizeAPN > _MAX_PATH )
+ {
+ return ( 0 );
+ }
+
+ wcsncpy(absPathName, dirName, dwDirLength +1);
+ wcsncpy(absPathName, szPathDelimW, 2);
+ wcsncpy(absPathName, fileName, dwFileLength +1);
+
+ return (sizeAPN);
+
+}
+
+int __cdecl main( int argc, char **argv )
+
+{
+
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+
+ static FILE * fp;
+
+ DWORD dwFileLength;
+ DWORD dwDirLength;
+ DWORD dwSize;
+
+ size_t cslen;
+
+ char szReadStringA[256];
+
+ char szAbsPathNameA[_MAX_PATH];
+ WCHAR szDirNameW[_MAX_DIR];
+ WCHAR absPathBuf[_MAX_PATH];
+ WCHAR *szAbsPathNameW;
+
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ ZeroMemory ( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ ZeroMemory ( &pi, sizeof(pi) );
+
+ szAbsPathNameW=&absPathBuf[0];
+ dwFileLength = wcslen( szChildFileW );
+
+ dwDirLength = GetCurrentDirectory(_MAX_PATH, szDirNameW);
+
+ if (0 == dwDirLength)
+ {
+ Fail ("GetCurrentDirectory call failed. Could not get "
+ "current working directory\n. Exiting.\n");
+ }
+
+ dwSize = mkAbsoluteFilenameW( szDirNameW, dwDirLength, szChildFileW,
+ dwFileLength, szAbsPathNameW );
+
+ if (0 == dwSize)
+ {
+ Fail ("Palsuite Code: mkAbsoluteFilename() call failed. Could "
+ "not build absolute path name to file\n. Exiting.\n");
+ }
+
+ if ( !CreateProcessW ( NULL,
+ szAbsPathNameW,
+ NULL,
+ NULL,
+ FALSE,
+ CREATE_NEW_CONSOLE,
+ NULL,
+ NULL,
+ &si,
+ &pi )
+ )
+ {
+ Fail ( "CreateProcess call failed. GetLastError returned %d\n",
+ GetLastError() );
+ }
+
+ WaitForSingleObject ( pi.hProcess, INFINITE );
+
+ szAbsPathNameW=&absPathBuf[0];
+
+ dwFileLength = wcslen( szCommonFileW );
+
+ dwSize = mkAbsoluteFilenameW( szDirNameW, dwDirLength, szCommonFileW,
+ dwFileLength, szAbsPathNameW );
+
+ /* set the string length for the open call*/
+
+ if (0 == dwSize)
+ {
+ Fail ("Palsuite Code: mkAbsoluteFilename() call failed. Could "
+ "not build absolute path name to file\n. Exiting.\n");
+ }
+
+ WideCharToMultiByte (CP_ACP, 0, szAbsPathNameW, -1, szAbsPathNameA,
+ (dwSize + 1), NULL, NULL);
+
+ if ( NULL == ( fp = fopen ( szAbsPathNameA , "r" ) ) )
+ {
+ Fail ("%s\nunable to open %s\nfor reading. Exiting.\n", argv[0],
+ szAbsPathNameA );
+ }
+
+ cslen = strlen ( szCommonStringA );
+
+ if ( NULL == fgets( szReadStringA, (cslen + 1), fp ))
+ {
+ /*
+ * A return value of NULL indicates an error condition or an
+ * EOF condition
+ */
+ Fail ("%s\nunable to read file\n%s\nszReadStringA is %s\n"
+ "Exiting.\n", argv[0], szAbsPathNameA,
+ szReadStringA );
+ }
+
+ if ( 0 != strncmp( szReadStringA, szCommonStringA, cslen ))
+ {
+ Fail ("string comparison failed.\n szReadStringA is %s and\n"
+ "szCommonStringA is %s\n", szReadStringA,
+ szCommonStringA );
+ }
+ else
+ {
+ Trace ("string comparison passed.\n");
+ }
+
+ if (0 != (fclose ( fp )))
+ {
+ Trace ("%s unable to close file %s. This may cause a file pointer "
+ "leak. Continuing.\n", argv[0], szAbsPathNameA );
+ }
+
+ /* Close process and thread handle */
+ CloseHandle ( pi.hProcess );
+ CloseHandle ( pi.hThread );
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessW/test1/testinfo.dat b/src/pal/tests/palsuite/threading/CreateProcessW/test1/testinfo.dat
new file mode 100644
index 0000000000..2acf2c9289
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test1/testinfo.dat
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateProcessW
+Name = Positive Test for CreateProcessW
+TYPE = DEFAULT
+EXE1 = parentprocess
+EXE2 = childprocess
+Description
+= Test the CreateProcessW function. The test executes the childprocess
+= program. The childprocess program launches and writes a const char string
+= to a file childdata. The parent waits for the completion of childprocess
+= and then reads the string from the childdata file. If the string in the
+= file matches it's copy of the const char string, then the test succeeds.
diff --git a/src/pal/tests/palsuite/threading/CreateProcessW/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateProcessW/test2/CMakeLists.txt
new file mode 100644
index 0000000000..3feef213c4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test2/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ parentprocess.c
+)
+
+add_executable(paltest_createprocessw_test2
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_createprocessw_test2 coreclrpal)
+
+target_link_libraries(paltest_createprocessw_test2
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ childprocess.c
+)
+
+add_executable(paltest_createprocessw_test2_child
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_createprocessw_test2_child coreclrpal)
+
+target_link_libraries(paltest_createprocessw_test2_child
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateProcessW/test2/childprocess.c b/src/pal/tests/palsuite/threading/CreateProcessW/test2/childprocess.c
new file mode 100644
index 0000000000..b4ab9366d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test2/childprocess.c
@@ -0,0 +1,78 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: createprocessw/test2/childprocess.c
+**
+** Purpose: This child process reads a string from stdin
+** and writes it out to stdout & stderr
+**
+** Dependencies: memset
+** fgets
+** gputs
+**
+
+**
+**=========================================================*/
+
+#define UNICODE
+#include <palsuite.h>
+#include "test2.h"
+
+
+int __cdecl main( int argc, char **argv )
+{
+ int iRetCode = EXIT_OK_CODE; /* preset exit code to OK */
+ char szBuf[BUF_LEN];
+
+ WCHAR *swzParam1, *swzParam2, *swzParam3 = NULL;
+
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+ if (argc != 4)
+ {
+ return EXIT_ERR_CODE3;
+ }
+
+ swzParam1 = convert(argv[1]);
+ swzParam2 = convert(argv[2]);
+ swzParam3 = convert(argv[3]);
+
+ if (wcscmp(swzParam1, szArg1) != 0
+ || wcscmp(swzParam2, szArg2) != 0
+ || wcscmp(swzParam3, szArg3) != 0)
+ {
+ return EXIT_ERR_CODE4;
+ }
+
+ free(swzParam1);
+ free(swzParam2);
+ free(swzParam3);
+
+ memset(szBuf, 0, BUF_LEN);
+
+ /* Read the string that was written by the parent */
+ if (fgets(szBuf, BUF_LEN, stdin) == NULL)
+ {
+ return EXIT_ERR_CODE1;
+ }
+
+
+ /* Write the string out to the stdout & stderr pipes */
+ if (fputs(szBuf, stdout) == EOF
+ || fputs(szBuf, stderr) == EOF)
+ {
+ return EXIT_ERR_CODE2;
+ }
+
+ /* The exit code will indicate success or failure */
+ PAL_TerminateEx(iRetCode);
+
+ /* Return special exit code to indicate success or failure */
+ return iRetCode;
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.c b/src/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.c
new file mode 100644
index 0000000000..439b7b5eef
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.c
@@ -0,0 +1,245 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: createprocessw/test2/parentprocess.c
+**
+** Purpose: Test the following features of CreateProcessW:
+** - Check to see if hProcess & hThread are set in
+** return PROCESS_INFORMATION structure
+** - Check to see if stdin, stdout, & stderr handles
+** are used when STARTF_USESTDHANDLES is specified
+** in STARUPINFO flags and bInheritHandles = TRUE
+** - Check to see that proper arguments are passed to
+** child process
+**
+** Dependencies: CreatePipe
+** strcpy, strlen, strncmp, memset
+** WaitForSingleObject
+** WriteFile, ReadFile
+** GetExitCodeProcess
+**
+
+**
+**=========================================================*/
+
+#define UNICODE
+#include <palsuite.h>
+#include "test2.h"
+
+
+
+int __cdecl main( int argc, char **argv )
+{
+
+ /*******************************************
+ * Declarations
+ *******************************************/
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ HANDLE hTestStdInR = NULL;
+ HANDLE hTestStdInW = NULL;
+ HANDLE hTestStdOutR = NULL;
+ HANDLE hTestStdOutW = NULL;
+ HANDLE hTestStdErrR = NULL;
+ HANDLE hTestStdErrW = NULL;
+
+ BOOL bRetVal = FALSE;
+ DWORD dwBytesWritten = 0;
+ DWORD dwBytesRead = 0;
+ DWORD dwExitCode = 0;
+
+ SECURITY_ATTRIBUTES pipeAttributes;
+
+ char szStdOutBuf[BUF_LEN];
+ char szStdErrBuf[BUF_LEN];
+ WCHAR szFullPathNameW[_MAX_PATH];
+
+
+ /*******************************************
+ * Initialization
+ *******************************************/
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ /*Setup SECURITY_ATTRIBUTES structure for CreatePipe*/
+ pipeAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+ pipeAttributes.lpSecurityDescriptor = NULL;
+ pipeAttributes.bInheritHandle = TRUE;
+
+
+ /*Create a StdIn pipe for child*/
+ bRetVal = CreatePipe(&hTestStdInR, /* read handle*/
+ &hTestStdInW, /* write handle */
+ &pipeAttributes, /* security attributes*/
+ 1024); /* pipe size*/
+
+ if (bRetVal == FALSE)
+ {
+ Fail("ERROR: %ld :Unable to create stdin pipe\n", GetLastError());
+ }
+
+
+ /*Create a StdOut pipe for child*/
+ bRetVal = CreatePipe(&hTestStdOutR, /* read handle*/
+ &hTestStdOutW, /* write handle */
+ &pipeAttributes, /* security attributes*/
+ 0); /* pipe size*/
+
+ if (bRetVal == FALSE)
+ {
+ Fail("ERROR: %ld :Unable to create stdout pipe\n", GetLastError());
+ }
+
+
+ /*Create a StdErr pipe for child*/
+ bRetVal = CreatePipe(&hTestStdErrR, /* read handle*/
+ &hTestStdErrW, /* write handle */
+ &pipeAttributes, /* security attributes*/
+ 0); /* pipe size*/
+
+ if (bRetVal == FALSE)
+ {
+ Fail("ERROR: %ld :Unable to create stderr pipe\n", GetLastError());
+ }
+
+ /* Zero the data structure space */
+ ZeroMemory ( &pi, sizeof(pi) );
+ ZeroMemory ( &si, sizeof(si) );
+
+ /* Set the process flags and standard io handles */
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = hTestStdInR;
+ si.hStdOutput = hTestStdOutW;
+ si.hStdError = hTestStdErrW;
+
+ wcscpy(szFullPathNameW, szChildFileW);
+ wcscat(szFullPathNameW, szArgs);
+
+ /*******************************************
+ * Start Testing
+ *******************************************/
+
+ /* Launch the child */
+ if ( !CreateProcess (NULL, szFullPathNameW, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ))
+ {
+ Fail("ERROR: CreateProcess call failed. GetLastError returned %d\n",
+ GetLastError() );
+ }
+
+ /* Check the returned process information for validity */
+ if (pi.hProcess == 0 || pi.hThread == 0)
+ {
+ Fail("ERROR: CreateProcess Error: Process Handle = %u, Thread Handle = %u\n",
+ pi.hProcess, pi.hThread);
+ }
+
+
+ /* Write the Constructed string to stdin pipe for the child process */
+ if (WriteFile(hTestStdInW, szTestString, strlen(szTestString), &dwBytesWritten, NULL) == FALSE
+ || WriteFile(hTestStdInW, "\n", strlen("\n"), &dwBytesWritten, NULL) == FALSE)
+ {
+ Fail("ERROR: %ld :unable to write to write pipe handle "
+ "hTestStdInW=0x%lx\n", GetLastError(), hTestStdInW);
+ }
+
+ /* Wait for the child to finish, Max 20 seconds */
+ dwExitCode = WaitForSingleObject(pi.hProcess, 20000);
+
+ /* If the child failed then whole thing fails */
+ if (dwExitCode != WAIT_OBJECT_0)
+ {
+ TerminateProcess(pi.hProcess, 0);
+ Fail("ERROR: The child failed to run properly.\n");
+ }
+
+ /* Check for problems in the child process */
+ if (GetExitCodeProcess(pi.hProcess, &dwExitCode) == FALSE)
+ {
+ Fail("ERROR: Call to GetExitCodeProcess failed.\n");
+ }
+ else if (dwExitCode == EXIT_ERR_CODE1)
+ {
+ Fail("ERROR: The Child process could not reead the string "
+ "written to the stdin pipe.\n");
+ }
+ else if (dwExitCode == EXIT_ERR_CODE2)
+ {
+ Fail("ERROR: The Child process could not write the string "
+ "the stdout pipe or stderr pipe.\n");
+ }
+ else if (dwExitCode == EXIT_ERR_CODE3)
+ {
+ Fail("ERROR: The Child received the wrong number of "
+ "command line arguments.\n");
+ }
+ else if (dwExitCode == EXIT_ERR_CODE4)
+ {
+ Fail("ERROR: The Child received the wrong "
+ "command line arguments.\n");
+ }
+ else if (dwExitCode != EXIT_OK_CODE)
+ {
+ Fail("ERROR: Unexpected exit code returned: %u. Child process "
+ "did not complete its part of the test.\n", dwExitCode);
+ }
+
+
+ /* The child ran ok, so check to see if we received the proper */
+ /* strings through the pipes. */
+
+ /* clear our buffers */
+ memset(szStdOutBuf, 0, BUF_LEN);
+ memset(szStdErrBuf, 0, BUF_LEN);
+
+ /* Read the data back from the child process stdout */
+ bRetVal = ReadFile(hTestStdOutR, /* handle to read pipe*/
+ szStdOutBuf, /* buffer to write to*/
+ BUF_LEN, /* number of bytes to read*/
+ &dwBytesRead, /* number of bytes read*/
+ NULL); /* overlapped buffer*/
+
+ /*Read the data back from the child process stderr */
+ bRetVal = ReadFile(hTestStdErrR, /* handle to read pipe*/
+ szStdErrBuf, /* buffer to write to*/
+ BUF_LEN, /* number of bytes to read*/
+ &dwBytesRead, /* number of bytes read*/
+ NULL); /* overlapped buffer*/
+
+
+ /* Confirm that we recieved the same string that we originally */
+ /* wrote to the child and was received on both stdout & stderr.*/
+ if (strncmp(szTestString, szStdOutBuf, strlen(szTestString)) != 0
+ || strncmp(szTestString, szStdErrBuf, strlen(szTestString)) != 0)
+ {
+ Fail("ERROR: The data read back from child does not match "
+ "what was written. STDOUT: %s STDERR: %s\n",
+ szStdOutBuf, szStdErrBuf);
+ }
+
+
+ /*******************************************
+ * Clean Up
+ *******************************************/
+
+ /* Close process and thread handle */
+ CloseHandle ( pi.hProcess );
+ CloseHandle ( pi.hThread );
+
+ CloseHandle(hTestStdInR);
+ CloseHandle(hTestStdInW);
+ CloseHandle(hTestStdOutR);
+ CloseHandle(hTestStdOutW);
+ CloseHandle(hTestStdErrR);
+ CloseHandle(hTestStdErrW);
+
+ PAL_Terminate();
+ return ( PASS );
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessW/test2/test2.h b/src/pal/tests/palsuite/threading/CreateProcessW/test2/test2.h
new file mode 100644
index 0000000000..07d40b8942
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test2/test2.h
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test2.h
+**
+**
+
+**
+**=========================================================*/
+
+
+const WCHAR szChildFileW[] = u"paltest_createprocessw_test2_child";
+const WCHAR szArgs[] = {' ',0x41,' ','B',' ','C','\0'};
+const WCHAR szArg1[] = {0x41,'\0'};
+const WCHAR szArg2[] = {'B','\0'};
+const WCHAR szArg3[] = {'C','\0'};
+
+const char *szTestString = "An uninteresting test string (it works though)";
+
+const DWORD EXIT_OK_CODE = 100;
+const DWORD EXIT_ERR_CODE1 = 101;
+const DWORD EXIT_ERR_CODE2 = 102;
+const DWORD EXIT_ERR_CODE3 = 103;
+const DWORD EXIT_ERR_CODE4 = 104;
+const DWORD EXIT_ERR_CODE5 = 105;
+
+#define BUF_LEN 128
+
diff --git a/src/pal/tests/palsuite/threading/CreateProcessW/test2/testinfo.dat b/src/pal/tests/palsuite/threading/CreateProcessW/test2/testinfo.dat
new file mode 100644
index 0000000000..d16ae593f2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test2/testinfo.dat
@@ -0,0 +1,20 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateProcessW
+Name = PROCESS_INFORMATION and HANDLE Inheritance
+TYPE = DEFAULT
+EXE1 = parentprocess
+EXE2 = childprocess
+Description
+= Test the following features of CreateProcessW:
+= - Check to see if hProcess & hThread are set in
+= return PROCESS_INFORMATION structure
+= - Check to see if stdin, stdout, & stderr handles
+= are used when STARTF_USESTDHANDLES is specified
+= in STARUPINFO flags and bInheritHandles = TRUE
+= - Check to see that proper arguments are passed to
+= child process
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/CMakeLists.txt
new file mode 100644
index 0000000000..1962ade358
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/CMakeLists.txt
new file mode 100644
index 0000000000..f89e150a32
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ CreateSemaphore.c
+)
+
+add_executable(paltest_createsemaphorea_releasesemaphore_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createsemaphorea_releasesemaphore_test1 coreclrpal)
+
+target_link_libraries(paltest_createsemaphorea_releasesemaphore_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/CreateSemaphore.c b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/CreateSemaphore.c
new file mode 100644
index 0000000000..342b15ec29
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/CreateSemaphore.c
@@ -0,0 +1,322 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: createsemaphorea_releasesemaphore/test1/createsemaphore.c
+**
+** Purpose: Test Semaphore operation using classic IPC problem:
+** "Producer-Consumer Problem".
+**
+** Dependencies: CreateThread
+** ReleaseSemaphore
+** WaitForSingleObject
+** Sleep
+** fflush
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+#define PRODUCTION_TOTAL 26
+
+#define _BUF_SIZE 10
+
+DWORD dwThreadId; /* consumer thread identifier */
+
+HANDLE hThread; /* handle to consumer thread */
+
+HANDLE hSemaphoreM; /* handle to mutual exclusion semaphore */
+
+HANDLE hSemaphoreE; /* handle to semaphore that counts empty buffer slots */
+
+HANDLE hSemaphoreF; /* handle to semaphore that counts full buffer slots */
+
+typedef struct Buffer
+{
+ short readIndex;
+ short writeIndex;
+ CHAR message[_BUF_SIZE];
+
+} BufferStructure;
+
+CHAR producerItems[PRODUCTION_TOTAL + 1];
+
+CHAR consumerItems[PRODUCTION_TOTAL + 1];
+
+/*
+ * Read next message from the Buffer into provided pointer.
+ * Returns: 0 on failure, 1 on success.
+ */
+int
+readBuf(BufferStructure *Buffer, char *c)
+{
+ if( Buffer -> writeIndex == Buffer -> readIndex )
+ {
+ return 0;
+ }
+ *c = Buffer -> message[Buffer -> readIndex++];
+ Buffer -> readIndex %= _BUF_SIZE;
+ return 1;
+}
+
+/*
+ * Write message generated by the producer to Buffer.
+ * Returns: 0 on failure, 1 on success.
+ */
+int
+writeBuf(BufferStructure *Buffer, CHAR c)
+{
+ if( ( ((Buffer -> writeIndex) + 1) % _BUF_SIZE) ==
+ (Buffer -> readIndex) )
+ {
+ return 0;
+ }
+ Buffer -> message[Buffer -> writeIndex++] = c;
+ Buffer -> writeIndex %= _BUF_SIZE;
+ return 1;
+}
+
+/*
+ * Atomic decrement of semaphore value.
+ */
+VOID
+down(HANDLE hSemaphore)
+{
+ switch ( (WaitForSingleObject (
+ hSemaphore,
+ 10000))) /* Wait 10 seconds */
+ {
+ case WAIT_OBJECT_0: /*
+ * Semaphore was signaled. OK to access
+ * semaphore.
+ */
+ break;
+ case WAIT_ABANDONED: /*
+ * Object was mutex object whose owning
+ * thread has terminated. Shouldn't occur.
+ */
+ Fail("WaitForSingleObject call returned 'WAIT_ABANDONED'.\n"
+ "Failing Test.\n");
+ break;
+ case WAIT_FAILED: /* WaitForSingleObject function failed */
+ Fail("WaitForSingleObject call returned 'WAIT_FAILED'.\n"
+ "GetLastError returned %d\nFailing Test.\n",GetLastError());
+ break;
+ default:
+ Fail("WaitForSingleObject call returned an unexpected value.\n"
+ "Failing Test.\n");
+ break;
+ }
+
+}
+
+/*
+ * Atomic increment of semaphore value.
+ */
+VOID
+up(HANDLE hSemaphore)
+{
+ if (!ReleaseSemaphore (
+ hSemaphore,
+ 1,
+ NULL)
+ )
+ {
+ Fail("ReleaseSemaphore call failed. GetLastError returned %d\n",
+ GetLastError());
+ }
+}
+
+/*
+ * Sleep 500 milleseconds.
+ */
+VOID
+consumerSleep(VOID)
+{
+ Sleep(500);
+}
+
+/*
+ * Sleep between 10 milleseconds.
+ */
+VOID
+producerSleep(VOID)
+{
+ Sleep(10);
+}
+
+/*
+ * Produce a message and write the message to Buffer.
+ */
+VOID
+producer(BufferStructure *Buffer)
+{
+
+ int n = 0;
+ char c;
+
+ while (n < PRODUCTION_TOTAL)
+ {
+ c = 'A' + n ; /* Produce Item */
+
+ down(hSemaphoreE);
+ down(hSemaphoreM);
+
+ if (writeBuf(Buffer, c))
+ {
+ Trace("Producer produces %c.\n", c);
+ fflush(stdout);
+ producerItems[n++] = c;
+ }
+
+ up(hSemaphoreM);
+ up(hSemaphoreF);
+
+ producerSleep();
+ }
+
+ return;
+}
+
+/*
+ * Read and "Consume" the messages in Buffer.
+ */
+DWORD
+PALAPI
+consumer( LPVOID lpParam )
+{
+ int n = 0;
+ char c;
+
+ consumerSleep();
+
+ while (n < PRODUCTION_TOTAL)
+ {
+
+ down(hSemaphoreF);
+ down(hSemaphoreM);
+
+ if (readBuf((BufferStructure*)lpParam, &c))
+ {
+ Trace("\tConsumer consumes %c.\n", c);
+ fflush(stdout);
+ consumerItems[n++] = c;
+ }
+
+ up(hSemaphoreM);
+ up(hSemaphoreE);
+
+ consumerSleep();
+ }
+
+ return 0;
+}
+
+int __cdecl main (int argc, char **argv)
+{
+
+ BufferStructure Buffer, *pBuffer;
+
+ pBuffer = &Buffer;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ /*
+ * Create Semaphores
+ */
+ hSemaphoreM = CreateSemaphoreA (
+ NULL,
+ 1,
+ 1,
+ NULL);
+
+ if ( NULL == hSemaphoreM )
+ {
+ Fail ( "hSemaphoreM = CreateSemaphoreA () - returned NULL\n"
+ "Failing Test.\nGetLastError returned %d\n", GetLastError());
+ }
+
+
+ hSemaphoreE = CreateSemaphoreA (
+ NULL,
+ _BUF_SIZE ,
+ _BUF_SIZE ,
+ NULL);
+
+ if ( NULL == hSemaphoreE )
+ {
+ Fail ( "hSemaphoreE = CreateSemaphoreA () - returned NULL\n"
+ "Failing Test.\nGetLastError returned %d\n", GetLastError());
+ }
+
+ hSemaphoreF = CreateSemaphoreA (
+ NULL,
+ 0,
+ _BUF_SIZE ,
+ NULL);
+
+ if ( NULL == hSemaphoreF )
+ {
+ Fail ( "hSemaphoreF = CreateSemaphoreA () - returned NULL\n"
+ "Failing Test.\nGetLastError returned %d\n", GetLastError());
+ }
+
+
+ /*
+ * Initialize Buffer
+ */
+ pBuffer->writeIndex = pBuffer->readIndex = 0;
+
+ /*
+ * Create Consumer
+ */
+ hThread = CreateThread(
+ NULL,
+ 0,
+ consumer,
+ &Buffer,
+ 0,
+ &dwThreadId);
+
+ if ( NULL == hThread )
+ {
+ Fail ( "CreateThread() returned NULL. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ /*
+ * Start producing
+ */
+ producer(pBuffer);
+
+ /*
+ * Wait for consumer to complete
+ */
+ WaitForSingleObject (hThread, INFINITE);
+
+ /*
+ * Compare items produced vs. items consumed
+ */
+ if ( 0 != strncmp (producerItems, consumerItems, PRODUCTION_TOTAL) )
+ {
+ Fail("The producerItems string %s\n and the consumerItems string "
+ "%s\ndo not match. This could be a problem with the strncmp()"
+ " function\n FailingTest\nGetLastError() returned %d\n",
+ producerItems, consumerItems, GetLastError());
+ }
+
+ Trace ("producerItems and consumerItems arrays match. All %d\nitems "
+ "were produced and consumed in order.\nTest passed.\n",
+ PRODUCTION_TOTAL);
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/testinfo.dat b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/testinfo.dat
new file mode 100644
index 0000000000..880746e43e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/testinfo.dat
@@ -0,0 +1,18 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateSemaphoreA / ReleaseSemaphore
+Name = Positive Test for CreateSemaphoreA and ReleaseSemaphore
+TYPE = DEFAULT
+EXE1 = createsemaphore
+Description
+= Implementation of Producer / Consumer IPC problem using CreateSemaphoreA
+= and ReleaseSemaphore functions. This test case exercises CreateSemaphoreA
+= , ReleaseSemaphore, CreateThread and WaitForSingleObject functions.
+= Since there is no way to currently create "pseudo" random events in the
+= pal, this example does not behave as classic bounded buffers would. This
+= test case is designed to starve the consumer and have the producer fill
+= the buffer.
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/CMakeLists.txt
new file mode 100644
index 0000000000..f3e01749b8
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ CreateSemaphore.c
+)
+
+add_executable(paltest_createsemaphorea_releasesemaphore_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createsemaphorea_releasesemaphore_test2 coreclrpal)
+
+target_link_libraries(paltest_createsemaphorea_releasesemaphore_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/CreateSemaphore.c b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/CreateSemaphore.c
new file mode 100644
index 0000000000..bff5b51c33
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/CreateSemaphore.c
@@ -0,0 +1,313 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CreateSemaphoreA_ReleaseSemaphore/test2/createsemaphore.c
+**
+** Purpose: Test Semaphore operation using classic IPC problem:
+** "Producer-Consumer Problem".
+**
+** Dependencies: CreateThread
+** ReleaseSemaphore
+** WaitForSingleObject
+** Sleep
+** fflush
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+#define PRODUCTION_TOTAL 26
+
+#define _BUF_SIZE 10
+
+DWORD dwThreadId; /* consumer thread identifier */
+
+HANDLE hThread; /* handle to consumer thread */
+
+HANDLE hSemaphoreM; /* handle to mutual exclusion semaphore */
+
+HANDLE hSemaphoreE; /* handle to semaphore that counts empty buffer slots */
+
+HANDLE hSemaphoreF; /* handle to semaphore that counts full buffer slots */
+
+typedef struct Buffer
+{
+ short readIndex;
+ short writeIndex;
+ CHAR message[_BUF_SIZE];
+
+} BufferStructure;
+
+CHAR producerItems[PRODUCTION_TOTAL + 1];
+
+CHAR consumerItems[PRODUCTION_TOTAL + 1];
+
+/*
+ * Read next message from the Buffer into provided pointer.
+ * Returns: 0 on failure, 1 on success.
+ */
+int
+readBuf(BufferStructure *Buffer, char *c)
+{
+ if( Buffer -> writeIndex == Buffer -> readIndex )
+ {
+ return 0;
+ }
+ *c = Buffer -> message[Buffer -> readIndex++];
+ Buffer -> readIndex %= _BUF_SIZE;
+ return 1;
+}
+
+/*
+ * Write message generated by the producer to Buffer.
+ * Returns: 0 on failure, 1 on success.
+ */
+int
+writeBuf(BufferStructure *Buffer, CHAR c)
+{
+ if( ( ((Buffer -> writeIndex) + 1) % _BUF_SIZE) ==
+ (Buffer -> readIndex) )
+ {
+ return 0;
+ }
+ Buffer -> message[Buffer -> writeIndex++] = c;
+ Buffer -> writeIndex %= _BUF_SIZE;
+ return 1;
+}
+
+/*
+ * Atomic decrement of semaphore value.
+ */
+VOID
+down(HANDLE hSemaphore)
+{
+ switch ( (WaitForSingleObject (
+ hSemaphore,
+ 10000)))
+ {
+ case WAIT_OBJECT_0: /*
+ * Semaphore was signaled. OK to access semaphore.
+ */
+ break;
+ case WAIT_ABANDONED: /*
+ * Object was mutex object whose owning
+ * thread has terminated. Shouldn't occur.
+ */
+ Fail("WaitForSingleObject call returned 'WAIT_ABANDONED'.\n"
+ "Failing Test.\n");
+ break;
+ case WAIT_FAILED: /* WaitForSingleObject function failed */
+ Fail("WaitForSingleObject call returned 'WAIT_FAILED'.\n"
+ "GetLastError returned %d\nFailing Test.\n",GetLastError());
+ break;
+ default:
+ Fail("WaitForSingleObject call returned an unexpected value.\n"
+ "Failing Test.\n");
+ break;
+ }
+
+}
+
+/*
+ * Atomic increment of semaphore value.
+ */
+VOID
+up(HANDLE hSemaphore)
+{
+ if (!ReleaseSemaphore (
+ hSemaphore,
+ 1,
+ NULL)
+ )
+ {
+ Fail("ReleaseSemaphore call failed. GetLastError returned %d\n",
+ GetLastError());
+ }
+}
+
+/*
+ * Sleep 10 milleseconds.
+ */
+VOID
+consumerSleep(VOID)
+{
+ Sleep(10);
+}
+
+/*
+ * Sleep 500 milleseconds.
+ */
+VOID
+producerSleep(VOID)
+{
+ Sleep(500);
+}
+
+/*
+ * Produce a message and write the message to Buffer.
+ */
+VOID
+producer(BufferStructure *Buffer)
+{
+
+ int n = 0;
+ char c;
+
+ while (n < PRODUCTION_TOTAL)
+ {
+ c = 'A' + n ; /* Produce Item */
+
+ down(hSemaphoreE);
+ down(hSemaphoreM);
+
+ if (writeBuf(Buffer, c))
+ {
+ Trace("Producer produces %c.\n", c);
+ fflush(stdout);
+ producerItems[n++] = c;
+ }
+
+ up(hSemaphoreM);
+ up(hSemaphoreF);
+
+ producerSleep();
+ }
+ return;
+}
+
+/*
+ * Read and "Consume" the messages in Buffer.
+ */
+DWORD
+PALAPI
+consumer( LPVOID lpParam )
+{
+ int n = 0;
+ char c;
+
+ consumerSleep();
+
+ while (n < PRODUCTION_TOTAL)
+ {
+
+ down(hSemaphoreF);
+ down(hSemaphoreM);
+
+ if (readBuf((BufferStructure*)lpParam, &c))
+ {
+ Trace("\tConsumer consumes %c.\n", c);
+ fflush(stdout);
+ consumerItems[n++] = c;
+ }
+
+ up(hSemaphoreM);
+ up(hSemaphoreE);
+
+ consumerSleep();
+ }
+ return 0;
+}
+
+int __cdecl main (int argc, char **argv)
+{
+
+ BufferStructure Buffer, *pBuffer;
+
+ pBuffer = &Buffer;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return (FAIL);
+ }
+
+ /*
+ * Create Semaphores
+ */
+ hSemaphoreM = CreateSemaphoreA (
+ NULL,
+ 1,
+ 1,
+ NULL);
+
+ if ( NULL == hSemaphoreM )
+ {
+ Fail ( "hSemaphoreM = CreateSemaphoreA () - returned NULL\n"
+ "Failing Test.\n");
+ }
+
+ hSemaphoreE = CreateSemaphoreA (
+ NULL,
+ _BUF_SIZE ,
+ _BUF_SIZE ,
+ NULL);
+
+ if ( NULL == hSemaphoreE )
+ {
+ Fail ( "hSemaphoreE = CreateSemaphoreA () - returned NULL\n"
+ "Failing Test.\n");
+ }
+
+ hSemaphoreF = CreateSemaphoreA (
+ NULL,
+ 0,
+ _BUF_SIZE ,
+ NULL);
+
+ if ( NULL == hSemaphoreF )
+ {
+ Fail ( "hSemaphoreF = CreateSemaphoreA () - returned NULL\n"
+ "Failing Test.\n");
+ }
+
+ /*
+ * Initialize Buffer
+ */
+ pBuffer->writeIndex = pBuffer->readIndex = 0;
+
+ /*
+ * Create Consumer
+ */
+ hThread = CreateThread(
+ NULL,
+ 0,
+ consumer,
+ &Buffer,
+ 0,
+ &dwThreadId);
+
+ if ( NULL == hThread )
+ {
+ Fail ( "CreateThread() returned NULL. Failing test.\n");
+ }
+
+ /*
+ * Start producing
+ */
+ producer(pBuffer);
+
+ /*
+ * Wait for consumer to complete
+ */
+ WaitForSingleObject (hThread, INFINITE);
+
+ if ( 0 != strncmp (producerItems, consumerItems, PRODUCTION_TOTAL) )
+ {
+ Fail("The producerItems string %s\n and the consumerItems string "
+ "%s\ndo not match. This could be a problem with the strncmp()"
+ " function\n FailingTest\nGetLastError() returned %d\n",
+ producerItems, consumerItems, GetLastError());
+ }
+
+ Trace ("producerItems and consumerItems arrays match. All %d\nitems "
+ "were produced and consumed in order.\nTest passed.\n",
+ PRODUCTION_TOTAL);
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/testinfo.dat b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/testinfo.dat
new file mode 100644
index 0000000000..5a6c2e7909
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/testinfo.dat
@@ -0,0 +1,18 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateSemaphoreA / ReleaseSemaphore
+Name = Positive Test for CreateSemaphoreA and ReleaseSemaphore
+TYPE = DEFAULT
+EXE1 = createsemaphore
+Description
+= Implementation of Producer / Consumer IPC problem using CreateSemaphoreA
+= and ReleaseSemaphore functions. This test case exercises CreateSemaphoreA
+= , ReleaseSemaphore, CreateThread and WaitForSingleObject functions.
+= Since there is no way to currently create "pseudo" random events in the
+= pal, this example does not behave as classic bounded buffers would. This
+= test case is designed to starve the producer and have the consumer fill
+= the buffer.
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/CMakeLists.txt
new file mode 100644
index 0000000000..0c604d6ced
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ createsemaphore.c
+)
+
+add_executable(paltest_createsemaphorea_releasesemaphore_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createsemaphorea_releasesemaphore_test3 coreclrpal)
+
+target_link_libraries(paltest_createsemaphorea_releasesemaphore_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/createsemaphore.c b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/createsemaphore.c
new file mode 100644
index 0000000000..7c6db6b055
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/createsemaphore.c
@@ -0,0 +1,200 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================================
+**
+** Source: createsemaphorea_releasesemaphore/test3/createsemaphore.c
+**
+** Purpose: Test attributes of CreateSemaphoreA and ReleaseSemaphore.
+** Insure for CreateSemaphore that lInitialCount and lMaximumCount
+** constraints are respected. Validate that CreateSemaphore rejects
+** conditions where initial count and / or maximum count are negative
+** and conditions where the initial count is greater than the maximum
+** count. For ReleaseSemaphore validate that lpPreviousCount gets set
+** to the previous semaphore count and lpPreviousCount can be NULL.
+** Also establish ReleaseSemaphore fails when called in a semaphore
+** with count equal to lMaximumCount.
+**
+**
+**==========================================================================*/
+
+#include <palsuite.h>
+
+struct testcase
+{
+ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes;
+ LONG lInitialCount;
+ LONG lMaximumCount;
+ LPCTSTR lpName;
+ BOOL bNegativeTest;
+};
+
+struct testcase testCases[] =
+{
+ {NULL, -1, 1, NULL, TRUE},
+ {NULL, 1, -1, NULL, TRUE},
+ {NULL, -1, -1, NULL, TRUE},
+ {NULL, 2, 1, NULL, TRUE},
+ {NULL, 1, 2, NULL, FALSE},
+ {NULL, 0, 10, NULL, FALSE},
+ {NULL, INT_MAX - 1, INT_MAX, NULL, FALSE},
+ {NULL, INT_MAX, INT_MAX, NULL, FALSE}
+};
+
+HANDLE hSemaphore[sizeof(testCases)/sizeof(struct testcase)];
+
+BOOL cleanup(int index)
+{
+ int i;
+ BOOL bRet = TRUE;
+ for (i = 0; i < index; i++)
+ {
+ if (!CloseHandle(hSemaphore[i]))
+ {
+ bRet = FALSE;
+ Trace("PALSUITE ERROR: CloseHandle(%p) call failed for index %d\n",
+ hSemaphore[i], i);
+ }
+ }
+ return(bRet);
+}
+
+int __cdecl main (int argc, char **argv)
+{
+ int i;
+ int j;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return (FAIL);
+ }
+ /* create semaphores */
+ for (i = 0; i < sizeof(testCases)/sizeof(struct testcase); i++)
+ {
+ hSemaphore[i] = CreateSemaphoreA (testCases[i].lpSemaphoreAttributes,
+ testCases[i].lInitialCount,
+ testCases[i].lMaximumCount,
+ testCases[i].lpName);
+
+ if (NULL == hSemaphore[i])
+ {
+ if (!testCases[i].bNegativeTest)
+ {
+ Trace("PALSUITE ERROR: CreateSemaphoreA('%p' '%ld' '%ld' "
+ "'%p') returned NULL at index %d.\nGetLastError "
+ "returned %d.\n", testCases[i].lpSemaphoreAttributes,
+ testCases[i].lInitialCount, testCases[i].lMaximumCount,
+ testCases[i].lpName, i, GetLastError());
+ if (i > 0)
+ {
+ cleanup(i - 1);
+ }
+ Fail("");
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ /* increment semaphore count to lMaximumCount */
+ for (j = testCases[i].lInitialCount; (ULONG)j <= (ULONG)testCases[i].lMaximumCount;
+ j++)
+ {
+ if (testCases[i].lMaximumCount == j)
+ {
+ /* Call ReleaseSemaphore once more to ensure ReleaseSemaphore
+ fails */
+ if(ReleaseSemaphore(hSemaphore[i], 1, NULL))
+ {
+ Trace("PALSUITE ERROR: ReleaseSemaphore('%p' '%ld' '%p') "
+ "call returned %d\nwhen it should have returned "
+ "%d.\nThe semaphore's count was %d.\nGetLastError "
+ "returned %d.\n", hSemaphore[i], 1, NULL, TRUE,
+ FALSE, j, GetLastError());
+ cleanup(i);
+ Fail("");
+ }
+ }
+ else
+ {
+ int previous;
+ BOOL bRet = ReleaseSemaphore(hSemaphore[i], 1, &previous);
+ DWORD dwError = GetLastError();
+
+ if(!bRet)
+ {
+ Trace("PALSUITE ERROR: ReleaseSemaphore('%p' '%ld' '%p') "
+ "call returned %d\nwhen it should have returned "
+ "%d.\nThe semaphore count was %d and it's "
+ "lMaxCount was %d.\nGetLastError returned %d.\n",
+ hSemaphore[i], 1, &previous, bRet, TRUE, j,
+ testCases[i].lMaximumCount, dwError);
+ cleanup(i);
+ Fail("");
+ }
+ if (previous != j)
+ {
+ Trace("PALSUITE ERROR: ReleaseSemaphore('%p' '%ld' '%p') "
+ "call set %p to %d instead of %d.\n The semaphore "
+ "count was %d and GetLastError returned %d.\n",
+ hSemaphore[i], 1, &previous, &previous, previous,
+ j, j, dwError);
+ cleanup(i);
+ Fail("");
+ }
+ }
+ }
+
+ // Skip exhaustive decrement tests for too large an initial count
+ if(testCases[i].lInitialCount >= INT_MAX - 1)
+ {
+ continue;
+ }
+
+ /* decrement semaphore count to 0 */
+ for (j = testCases[i].lMaximumCount; j >= 0; j--)
+ {
+ DWORD dwRet = WaitForSingleObject(hSemaphore[i], 0);
+ DWORD dwError = GetLastError();
+
+ if (0 == j)
+ {
+ /* WaitForSingleObject should report that the
+ semaphore is nonsignaled */
+ if (WAIT_TIMEOUT != dwRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject('%p' '%u') "
+ "call returned %d\nwhen it should have returned "
+ "%d.\nThe semaphore's count was %d.\nGetLastError "
+ "returned %d.\n", hSemaphore[i], 0, dwRet,
+ WAIT_TIMEOUT, j, dwError);
+ cleanup(i);
+ Fail("");
+ }
+ }
+ else
+ {
+ /* WaitForSingleObject should report that the
+ semaphore is signaled */
+ if (WAIT_OBJECT_0 != dwRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject('%p' '%u') "
+ "call returned %d\nwhen it should have returned "
+ "%d.\nThe semaphore's count was %d.\nGetLastError "
+ "returned %d.\n", hSemaphore[i], 0, dwRet,
+ WAIT_OBJECT_0, j, dwError);
+ cleanup(i);
+ Fail("");
+ }
+ }
+ }
+ }
+ PAL_Terminate();
+ return (PASS);
+}
+
+
+
+
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/testinfo.dat b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/testinfo.dat
new file mode 100644
index 0000000000..d8cd590923
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/testinfo.dat
@@ -0,0 +1,20 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateSemaphoreA / ReleaseSemaphore
+Name = Positive Test for CreateSemaphoreA and ReleaseSemaphore
+TYPE = DEFAULT
+EXE1 = createsemaphore
+Description
+= Test attributes of CreateSemaphoreA and ReleaseSemaphore.
+= Insure for CreateSemaphore that lInitialCount and lMaximumCount
+= constraints are respected. Validate that CreateSemaphore rejects
+= conditions where, initial count and / or maximum count are negative
+= and conditions where the initial count is greater than the maximum
+= count. For ReleaseSemaphore validate that lpPreviousCount gets set
+= to the previous semaphore count and lpPreviousCount can be NULL.
+= Also establish ReleaseSemaphore fails when called in a semaphore
+= with count equal to lMaximumCount.
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/CMakeLists.txt
new file mode 100644
index 0000000000..1962ade358
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CMakeLists.txt
new file mode 100644
index 0000000000..b40f2695bc
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ CreateSemaphore.c
+)
+
+add_executable(paltest_createsemaphorew_releasesemaphore_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createsemaphorew_releasesemaphore_test1 coreclrpal)
+
+target_link_libraries(paltest_createsemaphorew_releasesemaphore_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.c b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.c
new file mode 100644
index 0000000000..854d16d0ef
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.c
@@ -0,0 +1,323 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.c
+**
+** Purpose: Test Semaphore operation using classic IPC problem:
+** "Producer-Consumer Problem".
+**
+** Dependencies: CreateThread
+** ReleaseSemaphore
+** WaitForSingleObject
+** Sleep
+** fflush
+**
+
+**
+**=========================================================*/
+
+#define UNICODE
+#include <palsuite.h>
+
+#define PRODUCTION_TOTAL 26
+
+#define _BUF_SIZE 10
+
+DWORD dwThreadId; /* consumer thread identifier */
+
+HANDLE hThread; /* handle to consumer thread */
+
+HANDLE hSemaphoreM; /* handle to mutual exclusion semaphore */
+
+HANDLE hSemaphoreE; /* handle to semaphore that counts empty buffer slots */
+
+HANDLE hSemaphoreF; /* handle to semaphore that counts full buffer slots */
+
+typedef struct Buffer
+{
+ short readIndex;
+ short writeIndex;
+ CHAR message[_BUF_SIZE];
+
+} BufferStructure;
+
+CHAR producerItems[PRODUCTION_TOTAL + 1];
+
+CHAR consumerItems[PRODUCTION_TOTAL + 1];
+
+/*
+ * Read next message from the Buffer into provided pointer.
+ * Returns: 0 on failure, 1 on success.
+ */
+int
+readBuf(BufferStructure *Buffer, char *c)
+{
+ if( Buffer -> writeIndex == Buffer -> readIndex )
+ {
+ return 0;
+ }
+ *c = Buffer -> message[Buffer -> readIndex++];
+ Buffer -> readIndex %= _BUF_SIZE;
+ return 1;
+}
+
+/*
+ * Write message generated by the producer to Buffer.
+ * Returns: 0 on failure, 1 on success.
+ */
+int
+writeBuf(BufferStructure *Buffer, CHAR c)
+{
+ if( ( ((Buffer -> writeIndex) + 1) % _BUF_SIZE) ==
+ (Buffer -> readIndex) )
+ {
+ return 0;
+ }
+ Buffer -> message[Buffer -> writeIndex++] = c;
+ Buffer -> writeIndex %= _BUF_SIZE;
+ return 1;
+}
+
+/*
+ * Atomic decrement of semaphore value.
+ */
+VOID
+down(HANDLE hSemaphore)
+{
+ switch ( (WaitForSingleObject (
+ hSemaphore,
+ 10000))) /* Wait 10 seconds */
+ {
+ case WAIT_OBJECT_0: /*
+ * Semaphore was signaled. OK to access
+ * semaphore.
+ */
+ break;
+ case WAIT_ABANDONED: /*
+ * Object was mutex object whose owning
+ * thread has terminated. Shouldn't occur.
+ */
+ Fail("WaitForSingleObject call returned 'WAIT_ABANDONED'.\n"
+ "Failing Test.\n");
+ break;
+ case WAIT_FAILED: /* WaitForSingleObject function failed */
+ Fail("WaitForSingleObject call returned 'WAIT_FAILED'.\n"
+ "GetLastError returned %d\nFailing Test.\n",GetLastError());
+ break;
+ default:
+ Fail("WaitForSingleObject call returned an unexpected value.\n"
+ "Failing Test.\n");
+ break;
+ }
+
+}
+
+/*
+ * Atomic increment of semaphore value.
+ */
+VOID
+up(HANDLE hSemaphore)
+{
+ if (!ReleaseSemaphore (
+ hSemaphore,
+ 1,
+ NULL)
+ )
+ {
+ Fail("ReleaseSemaphore call failed. GetLastError returned %d\n",
+ GetLastError());
+ }
+}
+
+/*
+ * Sleep 500 milleseconds.
+ */
+VOID
+consumerSleep(VOID)
+{
+ Sleep(500);
+}
+
+/*
+ * Sleep between 10 milleseconds.
+ */
+VOID
+producerSleep(VOID)
+{
+ Sleep(10);
+}
+
+/*
+ * Produce a message and write the message to Buffer.
+ */
+VOID
+producer(BufferStructure *Buffer)
+{
+
+ int n = 0;
+ char c;
+
+ while (n < PRODUCTION_TOTAL)
+ {
+ c = 'A' + n ; /* Produce Item */
+
+ down(hSemaphoreE);
+ down(hSemaphoreM);
+
+ if (writeBuf(Buffer, c))
+ {
+ Trace("Producer produces %c.\n", c);
+ fflush(stdout);
+ producerItems[n++] = c;
+ }
+
+ up(hSemaphoreM);
+ up(hSemaphoreF);
+
+ producerSleep();
+ }
+
+ return;
+}
+
+/*
+ * Read and "Consume" the messages in Buffer.
+ */
+DWORD
+PALAPI
+consumer( LPVOID lpParam )
+{
+ int n = 0;
+ char c;
+
+ consumerSleep();
+
+ while (n < PRODUCTION_TOTAL)
+ {
+
+ down(hSemaphoreF);
+ down(hSemaphoreM);
+
+ if (readBuf((BufferStructure*)lpParam, &c))
+ {
+ Trace("\tConsumer consumes %c.\n", c);
+ fflush(stdout);
+ consumerItems[n++] = c;
+ }
+
+ up(hSemaphoreM);
+ up(hSemaphoreE);
+
+ consumerSleep();
+ }
+
+ return 0;
+}
+
+int __cdecl main (int argc, char **argv)
+{
+
+ BufferStructure Buffer, *pBuffer;
+
+ pBuffer = &Buffer;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ /*
+ * Create Semaphores
+ */
+ hSemaphoreM = CreateSemaphoreW (
+ NULL,
+ 1,
+ 1,
+ NULL);
+
+ if ( NULL == hSemaphoreM )
+ {
+ Fail ( "hSemaphoreM = CreateSemaphoreW () - returned NULL\n"
+ "Failing Test.\nGetLastError returned %d\n", GetLastError());
+ }
+
+
+ hSemaphoreE = CreateSemaphoreW (
+ NULL,
+ _BUF_SIZE ,
+ _BUF_SIZE ,
+ NULL);
+
+ if ( NULL == hSemaphoreE )
+ {
+ Fail ( "hSemaphoreE = CreateSemaphoreW () - returned NULL\n"
+ "Failing Test.\nGetLastError returned %d\n", GetLastError());
+ }
+
+ hSemaphoreF = CreateSemaphoreW (
+ NULL,
+ 0,
+ _BUF_SIZE ,
+ NULL);
+
+ if ( NULL == hSemaphoreF )
+ {
+ Fail ( "hSemaphoreF = CreateSemaphoreW () - returned NULL\n"
+ "Failing Test.\nGetLastError returned %d\n", GetLastError());
+ }
+
+
+ /*
+ * Initialize Buffer
+ */
+ pBuffer->writeIndex = pBuffer->readIndex = 0;
+
+ /*
+ * Create Consumer
+ */
+ hThread = CreateThread(
+ NULL,
+ 0,
+ consumer,
+ &Buffer,
+ 0,
+ &dwThreadId);
+
+ if ( NULL == hThread )
+ {
+ Fail ( "CreateThread() returned NULL. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ /*
+ * Start producing
+ */
+ producer(pBuffer);
+
+ /*
+ * Wait for consumer to complete
+ */
+ WaitForSingleObject (hThread, INFINITE);
+
+ /*
+ * Compare items produced vs. items consumed
+ */
+ if ( 0 != strncmp (producerItems, consumerItems, PRODUCTION_TOTAL) )
+ {
+ Fail("The producerItems string %s\n and the consumerItems string "
+ "%s\ndo not match. This could be a problem with the strncmp()"
+ " function\n FailingTest\nGetLastError() returned %d\n",
+ producerItems, consumerItems, GetLastError());
+ }
+
+ Trace ("producerItems and consumerItems arrays match. All %d\nitems "
+ "were produced and consumed in order.\nTest passed.\n",
+ PRODUCTION_TOTAL);
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/testinfo.dat b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/testinfo.dat
new file mode 100644
index 0000000000..9127589333
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/testinfo.dat
@@ -0,0 +1,18 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateSemaphoreW / ReleaseSemaphore
+Name = Positive Test for CreateSemaphoreW and ReleaseSemaphore
+TYPE = DEFAULT
+EXE1 = createsemaphore
+Description
+= Implementation of Producer / Consumer IPC problem using CreateSemaphoreW
+= and ReleaseSemaphore functions. This test case exercises CreateSemaphoreW
+= , ReleaseSemaphore, CreateThread and WaitForSingleObject functions.
+= Since there is no way to currently create "pseudo" random events in the
+= pal, this example does not behave as classic bounded buffers would. This
+= test case is designed to starve the consumer and have the producer fill
+= the buffer.
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CMakeLists.txt
new file mode 100644
index 0000000000..b14284d08f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ CreateSemaphore.c
+)
+
+add_executable(paltest_createsemaphorew_releasesemaphore_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createsemaphorew_releasesemaphore_test2 coreclrpal)
+
+target_link_libraries(paltest_createsemaphorew_releasesemaphore_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CreateSemaphore.c b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CreateSemaphore.c
new file mode 100644
index 0000000000..62532737ac
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CreateSemaphore.c
@@ -0,0 +1,314 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.c
+**
+** Purpose: Test Semaphore operation using classic IPC problem:
+** "Producer-Consumer Problem".
+**
+** Dependencies: CreateThread
+** ReleaseSemaphore
+** WaitForSingleObject
+** Sleep
+** fflush
+**
+
+**
+**=========================================================*/
+
+#define UNICODE
+#include <palsuite.h>
+
+#define PRODUCTION_TOTAL 26
+
+#define _BUF_SIZE 10
+
+DWORD dwThreadId; /* consumer thread identifier */
+
+HANDLE hThread; /* handle to consumer thread */
+
+HANDLE hSemaphoreM; /* handle to mutual exclusion semaphore */
+
+HANDLE hSemaphoreE; /* handle to semaphore that counts empty buffer slots */
+
+HANDLE hSemaphoreF; /* handle to semaphore that counts full buffer slots */
+
+typedef struct Buffer
+{
+ short readIndex;
+ short writeIndex;
+ CHAR message[_BUF_SIZE];
+
+} BufferStructure;
+
+CHAR producerItems[PRODUCTION_TOTAL + 1];
+
+CHAR consumerItems[PRODUCTION_TOTAL + 1];
+
+/*
+ * Read next message from the Buffer into provided pointer.
+ * Returns: 0 on failure, 1 on success.
+ */
+int
+readBuf(BufferStructure *Buffer, char *c)
+{
+ if( Buffer -> writeIndex == Buffer -> readIndex )
+ {
+ return 0;
+ }
+ *c = Buffer -> message[Buffer -> readIndex++];
+ Buffer -> readIndex %= _BUF_SIZE;
+ return 1;
+}
+
+/*
+ * Write message generated by the producer to Buffer.
+ * Returns: 0 on failure, 1 on success.
+ */
+int
+writeBuf(BufferStructure *Buffer, CHAR c)
+{
+ if( ( ((Buffer -> writeIndex) + 1) % _BUF_SIZE) ==
+ (Buffer -> readIndex) )
+ {
+ return 0;
+ }
+ Buffer -> message[Buffer -> writeIndex++] = c;
+ Buffer -> writeIndex %= _BUF_SIZE;
+ return 1;
+}
+
+/*
+ * Atomic decrement of semaphore value.
+ */
+VOID
+down(HANDLE hSemaphore)
+{
+ switch ( (WaitForSingleObject (
+ hSemaphore,
+ 10000)))
+ {
+ case WAIT_OBJECT_0: /*
+ * Semaphore was signaled. OK to access semaphore.
+ */
+ break;
+ case WAIT_ABANDONED: /*
+ * Object was mutex object whose owning
+ * thread has terminated. Shouldn't occur.
+ */
+ Fail("WaitForSingleObject call returned 'WAIT_ABANDONED'.\n"
+ "Failing Test.\n");
+ break;
+ case WAIT_FAILED: /* WaitForSingleObject function failed */
+ Fail("WaitForSingleObject call returned 'WAIT_FAILED'.\n"
+ "GetLastError returned %d\nFailing Test.\n",GetLastError());
+ break;
+ default:
+ Fail("WaitForSingleObject call returned an unexpected value.\n"
+ "Failing Test.\n");
+ break;
+ }
+
+}
+
+/*
+ * Atomic increment of semaphore value.
+ */
+VOID
+up(HANDLE hSemaphore)
+{
+ if (!ReleaseSemaphore (
+ hSemaphore,
+ 1,
+ NULL)
+ )
+ {
+ Fail("ReleaseSemaphore call failed. GetLastError returned %d\n",
+ GetLastError());
+ }
+}
+
+/*
+ * Sleep 10 milleseconds.
+ */
+VOID
+consumerSleep(VOID)
+{
+ Sleep(10);
+}
+
+/*
+ * Sleep 500 milleseconds.
+ */
+VOID
+producerSleep(VOID)
+{
+ Sleep(500);
+}
+
+/*
+ * Produce a message and write the message to Buffer.
+ */
+VOID
+producer(BufferStructure *Buffer)
+{
+
+ int n = 0;
+ char c;
+
+ while (n < PRODUCTION_TOTAL)
+ {
+ c = 'A' + n ; /* Produce Item */
+
+ down(hSemaphoreE);
+ down(hSemaphoreM);
+
+ if (writeBuf(Buffer, c))
+ {
+ Trace("Producer produces %c.\n", c);
+ fflush(stdout);
+ producerItems[n++] = c;
+ }
+
+ up(hSemaphoreM);
+ up(hSemaphoreF);
+
+ producerSleep();
+ }
+ return;
+}
+
+/*
+ * Read and "Consume" the messages in Buffer.
+ */
+DWORD
+PALAPI
+consumer( LPVOID lpParam )
+{
+ int n = 0;
+ char c;
+
+ consumerSleep();
+
+ while (n < PRODUCTION_TOTAL)
+ {
+
+ down(hSemaphoreF);
+ down(hSemaphoreM);
+
+ if (readBuf((BufferStructure*)lpParam, &c))
+ {
+ Trace("\tConsumer consumes %c.\n", c);
+ fflush(stdout);
+ consumerItems[n++] = c;
+ }
+
+ up(hSemaphoreM);
+ up(hSemaphoreE);
+
+ consumerSleep();
+ }
+ return 0;
+}
+
+int __cdecl main (int argc, char **argv)
+{
+
+ BufferStructure Buffer, *pBuffer;
+
+ pBuffer = &Buffer;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return (FAIL);
+ }
+
+ /*
+ * Create Semaphores
+ */
+ hSemaphoreM = CreateSemaphoreW (
+ NULL,
+ 1,
+ 1,
+ NULL);
+
+ if ( NULL == hSemaphoreM )
+ {
+ Fail ( "hSemaphoreM = CreateSemaphoreW () - returned NULL\n"
+ "Failing Test.\n");
+ }
+
+ hSemaphoreE = CreateSemaphoreW (
+ NULL,
+ _BUF_SIZE ,
+ _BUF_SIZE ,
+ NULL);
+
+ if ( NULL == hSemaphoreE )
+ {
+ Fail ( "hSemaphoreE = CreateSemaphoreW () - returned NULL\n"
+ "Failing Test.\n");
+ }
+
+ hSemaphoreF = CreateSemaphoreW (
+ NULL,
+ 0,
+ _BUF_SIZE ,
+ NULL);
+
+ if ( NULL == hSemaphoreF )
+ {
+ Fail ( "hSemaphoreF = CreateSemaphoreW () - returned NULL\n"
+ "Failing Test.\n");
+ }
+
+ /*
+ * Initialize Buffer
+ */
+ pBuffer->writeIndex = pBuffer->readIndex = 0;
+
+ /*
+ * Create Consumer
+ */
+ hThread = CreateThread(
+ NULL,
+ 0,
+ consumer,
+ &Buffer,
+ 0,
+ &dwThreadId);
+
+ if ( NULL == hThread )
+ {
+ Fail ( "CreateThread() returned NULL. Failing test.\n");
+ }
+
+ /*
+ * Start producing
+ */
+ producer(pBuffer);
+
+ /*
+ * Wait for consumer to complete
+ */
+ WaitForSingleObject (hThread, INFINITE);
+
+ if ( 0 != strncmp (producerItems, consumerItems, PRODUCTION_TOTAL) )
+ {
+ Fail("The producerItems string %s\n and the consumerItems string "
+ "%s\ndo not match. This could be a problem with the strncmp()"
+ " function\n FailingTest\nGetLastError() returned %d\n",
+ producerItems, consumerItems, GetLastError());
+ }
+
+ Trace ("producerItems and consumerItems arrays match. All %d\nitems "
+ "were produced and consumed in order.\nTest passed.\n",
+ PRODUCTION_TOTAL);
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/testinfo.dat b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/testinfo.dat
new file mode 100644
index 0000000000..32b107fd9e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/testinfo.dat
@@ -0,0 +1,18 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateSemaphoreW / ReleaseSemaphore
+Name = Positive Test for CreateSemaphoreW and ReleaseSemaphore
+TYPE = DEFAULT
+EXE1 = createsemaphore
+Description
+= Implementation of Producer / Consumer IPC problem using CreateSemaphoreW
+= and ReleaseSemaphore functions. This test case exercises CreateSemaphoreW
+= , ReleaseSemaphore, CreateThread and WaitForSingleObject functions.
+= Since there is no way to currently create "pseudo" random events in the
+= pal, this example does not behave as classic bounded buffers would. This
+= test case is designed to starve the producer and have the consumer fill
+= the buffer.
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/CMakeLists.txt
new file mode 100644
index 0000000000..f7f0905761
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ createsemaphore.c
+)
+
+add_executable(paltest_createsemaphorew_releasesemaphore_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createsemaphorew_releasesemaphore_test3 coreclrpal)
+
+target_link_libraries(paltest_createsemaphorew_releasesemaphore_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/createsemaphore.c b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/createsemaphore.c
new file mode 100644
index 0000000000..ea0a5e0846
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/createsemaphore.c
@@ -0,0 +1,201 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================================
+**
+** Source: createsemaphorew_releasesemaphore/test3/createsemaphore.c
+**
+** Purpose: Test attributes of CreateSemaphoreW and ReleaseSemaphore.
+** Insure for CreateSemaphore that lInitialCount and lMaximumCount
+** constraints are respected. Validate that CreateSemaphore rejects
+** conditions where initial count and / or maximum count are negative
+** and conditions where the initial count is greater than the maximum
+** count. For ReleaseSemaphore validate that lpPreviousCount gets set
+** to the previous semaphore count and lpPreviousCount can be NULL.
+** Also establish ReleaseSemaphore fails when called in a semaphore
+** with count equal to lMaximumCount.
+**
+**
+**==========================================================================*/
+
+#include <palsuite.h>
+
+struct testcase
+{
+ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes;
+ LONG lInitialCount;
+ LONG lMaximumCount;
+ LPCWSTR lpName;
+ BOOL bNegativeTest;
+};
+
+struct testcase testCases[] =
+{
+ {NULL, -1, 1, NULL, TRUE},
+ {NULL, 1, -1, NULL, TRUE},
+ {NULL, -1, -1, NULL, TRUE},
+ {NULL, 2, 1, NULL, TRUE},
+ {NULL, 1, 2, NULL, FALSE},
+ {NULL, 0, 10, NULL, FALSE},
+ {NULL, INT_MAX - 1, INT_MAX, NULL, FALSE},
+ {NULL, INT_MAX, INT_MAX, NULL, FALSE}
+};
+
+HANDLE hSemaphore[sizeof(testCases)/sizeof(struct testcase)];
+
+BOOL cleanup(int index)
+{
+ int i;
+ BOOL bRet = TRUE;
+ for (i = 0; i < index; i++)
+ {
+ if (!CloseHandle(hSemaphore[i]))
+ {
+ bRet = FALSE;
+ Trace("PALSUITE ERROR: CloseHandle(%p) call failed for index %d\n",
+ hSemaphore[i], i);
+ }
+ }
+ return(bRet);
+}
+
+int __cdecl main (int argc, char **argv)
+{
+ int i;
+ int j;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return (FAIL);
+ }
+ /* create semaphores */
+ for (i = 0; i < sizeof(testCases)/sizeof(struct testcase); i++)
+ {
+ hSemaphore[i] = CreateSemaphoreW (testCases[i].lpSemaphoreAttributes,
+ testCases[i].lInitialCount,
+ testCases[i].lMaximumCount,
+ testCases[i].lpName);
+
+ if (NULL == hSemaphore[i])
+ {
+ if (!testCases[i].bNegativeTest)
+ {
+ Trace("PALSUITE ERROR: CreateSemaphoreW('%p' '%ld' '%ld' "
+ "'%p') returned NULL at index %d.\nGetLastError "
+ "returned %d.\n", testCases[i].lpSemaphoreAttributes,
+ testCases[i].lInitialCount, testCases[i].lMaximumCount,
+ testCases[i].lpName, i, GetLastError());
+ if (i > 0)
+ {
+ cleanup(i - 1);
+ }
+ Fail("");
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ /* increment semaphore count to lMaximumCount */
+ for (j = testCases[i].lInitialCount; (ULONG)j <= (ULONG)testCases[i].lMaximumCount;
+ j++)
+ {
+ if (testCases[i].lMaximumCount == j)
+ {
+ /* Call ReleaseSemaphore once more to ensure ReleaseSemaphore
+ fails */
+ if(ReleaseSemaphore(hSemaphore[i], 1, NULL))
+ {
+ Trace("PALSUITE ERROR: ReleaseSemaphore('%p' '%ld' '%p') "
+ "call returned %d\nwhen it should have returned "
+ "%d.\nThe semaphore's count was %d.\nGetLastError "
+ "returned %d.\n", hSemaphore[i], 1, NULL, TRUE,
+ FALSE, j, GetLastError());
+ cleanup(i);
+ Fail("");
+ }
+ }
+ else
+ {
+ int previous;
+ BOOL bRet = ReleaseSemaphore(hSemaphore[i], 1, &previous);
+ DWORD dwError = GetLastError();
+
+ if(!bRet)
+ {
+ Trace("PALSUITE ERROR: ReleaseSemaphore('%p' '%ld' '%p') "
+ "call returned %d\nwhen it should have returned "
+ "%d.\nThe semaphore count was %d and it's "
+ "lMaxCount was %d.\nGetLastError returned %d.\n",
+ hSemaphore[i], 1, &previous, bRet, TRUE, j,
+ testCases[i].lMaximumCount, dwError);
+ cleanup(i);
+ Fail("");
+ }
+ if (previous != j)
+ {
+ Trace("PALSUITE ERROR: ReleaseSemaphore('%p' '%ld' '%p') "
+ "call set %p to %d instead of %d.\n The semaphore "
+ "count was %d and GetLastError returned %d.\n",
+ hSemaphore[i], 1, &previous, &previous, previous,
+ j, j, dwError);
+ cleanup(i);
+ Fail("");
+ }
+ }
+ }
+
+ // Skip exhaustive decrement tests for too large an initial count
+ if(testCases[i].lInitialCount >= INT_MAX - 1)
+ {
+ continue;
+ }
+
+ /* decrement semaphore count to 0 */
+ for (j = testCases[i].lMaximumCount; j >= 0; j--)
+ {
+ DWORD dwRet = WaitForSingleObject(hSemaphore[i], 0);
+ DWORD dwError = GetLastError();
+
+ if (0 == j)
+ {
+ /* WaitForSingleObject should report that the
+ semaphore is nonsignaled */
+ if (WAIT_TIMEOUT != dwRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject('%p' '%u') "
+ "call returned %d\nwhen it should have returned "
+ "%d.\nThe semaphore's count was %d.\nGetLastError "
+ "returned %d.\n", hSemaphore[i], 0, dwRet,
+ WAIT_TIMEOUT, j, dwError);
+ cleanup(i);
+ Fail("");
+ }
+ }
+ else
+ {
+ /* WaitForSingleObject should report that the
+ semaphore is signaled */
+ if (WAIT_OBJECT_0 != dwRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject('%p' '%u') "
+ "call returned %d\nwhen it should have returned "
+ "%d.\nThe semaphore's count was %d.\nGetLastError "
+ "returned %d.\n", hSemaphore[i], 0, dwRet,
+ WAIT_OBJECT_0, j, dwError);
+ cleanup(i);
+ Fail("");
+ }
+ }
+ }
+ }
+ PAL_Terminate();
+ return (PASS);
+}
+
+
+
+
+
diff --git a/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/testinfo.dat b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/testinfo.dat
new file mode 100644
index 0000000000..beaac95f97
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/testinfo.dat
@@ -0,0 +1,20 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateSemaphoreW / ReleaseSemaphore
+Name = Positive Test for CreateSemaphoreW and ReleaseSemaphore
+TYPE = DEFAULT
+EXE1 = createsemaphore
+Description
+= Test attributes of CreateSemaphoreW and ReleaseSemaphore.
+= Insure for CreateSemaphore that lInitialCount and lMaximumCount
+= constraints are respected. Validate that CreateSemaphore rejects
+= conditions where, initial count and / or maximum count are negative
+= and conditions where the initial count is greater than the maximum
+= count. For ReleaseSemaphore validate that lpPreviousCount gets set
+= to the previous semaphore count and lpPreviousCount can be NULL.
+= Also establish ReleaseSemaphore fails when called in a semaphore
+= with count equal to lMaximumCount.
diff --git a/src/pal/tests/palsuite/threading/CreateThread/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateThread/CMakeLists.txt
new file mode 100644
index 0000000000..1962ade358
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+
diff --git a/src/pal/tests/palsuite/threading/CreateThread/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateThread/test1/CMakeLists.txt
new file mode 100644
index 0000000000..d3921c4409
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_createthread_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createthread_test1 coreclrpal)
+
+target_link_libraries(paltest_createthread_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateThread/test1/test1.c b/src/pal/tests/palsuite/threading/CreateThread/test1/test1.c
new file mode 100644
index 0000000000..2dd2f6acb6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test1/test1.c
@@ -0,0 +1,119 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test1.c
+**
+** Purpose: Test for CreateThread. Call CreateThread and ensure
+** that it succeeds. Also check to ensure the paramater is passed
+** properly.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+#ifndef PLATFORM_UNIX
+#define LLFORMAT "%I64u"
+#else
+#define LLFORMAT "%llu"
+#endif
+
+ULONGLONG dwCreateThreadTestParameter = 0;
+
+DWORD PALAPI CreateThreadTestThread( LPVOID lpParameter)
+{
+ DWORD dwRet = 0;
+
+ /* save parameter for test */
+ dwCreateThreadTestParameter = (ULONGLONG)lpParameter;
+
+ return dwRet;
+}
+
+BOOL CreateThreadTest()
+{
+ BOOL bRet = FALSE;
+ DWORD dwRet = 0;
+
+ LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL;
+ DWORD dwStackSize = 0;
+ LPTHREAD_START_ROUTINE lpStartAddress = &CreateThreadTestThread;
+ LPVOID lpParameter = lpStartAddress;
+ DWORD dwCreationFlags = 0; /* run immediately */
+ DWORD dwThreadId = 0;
+
+ HANDLE hThread = 0;
+
+ dwCreateThreadTestParameter = 0;
+
+ /* Create a thread, passing the appropriate paramaters as declared
+ above.
+ */
+
+ hThread = CreateThread( lpThreadAttributes,
+ dwStackSize,
+ lpStartAddress,
+ lpParameter,
+ dwCreationFlags,
+ &dwThreadId );
+
+ /* Ensure that the HANDLE is not invalid! */
+ if (hThread != INVALID_HANDLE_VALUE)
+ {
+ dwRet = WaitForSingleObject(hThread,INFINITE);
+
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Trace("CreateThreadTest:WaitForSingleObject "
+ "failed (%x)\n",GetLastError());
+ }
+ else
+ {
+ /* Check to ensure that the parameter passed to the thread
+ function is the same in the function as what we passed.
+ */
+
+ if (dwCreateThreadTestParameter != (ULONGLONG)lpParameter)
+ {
+ Trace("CreateThreadTest:parameter error. The "
+ "parameter passed should have been " LLFORMAT " but when "
+ "passed to the Thread function it was " LLFORMAT " . GetLastError[%x]\n",
+ dwCreateThreadTestParameter,lpParameter, GetLastError());
+ }
+ else
+ {
+ bRet = TRUE;
+ }
+
+ }
+ CloseHandle(hThread);
+ }
+ else
+ {
+ Trace("CreateThreadTest:CreateThread failed (%x)\n",GetLastError());
+ }
+
+ return bRet;
+}
+
+
+int __cdecl main(int argc, char **argv)
+{
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!CreateThreadTest())
+ {
+ Fail ("Test failed\n");
+ }
+
+ Trace("Test Passed\n");
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateThread/test1/testinfo.dat b/src/pal/tests/palsuite/threading/CreateThread/test1/testinfo.dat
new file mode 100644
index 0000000000..3ae70625c2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test1/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateThread
+Name = Positive Test for CreateThread
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Test for CreateThread. Call CreateThread and ensure
+= that it succeeds. Also check to ensure the paramater is passed
+= properly.
diff --git a/src/pal/tests/palsuite/threading/CreateThread/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateThread/test2/CMakeLists.txt
new file mode 100644
index 0000000000..350cf66e9b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_createthread_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createthread_test2 coreclrpal)
+
+target_link_libraries(paltest_createthread_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateThread/test2/test2.c b/src/pal/tests/palsuite/threading/CreateThread/test2/test2.c
new file mode 100644
index 0000000000..f64c32ea87
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test2/test2.c
@@ -0,0 +1,184 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*===========================================================
+**
+** Source: test2.c
+**
+** Purpose: Test that lpThreadId is assigned the correct
+** threadId value and that lpThreadId can be NULL.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+#define NUM_TESTS 3
+
+HANDLE hThread[NUM_TESTS];
+DWORD dwThreadId[NUM_TESTS];
+volatile BOOL bResult[NUM_TESTS];
+volatile DWORD dwThreadId1[NUM_TESTS];
+
+DWORD PALAPI Thread( LPVOID lpParameter)
+{
+ dwThreadId1[(DWORD) lpParameter] = GetCurrentThreadId();
+ bResult[(DWORD) lpParameter] = TRUE;
+ return (DWORD) lpParameter;
+}
+
+struct testCase
+{
+ LPSECURITY_ATTRIBUTES lpThreadAttributes;
+ DWORD dwStackSize;
+ LPTHREAD_START_ROUTINE lpStartAddress;
+ DWORD dwCreationFlags;
+ LPVOID lpThreadId;
+};
+
+struct testCase testCases[]=
+{
+ {NULL, 0, &Thread, 0, NULL},
+ {NULL, 0, &Thread, CREATE_SUSPENDED, NULL},
+ {NULL, 0, &Thread, 0, (LPVOID) 1}
+};
+
+/*
+ * close handles
+ */
+BOOL cleanup(int index)
+{
+ int i;
+ BOOL bRet = TRUE;
+
+ for (i = 0; i < index; i++)
+ {
+ if (!CloseHandle(hThread[i]))
+ {
+ bRet = FALSE;
+ Trace("PALSUITE ERROR: CloseHandle(%p) call failed for index %d\n",
+ hThread[i], i);
+ }
+ }
+
+ return(bRet);
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ SIZE_T i;
+ DWORD dwRetWFSO;
+ DWORD dwRetRT;
+ BOOL bRet = TRUE;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return (FAIL);
+ }
+
+ /* set results array to FALSE */
+ for (i = 0; i < NUM_TESTS; i++)
+ {
+ bResult[i]=FALSE;
+ dwThreadId[i]=0;
+ }
+
+ for (i = 0; i < NUM_TESTS; i++)
+ {
+ if (NULL != testCases[i].lpThreadId)
+ {
+ testCases[i].lpThreadId = &dwThreadId[i];
+ }
+ /* pass the index as the thread argument */
+ hThread[i] = CreateThread( testCases[i].lpThreadAttributes,
+ testCases[i].dwStackSize,
+ testCases[i].lpStartAddress,
+ (LPVOID) i,
+ testCases[i].dwCreationFlags,
+ testCases[i].lpThreadId);
+ if (hThread[i] == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateThread('%p' '%d' '%p' '%p' '%d' "
+ "'%p') call failed.\nGetLastError returned '%u'.\n",
+ testCases[i].lpThreadAttributes, testCases[i].dwStackSize,
+ testCases[i].lpStartAddress, (LPVOID) i,
+ testCases[i].dwCreationFlags,
+ testCases[i].lpThreadId, GetLastError());
+ cleanup(i - 1);
+ Fail("");
+ }
+
+ /* Resume suspended threads */
+ if (testCases[i].dwCreationFlags == CREATE_SUSPENDED)
+ {
+ dwRetRT = ResumeThread (hThread[i]);
+ if (dwRetRT != 1)
+ {
+ Trace ("PALSUITE ERROR: ResumeThread(%p) "
+ "call returned %d it should have returned %d.\n"
+ "GetLastError returned %u.\n", hThread[i], dwRetRT,
+ 1, GetLastError());
+ cleanup(i);
+ Fail("");
+ }
+ }
+ }
+
+ /* cleanup */
+ for (i = 0; i < NUM_TESTS; i++)
+ {
+ dwRetWFSO = WaitForSingleObject(hThread[i], 10000);
+ if (dwRetWFSO != WAIT_OBJECT_0)
+ {
+ Trace ("PALSUITE ERROR: WaitForSingleObject('%p' '%d') "
+ "call returned %d instead of WAIT_OBJECT_0 ('%d').\n"
+ "GetLastError returned %u.\n", hThread[i], 10000,
+ dwRetWFSO, WAIT_OBJECT_0, GetLastError());
+ cleanup(i);
+ Fail("");
+ }
+ }
+ if(!cleanup(NUM_TESTS))
+ {
+ Fail("");
+ }
+
+ for (i = 0; i < NUM_TESTS; i++)
+ {
+ /*
+ * check to see that all threads were created and were passed
+ * the array index as an argument.
+ */
+ if (FALSE == bResult[i])
+ {
+ bRet = FALSE;
+ Trace("PALSUITE ERROR: result[%d]=%d. It should be %d\n", i,
+ FALSE, TRUE);
+ }
+ /*
+ * check to see that lpThreadId received the correct value.
+ */
+ if (0 != dwThreadId[i])
+ {
+ if (dwThreadId[i] != dwThreadId1[i])
+ {
+ bRet = FALSE;
+ Trace("PALSUITE ERROR: dwThreadId[%d]=%p and dwThreadId1[%d]"
+ "=%p\nThese values should be identical.\n", i,
+ dwThreadId[i], i, dwThreadId1[i]);
+ }
+ }
+ }
+ if (!bRet)
+ {
+ cleanup(NUM_TESTS);
+ Fail("");
+ }
+
+ PAL_Terminate();
+ return (PASS);
+}
+
+
+
diff --git a/src/pal/tests/palsuite/threading/CreateThread/test2/testinfo.dat b/src/pal/tests/palsuite/threading/CreateThread/test2/testinfo.dat
new file mode 100644
index 0000000000..0333beb360
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test2/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateThread
+Name = Positive Test for CreateThread
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Test that lpThreadId is assigned the correct threadId value and
+= that lpThreadId can be NULL.
diff --git a/src/pal/tests/palsuite/threading/CreateThread/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateThread/test3/CMakeLists.txt
new file mode 100644
index 0000000000..923d7a72ab
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test3.c
+)
+
+add_executable(paltest_createthread_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_createthread_test3 coreclrpal)
+
+target_link_libraries(paltest_createthread_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateThread/test3/test3.c b/src/pal/tests/palsuite/threading/CreateThread/test3/test3.c
new file mode 100644
index 0000000000..0c44d1fdd0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test3/test3.c
@@ -0,0 +1,101 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*===========================================================
+**
+** Source: test3.c
+**
+** Purpose: Check to see that the handle CreateThread returns
+** can be closed while the thread is still running.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+HANDLE hThread;
+HANDLE hEvent;
+
+DWORD PALAPI Thread( LPVOID lpParameter)
+{
+ DWORD dwRet;
+ dwRet = WaitForSingleObject(hEvent, INFINITE);
+ /* if this thread continues beyond here, fail */
+ Fail("");
+
+ return 0;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ DWORD dwThreadId;
+ DWORD dwRet;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return (FAIL);
+ }
+
+ hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (hEvent == NULL)
+ {
+ Fail("PALSUITE ERROR: CreateEvent call #0 failed. GetLastError "
+ "returned %u.\n", GetLastError());
+ }
+
+ /* pass the index as the thread argument */
+ hThread = CreateThread( NULL,
+ 0,
+ &Thread,
+ (LPVOID) 0,
+ 0,
+ &dwThreadId);
+ if (hThread == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateThread('%p' '%d' '%p' '%p' '%d' '%p') "
+ "call failed.\nGetLastError returned '%u'.\n", NULL,
+ 0, &Thread, (LPVOID) 0, 0, &dwThreadId, GetLastError());
+ if (0 == CloseHandle(hEvent))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%u'.\n", hEvent);
+ }
+ Fail("");
+ }
+
+ dwRet = WaitForSingleObject(hThread, 10000);
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace ("PALSUITE ERROR: WaitForSingleObject('%p' '%d') "
+ "call returned %d instead of WAIT_TIMEOUT ('%d').\n"
+ "GetLastError returned '%u'.\n", hThread, 10000,
+ dwRet, WAIT_TIMEOUT, GetLastError());
+ Fail("");
+ }
+
+ if (0 == CloseHandle(hThread))
+ {
+ Trace("PALSUITE ERROR: Unable to CloseHandle(%p) on a running thread."
+ "\nGetLastError returned '%u'.\n", hThread, GetLastError());
+ if (0 == CloseHandle(hEvent))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "cleanup.\nGetLastError returned '%u'.\n", hEvent,
+ GetLastError());
+ }
+ Fail("");
+ }
+ if (0 == CloseHandle(hEvent))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "cleanup.\nGetLastError returned '%u'.\n", hEvent,
+ GetLastError());
+ Fail("");
+ }
+
+ PAL_Terminate();
+ return (PASS);
+}
+
diff --git a/src/pal/tests/palsuite/threading/CreateThread/test3/testinfo.dat b/src/pal/tests/palsuite/threading/CreateThread/test3/testinfo.dat
new file mode 100644
index 0000000000..712c3a6652
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test3/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateThread
+Name = Positive Test for CreateThread
+TYPE = DEFAULT
+EXE1 = test3
+Description
+= Check to see that the handle CreateThread returns can be closed while
+= the thread is still running.
+
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/CMakeLists.txt b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/CMakeLists.txt
new file mode 100644
index 0000000000..b7c6e394b0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+add_subdirectory(test4)
+add_subdirectory(test5)
+add_subdirectory(test6)
+add_subdirectory(test7)
+add_subdirectory(test8)
+
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/CMakeLists.txt
new file mode 100644
index 0000000000..5ba04fd801
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ InitializeCriticalSection.c
+)
+
+add_executable(paltest_criticalsectionfunctions_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_criticalsectionfunctions_test1 coreclrpal)
+
+target_link_libraries(paltest_criticalsectionfunctions_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/InitializeCriticalSection.c b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/InitializeCriticalSection.c
new file mode 100644
index 0000000000..f294cea472
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/InitializeCriticalSection.c
@@ -0,0 +1,235 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: criticalsectionfunctions/test1/initializecriticalsection.c
+**
+** Purpose: Test Semaphore operation using classic IPC problem:
+** "Producer-Consumer Problem".
+**
+** Dependencies: CreateThread
+** InitializeCriticalSection
+** EnterCriticalSection
+** LeaveCriticalSection
+** DeleteCriticalSection
+** WaitForSingleObject
+** Sleep
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+#define PRODUCTION_TOTAL 26
+
+#define _BUF_SIZE 10
+
+DWORD dwThreadId; /* consumer thread identifier */
+
+HANDLE hThread; /* handle to consumer thread */
+
+CRITICAL_SECTION CriticalSectionM; /* Critical Section Object (used as mutex) */
+
+typedef struct Buffer
+{
+ short readIndex;
+ short writeIndex;
+ CHAR message[_BUF_SIZE];
+
+} BufferStructure;
+
+CHAR producerItems[PRODUCTION_TOTAL + 1];
+
+CHAR consumerItems[PRODUCTION_TOTAL + 1];
+
+/*
+ * Read next message from the Buffer into provided pointer.
+ * Returns: 0 on failure, 1 on success.
+ */
+int
+readBuf(BufferStructure *Buffer, char *c)
+{
+ if( Buffer -> writeIndex == Buffer -> readIndex )
+ {
+ return 0;
+ }
+ *c = Buffer -> message[Buffer -> readIndex++];
+ Buffer -> readIndex %= _BUF_SIZE;
+ return 1;
+}
+
+/*
+ * Write message generated by the producer to Buffer.
+ * Returns: 0 on failure, 1 on success.
+ */
+int
+writeBuf(BufferStructure *Buffer, CHAR c)
+{
+ if( ( ((Buffer -> writeIndex) + 1) % _BUF_SIZE) ==
+ (Buffer -> readIndex) )
+ {
+ return 0;
+ }
+ Buffer -> message[Buffer -> writeIndex++] = c;
+ Buffer -> writeIndex %= _BUF_SIZE;
+ return 1;
+}
+
+/*
+ * Sleep 500 milleseconds.
+ */
+VOID
+consumerSleep(VOID)
+{
+ Sleep(500);
+}
+
+/*
+ * Sleep between 10 milleseconds.
+ */
+VOID
+producerSleep(VOID)
+{
+ Sleep(10);
+}
+
+/*
+ * Produce a message and write the message to Buffer.
+ */
+VOID
+producer(BufferStructure *Buffer)
+{
+
+ int n = 0;
+ char c;
+
+ while (n < PRODUCTION_TOTAL)
+ {
+ c = 'A' + n ; /* Produce Item */
+
+ EnterCriticalSection(&CriticalSectionM);
+
+ if (writeBuf(Buffer, c))
+ {
+ printf("Producer produces %c.\n", c);
+ producerItems[n++] = c;
+ }
+
+ LeaveCriticalSection(&CriticalSectionM);
+
+ producerSleep();
+ }
+
+ return;
+}
+
+/*
+ * Read and "Consume" the messages in Buffer.
+ */
+DWORD
+PALAPI
+consumer( LPVOID lpParam )
+{
+ int n = 0;
+ char c;
+
+ consumerSleep();
+
+ while (n < PRODUCTION_TOTAL)
+ {
+
+ EnterCriticalSection(&CriticalSectionM);
+
+ if (readBuf((BufferStructure*)lpParam, &c))
+ {
+ printf("\tConsumer consumes %c.\n", c);
+ consumerItems[n++] = c;
+ }
+
+ LeaveCriticalSection(&CriticalSectionM);
+
+ consumerSleep();
+ }
+
+ return 0;
+}
+
+int __cdecl main (int argc, char **argv)
+{
+
+ BufferStructure Buffer, *pBuffer;
+
+ pBuffer = &Buffer;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /*
+ * Create mutual exclusion mechanisms
+ */
+
+ InitializeCriticalSection ( &CriticalSectionM );
+
+ /*
+ * Initialize Buffer
+ */
+ pBuffer->writeIndex = pBuffer->readIndex = 0;
+
+
+
+ /*
+ * Create Consumer
+ */
+ hThread = CreateThread(
+ NULL,
+ 0,
+ consumer,
+ &Buffer,
+ 0,
+ &dwThreadId);
+
+ if ( NULL == hThread )
+ {
+ Fail ( "CreateThread() returned NULL. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ /*
+ * Start producing
+ */
+ producer(pBuffer);
+
+ /*
+ * Wait for consumer to complete
+ */
+ WaitForSingleObject (hThread, INFINITE);
+
+ /*
+ * Compare items produced vs. items consumed
+ */
+ if ( 0 != strncmp (producerItems, consumerItems, PRODUCTION_TOTAL) )
+ {
+ Fail("The producerItems string %s\n and the consumerItems string "
+ "%s\ndo not match. This could be a problem with the strncmp()"
+ " function\n FailingTest\nGetLastError() returned %d\n",
+ producerItems, consumerItems, GetLastError());
+ }
+
+ /*
+ * Clean up Critical Section object
+ */
+ DeleteCriticalSection(&CriticalSectionM);
+
+ Trace("producerItems and consumerItems arrays match. All %d\nitems "
+ "were produced and consumed in order.\nTest passed.\n",
+ PRODUCTION_TOTAL);
+
+ PAL_Terminate();
+ return (PASS);
+
+}
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/testinfo.dat b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/testinfo.dat
new file mode 100644
index 0000000000..494b899b90
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/testinfo.dat
@@ -0,0 +1,21 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = InitializeCriticalSection / EnterCriticalSection / LeaveCriticalSection / DeleteCriticalSection
+Name = Positive Test for InitializeCriticalSection, EnterCriticalSection, LeaveCriticalSection and DeleteCriticalSection
+TYPE = DEFAULT
+EXE1 = initializecriticalsection
+Description
+= Implementation of Producer / Consumer IPC problem using the
+= "CriticalSection" functions to provide a mutual exclusion mechanism.
+= This test case exercises InitializeCriticalSection, EnterCriticalSection,
+= LeaveCriticalSection, DeleteCriticalSection, and WaitForSingleObject
+= functions.
+= This case doesn't work with more than one producer and one consumer.
+= The producer thread and consumer thread each take turns blocking on
+= the CriticalSection object and do not have any other synchronization
+= mechanisms. This prevents adding producers or consumers as there are
+= no mechanisms to block them once the buffer is full.
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/CMakeLists.txt
new file mode 100644
index 0000000000..d26658cf5d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_criticalsectionfunctions_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_criticalsectionfunctions_test2 coreclrpal)
+
+target_link_libraries(paltest_criticalsectionfunctions_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/test2.c b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/test2.c
new file mode 100644
index 0000000000..47659a1c18
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/test2.c
@@ -0,0 +1,226 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CriticalSectionFunctions/test2/test2.c
+**
+** Purpose: Test that we are able to nest critical section calls.
+** The initial thread makes a call to EnterCriticalSection once,
+** blocking on a CRITICAL_SECTION object and creates a new thread.
+** The newly created thread blocks on the same CRITICAL_SECTION object.
+** The first thread now makes a call to LeaveCriticalSection.
+** Test to see that the new thread doesn't get unblocked.
+**
+** Dependencies: CreateThread
+** InitializeCriticalSection
+** EnterCriticalSection
+** LeaveCriticalSection
+** DeleteCriticalSection
+** WaitForSingleObject
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+CRITICAL_SECTION CriticalSection;
+
+volatile BOOL t0_tflag = FAIL; /* thread 0 timeout flag */
+volatile BOOL t1_aflag = FAIL; /* thread 1 access flag */
+volatile BOOL t1_cflag = FAIL; /* thread 1 critical section flag */
+volatile BOOL bTestResult = FAIL;
+
+DWORD PALAPI Thread(LPVOID lpParam)
+{
+ t1_aflag = PASS;
+ EnterCriticalSection(&CriticalSection);
+ t1_cflag = PASS;
+ LeaveCriticalSection(&CriticalSection);
+ return 0;
+}
+
+int __cdecl main (int argc, char **argv)
+{
+ HANDLE hThread;
+ DWORD dwThreadId;
+ DWORD dwRet;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return (bTestResult);
+ }
+
+ /*
+ * Create critical section object and enter it
+ */
+ InitializeCriticalSection ( &CriticalSection );
+ EnterCriticalSection(&CriticalSection);
+
+ /*
+ * Create a suspended thread
+ */
+ hThread = CreateThread(NULL,
+ 0,
+ &Thread,
+ (LPVOID) NULL,
+ CREATE_SUSPENDED,
+ &dwThreadId);
+
+ if (hThread == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateThread call failed. GetLastError "
+ "returned %d.\n", GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ EnterCriticalSection(&CriticalSection);
+ /*
+ * Set priority of the thread to greater than that of the currently
+ * running thread so it is guaranteed to run.
+ */
+ dwRet = (DWORD) SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL);
+
+ if (0 == dwRet)
+ {
+ Trace("PALSUITE ERROR: SetThreadPriority (%p, %d) call failed.\n"
+ "GetLastError returned %d.\n", hThread,
+ THREAD_PRIORITY_NORMAL, GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ CloseHandle(hThread);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ dwRet = ResumeThread(hThread);
+
+ if (-1 == dwRet)
+ {
+ Trace("PALSUITE ERROR: ResumeThread(%p) call failed.\nGetLastError "
+ "returned %d.\n", hThread, GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ CloseHandle(hThread);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+ /*
+ * Sleep until we know the thread has been invoked. This sleep in
+ * combination with the higher priority of the other thread should
+ * guarantee both threads block on the critical section.
+ */
+ while (t1_aflag == FAIL)
+ {
+ Sleep(1);
+ }
+
+ LeaveCriticalSection(&CriticalSection);
+
+ switch ((WaitForSingleObject(
+ hThread,
+ 10000))) /* Wait 10 seconds */
+ {
+ case WAIT_OBJECT_0:
+ /* Object (thread) is signaled */
+ LeaveCriticalSection(&CriticalSection);
+ CloseHandle(hThread);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_TIMEOUT ('%d'), instead it returned "
+ "WAIT_OBJECT_0 ('%d').\nA nested LeaveCriticalSection(%p) "
+ "call released both threads that were waiting on it!\n",
+ hThread, 10000, WAIT_TIMEOUT, WAIT_OBJECT_0, &CriticalSection);
+ break;
+ case WAIT_ABANDONED:
+ /*
+ * Object was mutex object whose owning
+ * thread has terminated. Shouldn't occur.
+ */
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_TIMEOUT ('%d'), instead it returned "
+ "WAIT_ABANDONED ('%d').\nGetLastError returned '%d'\n",
+ hThread, 10000, WAIT_TIMEOUT, WAIT_ABANDONED, GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ CloseHandle(hThread);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ break;
+ case WAIT_FAILED: /* WaitForSingleObject function failed */
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_TIMEOUT ('%d'), instead it returned "
+ "WAIT_FAILED ('%d').\nGetLastError returned '%d'\n",
+ hThread, 10000, WAIT_TIMEOUT, WAIT_FAILED, GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ CloseHandle(hThread);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ break;
+ case WAIT_TIMEOUT:
+ /*
+ * We expect this thread to timeout waiting for the
+ * critical section object to become available.
+ */
+ t0_tflag = PASS;
+ break;
+ }
+
+ LeaveCriticalSection(&CriticalSection);
+
+ if (WAIT_OBJECT_0 != WaitForSingleObject (hThread, 10000))
+ {
+ if (0 == CloseHandle(hThread))
+ {
+ Trace("PALSUITE ERROR: CloseHandle(%p) call failed.\n"
+ "WaitForSingleObject(%p,%d) should have returned "
+ "WAIT_OBJECT_0 ('%d').\nBoth calls failed. "
+ "Deleted CRITICAL_SECTION object which likely means\n"
+ "thread %p is now in an undefined state. GetLastError "
+ "returned '%d'.\n", hThread, hThread, 10000, WAIT_OBJECT_0,
+ hThread, GetLastError());
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+ else
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned WAIT_OBJECT_0 ('%d').\n GetLastError returned "
+ "'%d'.\n", hThread, hThread, 10000, WAIT_OBJECT_0,
+ hThread, GetLastError());
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+ }
+
+ if (0 == CloseHandle(hThread))
+ {
+ Trace("PALSUITE ERROR: CloseHandle(%p) call failed.\n"
+ "Deleted CRITICAL_SECTION object which likely means\n"
+ "thread %p is now in an undefined state. GetLastError "
+ "returned '%d'.\n", hThread, hThread, GetLastError());
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+
+ }
+ DeleteCriticalSection(&CriticalSection);
+ /*
+ * Ensure both thread 0 experienced a wait timeout and thread 1
+ * accessed the critical section or fail the test, otherwise pass it.
+ */
+ if ((t0_tflag == FAIL) || (t1_cflag == FAIL))
+ {
+ Trace("PALSUITE ERROR: Thread 0 returned %d when %d was expected.\n"
+ "Thread 1 returned %d when %d was expected.\n", t0_tflag,
+ PASS, t1_cflag, PASS);
+ bTestResult=FAIL;
+ }
+ else
+ {
+ bTestResult=PASS;
+ }
+
+ PAL_TerminateEx(bTestResult);
+ return (bTestResult);
+}
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/testinfo.dat b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/testinfo.dat
new file mode 100644
index 0000000000..06842124b9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/testinfo.dat
@@ -0,0 +1,18 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CriticalSectionFunctions
+Name = Positive test to ensure CRITICAL_SECTION objects can be nested
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Test that we are able to nest critical section calls.
+= The initial thread makes a call to EnterCriticalSection once,
+= blocking on a CRITICAL_SECTION object and creates a new thread.
+= The newly created thread blocks on the same CRITICAL_SECTION object.
+= The first thread now makes a call to LeaveCriticalSection.
+= Test to see that the new thread doesn't get unblocked.
+
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/CMakeLists.txt
new file mode 100644
index 0000000000..8bd2a72878
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test3.c
+)
+
+add_executable(paltest_criticalsectionfunctions_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_criticalsectionfunctions_test3 coreclrpal)
+
+target_link_libraries(paltest_criticalsectionfunctions_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/test3.c b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/test3.c
new file mode 100644
index 0000000000..d5911267b2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/test3.c
@@ -0,0 +1,376 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: CriticalSectionFunctions/test3/test3.c
+**
+** Purpose: Create two threads to exercise TryEnterCriticalSection
+** and EnterCriticalSection. TryEnterCriticalSection acquires
+** and holds a CRITICAL_SECTION object. Another call to
+** TryEnterCriticalSection is made from a different thread, at
+** this time, to establish a call to TryEnterCriticalSection
+** will return immediatly and to establish
+** TryEnterCriticalSection returns the proper value when it
+** attempts to lock a CRITICAL_SECTION that is already owned
+** by another thread. The CRITICAL_SECTION object is then
+** released and held by a call to EnterCriticalSection. A new
+** thread is invoked and attempts to acquire the held
+** CRITICAL_SECTION with a call to TryEnterCriticalSection.
+** TryEnterCriticalSection returns immediatly and returns
+** with the value that states the CRITICAL_SECTION object is
+** held by another thread. This establishes
+** TryEnterCriticalSection behaves the same way with
+** CriticalSections locked by TryEnterCriticalSection and
+** EnterCriticalSection.
+**
+**
+**===================================================================*/
+#include <palsuite.h>
+
+#define NUM_THREADS 2
+
+HANDLE hThread[NUM_THREADS];
+HANDLE hEvent[NUM_THREADS];
+CRITICAL_SECTION CriticalSection;
+BOOL bRet = FAIL;
+
+DWORD PALAPI Thread(LPVOID lpParam)
+{
+ DWORD dwRet;
+
+ if (0 == TryEnterCriticalSection(&CriticalSection))
+ {
+ dwRet = WaitForMultipleObjects(NUM_THREADS, hEvent, TRUE, 10000);
+ if ((WAIT_OBJECT_0 > dwRet) ||
+ ((WAIT_OBJECT_0 + NUM_THREADS - 1) < dwRet))
+ {
+#if 0
+ if (0 == CloseHandle(hThread[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) "
+ "during clean up.\nGetLastError returned '%d'.\n",
+ hThread[1], GetLastError());
+ }
+#endif
+ Trace("PALSUITE ERROR: WaitForMultipleObjects(%d, %p, %d, %d) call"
+ "returned an unexpected value, '%d'.\nGetLastError returned "
+ "%d.\n", NUM_THREADS, hEvent, TRUE, 10000, dwRet,
+ GetLastError());
+ }
+ else
+ {
+ bRet = PASS;
+ }
+ }
+ else
+ {
+ /* signal thread 0 */
+ if (0 == SetEvent(hEvent[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute SetEvent(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[0],
+ GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ if (0 == CloseHandle(hThread[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) "
+ "during clean up\nGetLastError returned '%d'.\n",
+ hThread[0], GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) "
+ "during clean up\nGetLastError returned '%d'.\n",
+ hEvent[0], GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) "
+ "during clean up\nGetLastError returned '%d'.\n",
+ hEvent[1], GetLastError());
+ }
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ /* wait to be signaled */
+ dwRet = WaitForSingleObject(hEvent[1], 10000);
+ if (WAIT_OBJECT_0 != dwRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_OBJECT_0 ('%d'), instead it returned "
+ "('%d').\nGetLastError returned '%d'.\n",
+ hEvent[0], 10000, WAIT_OBJECT_0, dwRet, GetLastError());
+ if (0 == CloseHandle(hThread[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) "
+ "during clean up.\nGetLastError returned '%d'.\n",
+ hThread[0], GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) "
+ "during clean up.\nGetLastError returned '%d'.\n",
+ hEvent[0], GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) "
+ "during clean up.\nGetLastError returned '%d.'\n",
+ hEvent[1], GetLastError());
+ }
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+ LeaveCriticalSection(&CriticalSection);
+ }
+ return FAIL;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ HANDLE hThread[NUM_THREADS];
+ DWORD dwThreadId[NUM_THREADS];
+ DWORD dwRet;
+
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return(bRet);
+ }
+
+ /* thread 0 event */
+ hEvent[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (hEvent[0] == NULL)
+ {
+ Fail("PALSUITE ERROR: CreateEvent call #0 failed. GetLastError "
+ "returned %d.\n", GetLastError());
+ }
+
+ /* thread 1 event */
+ hEvent[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (hEvent[1] == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateEvent call #1 failed. GetLastError "
+ "returned %d.\n", GetLastError());
+ if (0 == CloseHandle(hEvent[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[0]);
+ }
+ Fail("");
+ }
+
+ InitializeCriticalSection ( &CriticalSection );
+
+ hThread[0] = CreateThread(NULL,
+ 0,
+ &Thread,
+ (LPVOID) NULL,
+ 0,
+ &dwThreadId[0]);
+
+ if (hThread[0] == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateThread call #0 failed. GetLastError "
+ "returned %d.\n", GetLastError());
+ if (0 == CloseHandle(hEvent[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[0],
+ GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[1],
+ GetLastError());
+ }
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+
+ /* wait for thread 0 to be signaled */
+ dwRet = WaitForSingleObject(hEvent[0], 10000);
+ if (WAIT_OBJECT_0 != dwRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_OBJECT_0 ('%d'), instead it returned "
+ "('%d').\nGetLastError returned '%d'.\n", hEvent[0], 10000,
+ WAIT_OBJECT_0, dwRet, GetLastError());
+ if (0 == CloseHandle(hThread[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hThread[0],
+ GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[0],
+ GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[1],
+ GetLastError());
+ }
+ Fail("");
+ }
+
+ /*
+ * Attempting to enter CRITICAL_SECTION object owned by the
+ * created thread and locked with TryEnterCriticalSection
+ */
+ if (0 == TryEnterCriticalSection(&CriticalSection))
+ {
+ /* signal thread 1 */
+ if (0 == SetEvent(hEvent[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute SetEvent(%p) call.\n"
+ "GetLastError returned '%d'.\n", hEvent[1],
+ GetLastError());
+ goto done;
+ }
+ }
+ else
+ {
+ Trace("PALSUITE_ERROR: TryEnterCriticalSection was able to grab a"
+ " CRITICAL_SECTION object\nwhich was already owned.\n");
+ LeaveCriticalSection(&CriticalSection);
+ if (0 == CloseHandle(hThread[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hThread[0],
+ GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[0],
+ GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[1],
+ GetLastError());
+ }
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+ /*
+ * Enter the CRITICAL_SECTION and launch another thread to attempt
+ * to access the CRITICAL_SECTION with a call to TryEnterCriticalSection.
+ */
+ EnterCriticalSection(&CriticalSection);
+
+ hThread[1] = CreateThread(NULL,
+ 0,
+ &Thread,
+ (LPVOID) NULL,
+ 0,
+ &dwThreadId[1]);
+
+ if (hThread[1] == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateThread call #1 failed. GetLastError "
+ "returned %d.\n", GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ if (0 == CloseHandle(hThread[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hThread[0],
+ GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[0],
+ GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[1],
+ GetLastError());
+ }
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ dwRet = WaitForMultipleObjects(NUM_THREADS, hThread, TRUE, 10000);
+ if ((WAIT_OBJECT_0 > dwRet) ||
+ ((WAIT_OBJECT_0 + NUM_THREADS - 1) < dwRet))
+ {
+ Trace("PALSUITE ERROR: WaitForMultipleObjects(%d, %p, %d, %d) call "
+ "returned an unexpected value, '%d'.\nGetLastError returned "
+ "%d.\n", NUM_THREADS, hThread, TRUE, 10000, dwRet,
+ GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ if (0 == CloseHandle(hThread[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hThread[0],
+ GetLastError());
+ }
+ if (0 == CloseHandle(hThread[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hThread[1],
+ GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[0],
+ GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[1],
+ GetLastError());
+ }
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ LeaveCriticalSection(&CriticalSection);
+ if (0 == CloseHandle(hThread[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hThread[1],
+ GetLastError());
+ }
+done:
+ if (0 == CloseHandle(hThread[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hThread[0],
+ GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[0],
+ GetLastError());
+ }
+ if (0 == CloseHandle(hEvent[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%d'.\n", hEvent[1],
+ GetLastError());
+ }
+ DeleteCriticalSection(&CriticalSection);
+
+ PAL_TerminateEx(bRet);
+
+ return (bRet);
+}
+
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/testinfo.dat b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/testinfo.dat
new file mode 100644
index 0000000000..818b4870a2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/testinfo.dat
@@ -0,0 +1,29 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CriticalSectionFunctions
+Name = Positive Test for TryEnterCriticalSection
+TYPE = DEFAULT
+EXE1 = test3
+Description
+= Create two threads to exercise TryEnterCriticalSection
+= and EnterCriticalSection. TryEnterCriticalSection acquires
+= and holds a CRITICAL_SECTION object. Another call to
+= TryEnterCriticalSection is made from a different thread, at
+= this time, to establish a call to TryEnterCriticalSection
+= will return immediatly and to establish
+= TryEnterCriticalSection returns the proper value when it
+= attempts to lock a CRITICAL_SECTION that is already owned
+= by another thread. The CRITICAL_SECTION object is then
+= released and held by a call to EnterCriticalSection. A new
+= thread is invoked and attempts to acquire the held
+= CRITICAL_SECTION with a call to TryEnterCriticalSection.
+= TryEnterCriticalSection returns immediatly and returns
+= with the value that states the CRITICAL_SECTION object is
+= held by another thread. This establishes
+= TryEnterCriticalSection behaves the same way with
+= CriticalSections locked by TryEnterCriticalSection and
+= EnterCriticalSection.
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/CMakeLists.txt b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/CMakeLists.txt
new file mode 100644
index 0000000000..0626631640
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test4.c
+)
+
+add_executable(paltest_criticalsectionfunctions_test4
+ ${SOURCES}
+)
+
+add_dependencies(paltest_criticalsectionfunctions_test4 coreclrpal)
+
+target_link_libraries(paltest_criticalsectionfunctions_test4
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/test4.c b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/test4.c
new file mode 100644
index 0000000000..8a245a4776
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/test4.c
@@ -0,0 +1,241 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: criticalsectionfunctions/test4/test4.c
+**
+** Purpose: Test to see if threads blocked on a CRITICAL_SECTION object will
+** be released in an orderly manner. This case looks at the following
+** scenario. If one thread owns a CRITICAL_SECTION object and two threads
+** block in EnterCriticalSection, trying to hold the already owned
+** CRITICAL_SECTION object, when the first thread releases the CRITICAL_SECTION
+** object, will one and only one of the waiters get unblocked?
+**
+** Dependencies: CreateThread
+** InitializeCriticalSection
+** EnterCriticalSection
+** LeaveCriticalSection
+** DeleteCriticalSection
+** Sleep
+** WaitForSingleObject
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+#define NUM_BLOCKING_THREADS 2
+
+BOOL bTestResult;
+CRITICAL_SECTION CriticalSection;
+HANDLE hThread[NUM_BLOCKING_THREADS];
+HANDLE hEvent;
+DWORD dwThreadId[NUM_BLOCKING_THREADS];
+volatile int flags[NUM_BLOCKING_THREADS] = {0,0};
+
+DWORD PALAPI ThreadTest1(LPVOID lpParam)
+{
+
+ EnterCriticalSection ( &CriticalSection );
+
+ flags[0] = 1;
+
+ return 0;
+
+}
+
+DWORD PALAPI ThreadTest2(LPVOID lpParam)
+{
+
+ EnterCriticalSection ( &CriticalSection );
+
+ flags[1] = 1;
+
+ return 0;
+
+}
+
+int __cdecl main(int argc, char **argv)
+{
+
+ DWORD dwRet;
+ DWORD dwRet1;
+ bTestResult = FAIL;
+
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return(bTestResult);
+ }
+
+ /*
+ * Create Critical Section Object
+ */
+ InitializeCriticalSection ( &CriticalSection );
+
+ EnterCriticalSection ( &CriticalSection );
+
+ hThread[0] = CreateThread(NULL,
+ 0,
+ &ThreadTest1,
+ (LPVOID) 0,
+ CREATE_SUSPENDED,
+ &dwThreadId[0]);
+ if (hThread[0] == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateThread(%p, %d, %p, %p, %d, %p) call "
+ "failed.\nGetLastError returned %d.\n", NULL, 0, &ThreadTest1,
+ (LPVOID) 0, CREATE_SUSPENDED, &dwThreadId[0], GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ DeleteCriticalSection ( &CriticalSection );
+ Fail("");
+ }
+
+ hThread[1] = CreateThread(NULL,
+ 0,
+ &ThreadTest2,
+ (LPVOID) 0,
+ CREATE_SUSPENDED,
+ &dwThreadId[1]);
+ if (hThread[1] == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateThread(%p, %d, %p, %p, %d, %p) call "
+ "failed.\nGetLastError returned %d.\n", NULL, 0, &ThreadTest2,
+ (LPVOID) 0, CREATE_SUSPENDED, &dwThreadId[1], GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+
+ dwRet = ResumeThread(hThread[0]);
+ if (-1 == dwRet)
+ {
+ Trace("PALSUITE ERROR: ResumeThread(%p) call failed.\n"
+ "GetLastError returned '%d'.\n", hThread[0],
+ GetLastError());
+ }
+
+ dwRet = WaitForSingleObject(hThread[0], 10000);
+ if (WAIT_OBJECT_0 == dwRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p, %d) call "
+ "failed. '%d' was returned instead of the expected '%d'.\n"
+ "GetLastError returned '%d'.\n", hThread[0], 10000, dwRet,
+ WAIT_OBJECT_0, GetLastError());
+ }
+
+ if (0 == CloseHandle(hThread[0]))
+ {
+ Trace("PALSUITE NOTIFICATION: CloseHandle(%p) call failed.\n"
+ "GetLastError returned %d. Not failing tests.\n",
+ hThread[0], GetLastError());
+ }
+
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ /*
+ * Set other thread priorities to be higher than ours & Sleep to ensure
+ * we give up the processor.
+ */
+ dwRet = (DWORD) SetThreadPriority(hThread[0],
+ THREAD_PRIORITY_ABOVE_NORMAL);
+ if (0 == dwRet)
+ {
+ Trace("PALSUITE ERROR: SetThreadPriority(%p, %d) call failed.\n"
+ "GetLastError returned %d", hThread[0],
+ THREAD_PRIORITY_ABOVE_NORMAL, GetLastError());
+ }
+
+ dwRet = (DWORD) SetThreadPriority(hThread[1],
+ THREAD_PRIORITY_ABOVE_NORMAL);
+ if (0 == dwRet)
+ {
+ Trace("PALSUITE ERROR: SetThreadPriority(%p, %d) call failed.\n"
+ "GetLastError returned %d", hThread[1],
+ THREAD_PRIORITY_ABOVE_NORMAL, GetLastError());
+ }
+
+ dwRet = ResumeThread(hThread[0]);
+ if (-1 == dwRet)
+ {
+ Trace("PALSUITE ERROR: ResumeThread(%p, %d) call failed.\n"
+ "GetLastError returned %d", hThread[0],
+ GetLastError() );
+ }
+
+ dwRet = ResumeThread(hThread[1]);
+ if (-1 == dwRet)
+ {
+ Trace("PALSUITE ERROR: ResumeThread(%p, %d) call failed.\n"
+ "GetLastError returned %d", hThread[0],
+ GetLastError());
+ }
+
+ Sleep (0);
+
+ LeaveCriticalSection (&CriticalSection);
+
+ dwRet = WaitForSingleObject(hThread[0], 10000);
+ dwRet1 = WaitForSingleObject(hThread[1], 10000);
+
+ if ((WAIT_OBJECT_0 == dwRet) ||
+ (WAIT_OBJECT_0 == dwRet1))
+ {
+ if ((1 == flags[0] && 0 == flags[1]) ||
+ (0 == flags[0] && 1 == flags[1]))
+ {
+ bTestResult = PASS;
+ }
+ else
+ {
+ bTestResult = FAIL;
+ Trace ("PALSUITE ERROR: flags[%d] = {%d,%d}. These values are"
+ "inconsistent.\nCriticalSection test failed.\n",
+ NUM_BLOCKING_THREADS, flags[0], flags[1]);
+ }
+
+ /* Fail the test if both threads returned WAIT_OBJECT_0 */
+ if ((WAIT_OBJECT_0 == dwRet) && (WAIT_OBJECT_0 == dwRet1))
+ {
+ bTestResult = FAIL;
+ Trace ("PALSUITE ERROR: WaitForSingleObject(%p, %d) and "
+ "WaitForSingleObject(%p, %d)\nboth returned dwRet = '%d'\n"
+ "One should have returned WAIT_TIMEOUT ('%d').\n",
+ hThread[0], 10000, hThread[1], 10000, dwRet, WAIT_TIMEOUT);
+ }
+ }
+ else
+ {
+ bTestResult = FAIL;
+ Trace ("PALSUITE ERROR: WaitForSingleObject(%p, %d) and "
+ "WaitForSingleObject(%p, %d)\nReturned dwRet = '%d' and\n"
+ "dwRet1 = '%d' respectively.\n", hThread[0], 10000, hThread[1],
+ 10000, dwRet, dwRet1);
+ }
+
+ if (WAIT_OBJECT_0 == dwRet)
+ {
+ if (0 == CloseHandle(hThread[0]))
+ {
+ Trace("PALSUITE NOTIFICATION: CloseHandle(%p) call failed.\n"
+ "GetLastError returned %d. Not failing tests.\n",
+ hThread[0], GetLastError());
+ }
+ }
+ if (WAIT_OBJECT_0 == dwRet1)
+ {
+ if (0 == CloseHandle(hThread[1]))
+ {
+ Trace("PALSUITE NOTIFICATION: CloseHandle(%p) call failed.\n"
+ "GetLastError returned %d. Not failing tests.\n",
+ hThread[1], GetLastError());
+ }
+ }
+
+ /* Leaking the CS on purpose, since there is still a thread
+ waiting on it */
+
+ PAL_TerminateEx(bTestResult);
+ return (bTestResult);
+}
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/testinfo.dat b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/testinfo.dat
new file mode 100644
index 0000000000..9c07c24113
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/testinfo.dat
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = LeaveCriticalSection
+Name = Positive test to ensure CRITICAL_SECTION objects are released properly
+TYPE = DEFAULT
+EXE1 = test4
+Description
+= Test to see if threads blocked on a CRITICAL_SECTION object will
+= be released in an orderly manner. This case looks at the following
+= scenario. If one thread owns a CRITICAL_SECTION object and two threads
+= block in EnterCriticalSection, trying to hold the already owned
+= CRITICAL_SECTION object, when the first thread releases the CRITICAL_SECTION
+= object, will one and only one of the waiters get unblocked?
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/CMakeLists.txt b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/CMakeLists.txt
new file mode 100644
index 0000000000..37d709f121
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test5.c
+)
+
+add_executable(paltest_criticalsectionfunctions_test5
+ ${SOURCES}
+)
+
+add_dependencies(paltest_criticalsectionfunctions_test5 coreclrpal)
+
+target_link_libraries(paltest_criticalsectionfunctions_test5
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/test5.c b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/test5.c
new file mode 100644
index 0000000000..8dfa4f5f3d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/test5.c
@@ -0,0 +1,187 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: CriticalSectionFunctions/test5/test5.c
+**
+** Purpose: Attempt to delete a critical section owned by another
+** thread.
+**
+**
+**===================================================================*/
+#include <palsuite.h>
+
+/*
+ * Tokens 0 and 1 are events. Token 2 is the thread.
+ */
+#define NUM_TOKENS 3
+
+HANDLE hToken[NUM_TOKENS];
+CRITICAL_SECTION CriticalSection;
+
+BOOL CleanupHelper (HANDLE *hArray, DWORD dwIndex)
+{
+ BOOL bCHRet;
+
+ bCHRet = CloseHandle(hArray[dwIndex]);
+ if (!bCHRet)
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%u'.\n", hArray[dwIndex],
+ GetLastError());
+ }
+
+ return (bCHRet);
+}
+
+BOOL Cleanup(HANDLE *hArray, DWORD dwIndex)
+{
+ BOOL bCRet;
+ BOOL bCHRet = FALSE;
+
+ while (--dwIndex > 0)
+ {
+ bCHRet = CleanupHelper(&hArray[0], dwIndex);
+ }
+
+ bCRet = CloseHandle(hArray[0]);
+ if (!bCRet)
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%u'.\n", hArray[dwIndex],
+ GetLastError());
+ }
+
+ return (bCRet&&bCHRet);
+}
+
+DWORD PALAPI Thread(LPVOID lpParam)
+{
+ DWORD dwTRet;
+
+ EnterCriticalSection(&CriticalSection);
+
+ /* signal thread 0 */
+ if (0 == SetEvent(hToken[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute SetEvent(%p) during "
+ "clean up.\nGetLastError returned '%u'.\n", hToken[0],
+ GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ Cleanup (&hToken[0], NUM_TOKENS);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ /* wait to be signaled */
+ dwTRet = WaitForSingleObject(hToken[1], 10000);
+ if (WAIT_OBJECT_0 != dwTRet)
+
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_OBJECT_0 ('%d'), instead it returned "
+ "('%d').\nGetLastError returned '%u'.\n",
+ hToken[1], 10000, WAIT_OBJECT_0, dwTRet, GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ Cleanup (&hToken[0], NUM_TOKENS);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ LeaveCriticalSection(&CriticalSection);
+ return 0;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ DWORD dwThreadId;
+ DWORD dwMRet;
+
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return(FAIL);
+ }
+
+ /* thread 0 event */
+ hToken[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (NULL == hToken[0])
+ {
+ Fail("PALSUITE ERROR: CreateEvent call #0 failed. GetLastError "
+ "returned %u.\n", GetLastError());
+ }
+
+ /* thread 1 event */
+ hToken[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (NULL == hToken[1])
+ {
+ Trace("PALSUITE ERROR: CreateEvent call #1 failed. GetLastError "
+ "returned %u.\n", GetLastError());
+ Cleanup(&hToken[0], (NUM_TOKENS - 2));
+ Fail("");
+ }
+
+ InitializeCriticalSection(&CriticalSection);
+
+ hToken[2] = CreateThread(NULL,
+ 0,
+ &Thread,
+ (LPVOID) NULL,
+ 0,
+ &dwThreadId);
+ if (hToken[2] == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateThread call #0 failed. GetLastError "
+ "returned %u.\n", GetLastError());
+ Cleanup(&hToken[0], (NUM_TOKENS - 1));
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ /* wait for thread 0 to be signaled */
+ dwMRet = WaitForSingleObject(hToken[0], 10000);
+ if (WAIT_OBJECT_0 != dwMRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_OBJECT_0 ('%d'), instead it returned "
+ "('%d').\nGetLastError returned '%u'.\n", hToken[0], 10000,
+ WAIT_OBJECT_0, dwMRet, GetLastError());
+ Cleanup(&hToken[0], NUM_TOKENS);
+ Fail("");
+ }
+
+ /*
+ * Attempt to do delete CriticalSection object owned by other thread
+ */
+ DeleteCriticalSection(&CriticalSection);
+
+ /* signal thread 1 */
+ if (0 == SetEvent(hToken[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute SetEvent(%p) call.\n"
+ "GetLastError returned '%u'.\n", hToken[1],
+ GetLastError());
+ Cleanup(&hToken[0], NUM_TOKENS);
+ Fail("");
+ }
+
+ dwMRet = WaitForSingleObject(hToken[2], 10000);
+ if (WAIT_OBJECT_0 != dwMRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p, %d) call "
+ "returned an unexpected value '%d'.\nGetLastError returned "
+ "%u.\n", hToken[2], 10000, dwMRet, GetLastError());
+ Cleanup(&hToken[0], NUM_TOKENS);
+ Fail("");
+ }
+
+ if (!Cleanup(&hToken[0], NUM_TOKENS))
+ {
+ Fail("");
+ }
+
+ PAL_Terminate();
+
+ return (PASS);
+}
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/testinfo.dat b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/testinfo.dat
new file mode 100644
index 0000000000..aa1124925b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/testinfo.dat
@@ -0,0 +1,12 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CriticalSectionFunctions
+Name = Positive test for DeleteCriticalSection
+TYPE = DEFAULT
+EXE1 = test5
+Description
+= Attempt to delete a critical section owned by another thread.
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/CMakeLists.txt b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/CMakeLists.txt
new file mode 100644
index 0000000000..c580fdbd6b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test6.c
+)
+
+add_executable(paltest_criticalsectionfunctions_test6
+ ${SOURCES}
+)
+
+add_dependencies(paltest_criticalsectionfunctions_test6 coreclrpal)
+
+target_link_libraries(paltest_criticalsectionfunctions_test6
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/test6.c b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/test6.c
new file mode 100644
index 0000000000..c27db86e5b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/test6.c
@@ -0,0 +1,190 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: CriticalSectionFunctions/test6/test6.c
+**
+** Purpose: Attempt to leave a critical section which is owned by
+** another thread.
+**
+**
+**===================================================================*/
+#include <palsuite.h>
+
+/*
+ * Tokens 0 and 1 are events. Token 2 is the thread.
+ */
+#define NUM_TOKENS 3
+
+HANDLE hToken[NUM_TOKENS];
+CRITICAL_SECTION CriticalSection;
+
+BOOL CleanupHelper (HANDLE *hArray, DWORD dwIndex)
+{
+ BOOL bCHRet;
+
+ bCHRet = CloseHandle(hArray[dwIndex]);
+ if (!bCHRet)
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%u'.\n", hArray[dwIndex],
+ GetLastError());
+ }
+
+ return (bCHRet);
+}
+
+BOOL Cleanup(HANDLE *hArray, DWORD dwIndex)
+{
+ BOOL bCRet;
+ BOOL bCHRet;
+
+ while (--dwIndex > 0)
+ {
+ bCHRet = CleanupHelper(&hArray[0], dwIndex);
+ }
+
+ bCRet = CloseHandle(hArray[0]);
+ if (!bCRet)
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%u'.\n", hArray[dwIndex],
+ GetLastError());
+ }
+
+ return (bCRet&&bCHRet);
+}
+
+DWORD PALAPI Thread(LPVOID lpParam)
+{
+ DWORD dwTRet;
+
+ EnterCriticalSection(&CriticalSection);
+
+ /* signal thread 0 */
+ if (0 == SetEvent(hToken[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute SetEvent(%p) during "
+ "clean up.\nGetLastError returned '%u'.\n", hToken[0],
+ GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ Cleanup (&hToken[0], NUM_TOKENS);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ /* wait to be signaled */
+ dwTRet = WaitForSingleObject(hToken[1], 10000);
+ if (WAIT_OBJECT_0 != dwTRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_OBJECT_0 ('%d'), instead it returned "
+ "('%d').\nGetLastError returned '%u'.\n",
+ hToken[1], 10000, WAIT_OBJECT_0, dwTRet, GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ Cleanup (&hToken[0], NUM_TOKENS);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ LeaveCriticalSection(&CriticalSection);
+
+ return 0;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ DWORD dwThreadId;
+ DWORD dwMRet;
+
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return(FAIL);
+ }
+
+ /* thread 0 event */
+ hToken[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (hToken[0] == NULL)
+ {
+ Fail("PALSUITE ERROR: CreateEvent call #0 failed. GetLastError "
+ "returned %u.\n", GetLastError());
+ }
+
+ /* thread 1 event */
+ hToken[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (hToken[1] == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateEvent call #1 failed. GetLastError "
+ "returned %u.\n", GetLastError());
+ Cleanup(&hToken[0], (NUM_TOKENS - 2));
+ Fail("");
+ }
+
+ InitializeCriticalSection(&CriticalSection);
+
+ hToken[2] = CreateThread(NULL,
+ 0,
+ &Thread,
+ (LPVOID) NULL,
+ 0,
+ &dwThreadId);
+
+ if (hToken[2] == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateThread call #0 failed. GetLastError "
+ "returned %u.\n", GetLastError());
+ Cleanup(&hToken[0], (NUM_TOKENS - 1));
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ /* wait for thread 0 to be signaled */
+ dwMRet = WaitForSingleObject(hToken[0], 10000);
+ if (WAIT_OBJECT_0 != dwMRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_OBJECT_0 ('%d'), instead it returned "
+ "('%d').\nGetLastError returned '%u'.\n", hToken[0], 10000,
+ WAIT_OBJECT_0, dwMRet, GetLastError());
+ Cleanup(&hToken[0], NUM_TOKENS);
+ Fail("");
+ }
+
+ /*
+ * Attempt to leave critical section which is owned by the other thread.
+ */
+ LeaveCriticalSection(&CriticalSection);
+
+ /* signal thread 1 */
+ if (0 == SetEvent(hToken[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute SetEvent(%p) call.\n"
+ "GetLastError returned '%u'.\n", hToken[1],
+ GetLastError());
+ Cleanup(&hToken[0], NUM_TOKENS);
+ Fail("");
+ }
+
+ dwMRet = WaitForSingleObject(hToken[2], 10000);
+ if (WAIT_OBJECT_0 != dwMRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p, %d) call "
+ "returned an unexpected value '%d'.\nGetLastError returned "
+ "%u.\n", hToken[2], 10000, dwMRet, GetLastError());
+ Cleanup(&hToken[0], NUM_TOKENS);
+ Fail("");
+ }
+
+ if (!Cleanup(&hToken[0], NUM_TOKENS))
+ {
+ Fail("");
+ }
+
+ PAL_Terminate();
+
+ return(PASS);
+}
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/testinfo.dat b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/testinfo.dat
new file mode 100644
index 0000000000..151e1ed4d0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/testinfo.dat
@@ -0,0 +1,12 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CriticalSectionFunctions
+Name = Positive Test for LeaveCriticalSection
+TYPE = DEFAULT
+EXE1 = test6
+Description
+= Attempt to leave a critical section which is owned by another thread.
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/CMakeLists.txt b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/CMakeLists.txt
new file mode 100644
index 0000000000..0a12bfe3ef
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test7.c
+)
+
+add_executable(paltest_criticalsectionfunctions_test7
+ ${SOURCES}
+)
+
+add_dependencies(paltest_criticalsectionfunctions_test7 coreclrpal)
+
+target_link_libraries(paltest_criticalsectionfunctions_test7
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/test7.c b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/test7.c
new file mode 100644
index 0000000000..1c030d3c03
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/test7.c
@@ -0,0 +1,188 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: CriticalSectionFunctions/test7/test7.c
+**
+** Purpose: Attempt to delete a critical section owned by the current
+** thread.
+**
+**
+**===================================================================*/
+#include <palsuite.h>
+
+/*
+ * Tokens 0 and 1 are events. Token 2 is the thread.
+ */
+#define NUM_TOKENS 3
+
+HANDLE hToken[NUM_TOKENS];
+CRITICAL_SECTION CriticalSection;
+
+BOOL CleanupHelper (HANDLE *hArray, DWORD dwIndex)
+{
+ BOOL bCHRet;
+
+ bCHRet = CloseHandle(hArray[dwIndex]);
+ if (!bCHRet)
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%u'.\n", hArray[dwIndex],
+ GetLastError());
+ }
+
+ return (bCHRet);
+}
+
+BOOL Cleanup(HANDLE *hArray, DWORD dwIndex)
+{
+ BOOL bCRet;
+ BOOL bCHRet = 0;
+
+ while (--dwIndex > 0)
+ {
+ bCHRet = CleanupHelper(&hArray[0], dwIndex);
+ }
+
+ bCRet = CloseHandle(hArray[0]);
+ if (!bCRet)
+ {
+ Trace("PALSUITE ERROR: Unable to execute CloseHandle(%p) during "
+ "clean up.\nGetLastError returned '%u'.\n", hArray[dwIndex],
+ GetLastError());
+ }
+
+ return (bCRet&&bCHRet);
+}
+
+DWORD PALAPI Thread(LPVOID lpParam)
+{
+ DWORD dwTRet;
+
+ EnterCriticalSection(&CriticalSection);
+
+ /* signal thread 0 */
+ if (0 == SetEvent(hToken[0]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute SetEvent(%p) during "
+ "clean up.\nGetLastError returned '%u'.\n", hToken[0],
+ GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ Cleanup (&hToken[0], NUM_TOKENS);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ /* wait to be signaled */
+ dwTRet = WaitForSingleObject(hToken[1], 10000);
+ if (WAIT_OBJECT_0 != dwTRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_OBJECT_0 ('%d'), instead it returned "
+ "('%d').\nGetLastError returned '%u'.\n",
+ hToken[0], 10000, WAIT_OBJECT_0, dwTRet, GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ Cleanup (&hToken[0], NUM_TOKENS);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ DeleteCriticalSection(&CriticalSection);
+
+ return 0;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ DWORD dwThreadId;
+ DWORD dwMRet;
+
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return(FAIL);
+ }
+
+ /* thread 0 event */
+ hToken[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (hToken[0] == NULL)
+ {
+ Fail("PALSUITE ERROR: CreateEvent call #0 failed. GetLastError "
+ "returned %u.\n", GetLastError());
+ }
+
+ /* thread 1 event */
+ hToken[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (hToken[1] == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateEvent call #1 failed. GetLastError "
+ "returned %u.\n", GetLastError());
+ Cleanup (&hToken[0], (NUM_TOKENS - 2));
+ Fail("");
+ }
+
+ InitializeCriticalSection(&CriticalSection);
+
+ hToken[2] = CreateThread(NULL,
+ 0,
+ &Thread,
+ (LPVOID) NULL,
+ 0,
+ &dwThreadId);
+
+ if (hToken[2] == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateThread call #0 failed. GetLastError "
+ "returned %u.\n", GetLastError());
+ Cleanup (&hToken[0], (NUM_TOKENS - 1));
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ /* wait for thread 0 to be signaled */
+ dwMRet = WaitForSingleObject(hToken[0], 10000);
+ if (WAIT_OBJECT_0 != dwMRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_OBJECT_0 ('%d'), instead it returned "
+ "('%d').\nGetLastError returned '%u'.\n", hToken[0], 10000,
+ WAIT_OBJECT_0, dwMRet, GetLastError());
+ Cleanup (&hToken[0], NUM_TOKENS);
+ Fail("");
+ }
+
+ /* signal thread 1 */
+ if (0 == SetEvent(hToken[1]))
+ {
+ Trace("PALSUITE ERROR: Unable to execute SetEvent(%p) call.\n"
+ "GetLastError returned '%u'.\n", hToken[1],
+ GetLastError());
+ Cleanup (&hToken[0], NUM_TOKENS);
+ Fail("");
+ }
+
+ dwMRet = WaitForSingleObject(hToken[2], 10000);
+ if (WAIT_OBJECT_0 != dwMRet)
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p, %d) call "
+ "returned an unexpected value '%d'.\nGetLastError returned "
+ "%u.\n", hToken[2], 10000, dwMRet, GetLastError());
+ Cleanup (&hToken[0], NUM_TOKENS);
+ Fail("");
+ }
+
+ if (!Cleanup(&hToken[0], NUM_TOKENS))
+ {
+ Fail("");
+ }
+
+ PAL_Terminate();
+
+ return (PASS);
+}
+
+
+
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/testinfo.dat b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/testinfo.dat
new file mode 100644
index 0000000000..5cf5809622
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/testinfo.dat
@@ -0,0 +1,12 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CriticalSectionFunctions
+Name = Positive test for DeleteCriticalSection
+TYPE = DEFAULT
+EXE1 = test7
+Description
+= Attempt to delete a critical section owned by the current thread.
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/CMakeLists.txt b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/CMakeLists.txt
new file mode 100644
index 0000000000..0d95a95410
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test8.c
+)
+
+add_executable(paltest_criticalsectionfunctions_test8
+ ${SOURCES}
+)
+
+add_dependencies(paltest_criticalsectionfunctions_test8 coreclrpal)
+
+target_link_libraries(paltest_criticalsectionfunctions_test8
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/test8.c b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/test8.c
new file mode 100644
index 0000000000..7f0c58cd26
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/test8.c
@@ -0,0 +1,217 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: CriticalSectionFunctions/test8/test8.c
+**
+** Pyrpose: Ensure critical section functionality is working by
+** having multiple threads racing on a CS under different
+** scenarios
+**
+**
+**===================================================================*/
+#include <palsuite.h>
+
+#define MAX_THREAD_COUNT 128
+#define DEFAULT_THREAD_COUNT 10
+#define DEFAULT_LOOP_COUNT 1000
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+
+int g_iThreadCount = DEFAULT_THREAD_COUNT;
+int g_iLoopCount = DEFAULT_LOOP_COUNT;
+volatile LONG g_lCriticalCount = 0;
+HANDLE g_hEvStart = NULL;
+
+CRITICAL_SECTION g_cs;
+DWORD PALAPI Thread(LPVOID lpParam)
+{
+ int i, j, iLpCnt;
+ DWORD dwRet = 0;
+ DWORD dwTid = GetCurrentThreadId();
+ LONG lRet;
+ BOOL bSleepInside;
+ BOOL bSleepOutside;
+
+ Trace("[tid=%u] Thread starting\n", dwTid);
+
+ dwRet = WaitForSingleObject(g_hEvStart, INFINITE);
+ if (WAIT_OBJECT_0 != dwRet)
+ {
+ Fail("WaitForSingleObject returned unexpected %u [GetLastError()=%u]\n",
+ dwRet, GetLastError());
+ }
+
+ for (j=0;j<8;j++)
+ {
+ bSleepInside = 2 & j;
+ bSleepOutside = 4 & j;
+
+ iLpCnt = g_iLoopCount;
+ if (bSleepInside || bSleepOutside)
+ {
+ iLpCnt /= 10;
+ }
+
+ for (i=0;i<iLpCnt;i++)
+ {
+ EnterCriticalSection(&g_cs);
+ if (1 & i)
+ {
+ // Simple increment on odd iterations
+ lRet = (g_lCriticalCount += 1);
+ }
+ else
+ {
+ // Interlocked increment on even iterations
+ lRet = InterlockedIncrement(&g_lCriticalCount);
+ }
+
+ if (1 != lRet || 1 != g_lCriticalCount)
+ {
+ Fail("Detected %d threads in area protected by critical section "
+ "[expected: 1 thread]\n", g_lCriticalCount);
+ }
+ if (bSleepInside)
+ {
+ Sleep(rand()%10);
+ }
+ if (1 != g_lCriticalCount)
+ {
+ Fail("Detected %d threads inside area protected by critical section "
+ "[expected: 1 thread]\n", (int)g_lCriticalCount);
+ }
+
+ if (1 & i)
+ {
+ // Simple decrement on odd iterations
+ lRet = (g_lCriticalCount -= 1);
+ }
+ else
+ {
+ // Interlocked decrement on even iterations
+ lRet = InterlockedDecrement(&g_lCriticalCount);
+ }
+ LeaveCriticalSection(&g_cs);
+
+ if (bSleepOutside)
+ {
+ Sleep(rand()%10);
+ }
+ }
+ }
+
+ Trace("[tid=%u] Thread done\n", dwTid);
+
+ return 0;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ DWORD dwThreadId;
+ DWORD dwRet;
+ HANDLE hThreads[MAX_THREAD_COUNT] = { 0 };
+ int iThreadCount = 0;
+ int i, iVal;
+
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return(FAIL);
+ }
+
+ srand(time(NULL));
+
+ for (i=1; i<argc; i++)
+ {
+ if ('-' == *argv[i])
+ {
+ switch(*(argv[i]+1))
+ {
+ case 'n':
+ if (i < argc-1)
+ {
+ i += 1;
+ iVal = atoi(argv[i]);
+ if (0 < iVal)
+ {
+ g_iLoopCount = iVal;
+ }
+ }
+ break;
+ case 't':
+ if (i < argc-1)
+ {
+ i += 1;
+ iVal = atoi(argv[i]);
+ if (0 < iVal && MAX_THREAD_COUNT >= iVal)
+ {
+ g_iThreadCount = iVal;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ Trace ("Iterations:\t%d\n", g_iLoopCount);
+ Trace ("Threads:\t%d\n", g_iThreadCount);
+
+ g_hEvStart = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (g_hEvStart == NULL)
+ {
+ Fail("CreateEvent call failed. GetLastError "
+ "returned %u.\n", GetLastError());
+ }
+
+ InitializeCriticalSection(&g_cs);
+
+ for (i=0;i<g_iThreadCount;i++)
+ {
+ hThreads[iThreadCount] = CreateThread(NULL,
+ 0,
+ &Thread,
+ (LPVOID) NULL,
+ 0,
+ &dwThreadId);
+ if (NULL != hThreads[iThreadCount])
+ {
+ iThreadCount++;
+ }
+ }
+
+ Sleep(100);
+
+ Trace("Created %d client threads\n", g_iThreadCount);
+
+ if (2 > iThreadCount)
+ {
+ Fail("Failed to create minimum number if threads, i.e. 2\n");
+ }
+
+ if (!SetEvent(g_hEvStart))
+ {
+ Fail("SetEvent failed [GetLastError()=%u]\n", GetLastError());
+ }
+
+ for (i=0; i<iThreadCount; i+=64)
+ {
+ dwRet = WaitForMultipleObjects(MIN(iThreadCount-i,64),
+ hThreads+i,
+ TRUE,
+ INFINITE);
+ if (WAIT_OBJECT_0 != dwRet)
+ {
+ Fail("Wait for all threads failed\n");
+ }
+ }
+
+ PAL_Terminate();
+ return (PASS);
+}
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/thistest.dat b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/thistest.dat
new file mode 100644
index 0000000000..1057eca5c5
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/thistest.dat
@@ -0,0 +1,2 @@
+
+PAL,threading,palsuite\threading\criticalsectionfunctions\test8,Test8forCriticalSectionFunctionalities=test8.c,
diff --git a/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/test1.c b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/test1.c
new file mode 100644
index 0000000000..b64fd0c7d2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/test1.c
@@ -0,0 +1,145 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test1.c
+**
+** Purpose: Tests that DisableThreadLibraryCalls actually stops thread
+** attach/detach notifications to a library. Also tests how it
+** handles an invalid module handle.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+
+/* SHLEXT is defined only for Unix variants */
+
+#if defined(SHLEXT)
+#define LibName "testlib"SHLEXT
+#define GETCALLCOUNT "GetCallCount"
+#else
+#define LibName "testlib"
+#define GETCALLCOUNT "_GetCallCount@0"
+#endif
+
+DWORD __stdcall ThreadFunc(LPVOID lpParam);
+int RunTest(int DisableThreadCalls);
+
+int __cdecl main(int argc, char **argv)
+{
+ int ret;
+
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return (FAIL);
+ }
+
+
+ /*
+ * Although MSDN says that DisableThreadLibraryCalls will fail if passed
+ * an invalid handle, it actually returns success!
+ */
+ ret = DisableThreadLibraryCalls(NULL);
+ if (!ret)
+ {
+ Fail("DisableThreadLibraryCalls failed on an invalid module "
+ "handle (it actually should pass)!\n");
+ }
+
+
+ /*
+ * Test once without calling DisableThreadLibraryCalls and make sure we
+ * get expected results.
+ */
+ ret = RunTest(0);
+ if (ret != 2)
+ {
+ Fail("Expected to get 2 thread library calls, got %d!\n", ret);
+ }
+
+
+ /*
+ * Test again, this time calling DisableThreadLibraryCalls.
+ */
+ ret = RunTest(1);
+ if (ret != 0)
+ {
+ Fail("Expected to get 0 thread library calls, got %d!\n", ret);
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
+/*
+ * Thread entry point. Doesn't do anything.
+ */
+DWORD __stdcall ThreadFunc(LPVOID lpParam)
+{
+ return 0;
+}
+
+
+int RunTest(int DisableThreadCalls)
+{
+ HMODULE LibMod;
+ HANDLE hThread;
+ DWORD threadID;
+ DWORD WaitRet;
+ int (*GetCallCount)();
+ int count;
+
+ LibMod = LoadLibrary(LibName);
+ if (LibMod == NULL)
+ {
+ Fail("Unable to load test library!\nGetLastError returned %d\n",
+ GetLastError());
+ }
+
+ GetCallCount = (int(*)())GetProcAddress(LibMod, GETCALLCOUNT);
+ if (GetCallCount == NULL)
+ {
+ Fail("Unable to get function GetCallCount from library!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ if (DisableThreadCalls)
+ {
+ if (!DisableThreadLibraryCalls(LibMod))
+ {
+ Fail("DisabledThreadLibraryCalls failed!\n"
+ "GetLastError returned %d!\n", GetLastError());
+ }
+ }
+
+ hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ThreadFunc,
+ NULL, 0, &threadID);
+
+ if (hThread == NULL)
+ {
+ Fail("Unable to create a thread!\n");
+ }
+
+ WaitRet = WaitForSingleObject(hThread, INFINITE);
+ if (WaitRet == WAIT_FAILED)
+ {
+ Fail("Unable to wait on thread!\nGetLastError returned %d\n",
+ GetLastError());
+ }
+
+ count = GetCallCount();
+
+ CloseHandle(hThread);
+
+ if (!FreeLibrary(LibMod))
+ {
+ Fail("Failed freeing library!\nGetLastError returned %d\n",
+ GetLastError());
+ }
+
+ return count;
+}
diff --git a/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/testinfo.dat b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/testinfo.dat
new file mode 100644
index 0000000000..6d32f3591a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/testinfo.dat
@@ -0,0 +1,15 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Threading
+Function = DisableThreadLibraryCalls
+Name = Positive Test #1 for DisableThreadLibraryCalls
+TYPE = DEFAULT
+EXE1 = test1
+LIB1 = testlib
+Description
+=Tests that DisableThreadLibraryCalls actually stops thread
+=attach/detach notifications to a library. Also tests how it
+=handles an invalid module handle.
diff --git a/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/testlib.c b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/testlib.c
new file mode 100644
index 0000000000..53b66d1357
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/testlib.c
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: testlib.c
+**
+** Purpose: Simple library that counts thread attach/detach notifications
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+static int Count;
+
+BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+
+ if (fdwReason == DLL_PROCESS_ATTACH)
+ {
+ Count = 0;
+ }
+ else if (fdwReason == DLL_THREAD_ATTACH ||
+ fdwReason == DLL_THREAD_DETACH)
+ {
+ Count++;
+ }
+
+ return TRUE;
+}
+
+#ifdef WIN32
+BOOL __stdcall _DllMainCRTStartup(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ return DllMain(hinstDLL, fdwReason, lpvReserved);
+}
+#endif
+
+#ifdef WIN32
+__declspec(dllexport)
+#endif
+int __stdcall GetCallCount()
+{
+ return Count;
+}
diff --git a/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/dllmain1.c b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/dllmain1.c
new file mode 100644
index 0000000000..5010a27665
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/dllmain1.c
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: dllmain1.c
+**
+** Purpose: Test to ensure DllMain() is called with THREAD_ATTACH
+** when a thread in the application is started.
+**
+** Dependencies: none
+**
+**
+**===========================================================================*/
+
+#include <palsuite.h>
+
+/* count of the number of times DllMain() was called with THREAD_DETACH */
+static int g_attachCount = 0;
+
+
+/* standard DllMain() */
+BOOL PALAPI DllMain(HINSTANCE hinstDLL, DWORD reason, LPVOID lpvReserved)
+{
+ BOOL bResult = TRUE;
+
+ switch( reason )
+ {
+ case DLL_PROCESS_ATTACH:
+ {
+ break;
+ }
+
+ case DLL_PROCESS_DETACH:
+ {
+ break;
+ }
+
+ case DLL_THREAD_ATTACH:
+ /* increment g_attachCount */
+ g_attachCount++;
+ break;
+
+ case DLL_THREAD_DETACH:
+ break;
+ }
+ return bResult;
+}
+
+
+#ifdef WIN32
+BOOL __stdcall _DllMainCRTStartup(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ return DllMain(hinstDLL, fdwReason, lpvReserved);
+}
+#endif
+
+/* function to return the current attach count */
+#ifdef WIN32
+__declspec(dllexport)
+#endif
+int PALAPI GetAttachCount( void )
+{
+ return g_attachCount;
+}
diff --git a/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/dllmain2.c b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/dllmain2.c
new file mode 100644
index 0000000000..4e3f8862a4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/dllmain2.c
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: dllmain2.c
+**
+** Purpose: Test to ensure DllMain() is called with THREAD_ATTACH
+** when a thread in the application is started.
+**
+** Dependencies: none
+**
+**
+**===========================================================================*/
+
+#include <palsuite.h>
+
+/* count of the number of times DllMain() was called with THREAD_DETACH */
+static int g_attachCount = 0;
+
+
+/* standard DllMain() */
+BOOL PALAPI DllMain(HINSTANCE hinstDLL, DWORD reason, LPVOID lpvReserved)
+{
+ BOOL bResult = TRUE;
+
+ switch( reason )
+ {
+ case DLL_PROCESS_ATTACH:
+ {
+ break;
+ }
+
+ case DLL_PROCESS_DETACH:
+ {
+ break;
+ }
+
+ case DLL_THREAD_ATTACH:
+ /* increment g_attachCount */
+ g_attachCount++;
+ break;
+
+ case DLL_THREAD_DETACH:
+ break;
+ }
+ return bResult;
+}
+
+
+#ifdef WIN32
+BOOL __stdcall _DllMainCRTStartup(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ return DllMain(hinstDLL, fdwReason, lpvReserved);
+}
+#endif
+
+/* function to return the current attach count */
+#ifdef WIN32
+__declspec(dllexport)
+#endif
+int PALAPI GetAttachCount( void )
+{
+ return g_attachCount;
+}
diff --git a/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/test2.c b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/test2.c
new file mode 100644
index 0000000000..5fb694ea14
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/test2.c
@@ -0,0 +1,237 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test2.c
+**
+** Purpose: Test to ensure DisableThreadLibraryCalls() called for one
+** library will not disrupt THREAD_ATTACH notifications etc. by
+** other loaded modules.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** Fail
+** CreateThread
+** ResumeThread
+** LoadLibrary
+** FreeLibrary
+** GetProcAddress
+** WaitForSingleObject
+** GetLastError
+**
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+/* SHLEXT is defined only for Unix variants */
+
+#if defined(SHLEXT)
+#define rgchLibraryFile1 "dllmain1"SHLEXT
+#define rgchLibraryFile2 "dllmain2"SHLEXT
+#define szFunction "GetAttachCount"
+#else
+#define rgchLibraryFile1 "dllmain1"
+#define rgchLibraryFile2 "dllmain2"
+#define szFunction "_GetAttachCount@0"
+#endif
+
+/* define our test function type */
+typedef int ( PALAPI *LPTESTFUNC )( void );
+
+
+
+/**
+ * ThreadFunc
+ *
+ * Dummy thread function for causing DLL thread notifications.
+ */
+DWORD PALAPI ThreadFunc( LPVOID param )
+{
+ /* simulate some brief "work" */
+ int i;
+ for( i=0; i<100000; i++ )
+ {
+ }
+
+ return 0;
+}
+
+
+/* main program entry point */
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+
+ HANDLE hLib1 = NULL;
+ HANDLE hLib2 = NULL;
+ LPTESTFUNC pFunc1;
+ LPTESTFUNC pFunc2;
+ int attachCount1a = 0;
+ int attachCount1b = 0;
+ int attachCount2a = 0;
+ int attachCount2b = 0;
+
+ HANDLE hThread = NULL;
+ DWORD IDThread;
+ DWORD dwRet;
+
+ BOOL bResult = FAIL;
+
+ /* initialize the PAL */
+ if( PAL_Initialize(argc, argv) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* Load the first test library */
+ hLib1 = LoadLibrary( rgchLibraryFile1 );
+ if(hLib1 == NULL)
+ {
+ Fail( "ERROR:%lu:LoadLibrary() call failed for %s\n",
+ GetLastError(),
+ rgchLibraryFile1 );
+ }
+
+ /* Load the second test library */
+ hLib2 = LoadLibrary( rgchLibraryFile2 );
+ if(hLib2 == NULL)
+ {
+ Trace( "ERROR:%lu:LoadLibrary() call failed for %s\n",
+ GetLastError(),
+ rgchLibraryFile2 );
+ if( ! FreeLibrary( hLib1 ) )
+ {
+ Trace( "ERROR:%lu:FreeLibrary() call failed\n", GetLastError() );
+ }
+ Fail( "test failed\n" );
+ }
+
+
+ /* Get the addresses of our test functions in the dlls */
+ pFunc1 = (LPTESTFUNC)GetProcAddress( hLib1, szFunction );
+ if( pFunc1 == NULL )
+ {
+ Trace( "ERROR:%lu%:Unable to find \"%s\" in library \"%s\"\n",
+ GetLastError(),
+ szFunction,
+ rgchLibraryFile1 );
+ goto cleanup;
+ }
+
+ pFunc2 = (LPTESTFUNC)GetProcAddress( hLib2, szFunction );
+ if( pFunc1 == NULL )
+ {
+ Trace( "ERROR:%lu%:Unable to find \"%s\" in library \"%s\"\n",
+ GetLastError(),
+ szFunction,
+ rgchLibraryFile2 );
+ goto cleanup;
+ }
+
+ /* disable thread library calls for the first library */
+ if( ! DisableThreadLibraryCalls( (HMODULE)hLib1 ) )
+ {
+ Trace( "ERROR:%lu:DisableThreadLibraryCalls() call failed\n",
+ GetLastError() );
+ goto cleanup;
+ }
+
+ /* Execute the test function to get the attach count */
+ attachCount1a = pFunc1();
+ attachCount2a = pFunc2();
+
+ /* run another dummy thread to cause notification of the libraries */
+ hThread = CreateThread( NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE) ThreadFunc, /* thread function */
+ (LPVOID) NULL, /* pass thread index as */
+ /* function argument */
+ CREATE_SUSPENDED, /* create suspended */
+ &IDThread ); /* returns thread id */
+
+ /* Check the return value for success. */
+ if( hThread == NULL )
+ {
+ /* error creating thread */
+ Trace( "ERROR:%lu:CreateThread() call failed\n",
+ GetLastError() );
+ goto cleanup;
+ }
+
+ /* Resume the suspended thread */
+ if( ResumeThread( hThread ) == -1 )
+ {
+ Trace( "ERROR:%lu:ResumeThread() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+
+ /* wait for the thread to complete */
+ dwRet = WaitForSingleObject( hThread, INFINITE );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR: WaitForSingleObject returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ goto cleanup;
+ }
+
+
+ /* Execute the test function to get the new detach count */
+ attachCount1b = pFunc1();
+ attachCount2b = pFunc2();
+
+ /* validate the result */
+ if( attachCount1b != attachCount1a )
+ {
+ Trace( "FAIL: unexpected DLL attach count %d, expected %d\n",
+ attachCount1b,
+ attachCount1a );
+ goto cleanup;
+ }
+
+ /* validate the result */
+ if( attachCount2b != (attachCount2a + 1) )
+ {
+ Trace( "FAIL: unexpected DLL attach count %d, expected %d\n",
+ attachCount2b,
+ (attachCount2a + 1) );
+ goto cleanup;
+ }
+
+ bResult = PASS;
+
+cleanup:
+ /* Unload the test libraries */
+ if( !FreeLibrary( hLib1 ) )
+ {
+ Trace( "ERROR:%u:FreeLibrary() failed on library \"%s\"\n",
+ GetLastError(),
+ rgchLibraryFile1 );
+ bResult = FAIL;
+ }
+
+ if( !FreeLibrary( hLib2 ) )
+ {
+ Trace( "ERROR:%u:FreeLibrary() failed on library \"%s\"\n",
+ GetLastError(),
+ rgchLibraryFile2 );
+ bResult = FAIL;
+ }
+
+ /* check for failure */
+ if( bResult == FAIL )
+ {
+ Fail( "test failed\n" );
+ }
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
+
diff --git a/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/testinfo.dat b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/testinfo.dat
new file mode 100644
index 0000000000..6d28595628
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/testinfo.dat
@@ -0,0 +1,18 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = DisableThreadLibraryCalls
+Name = Test for DisableThreadLibraryCalls
+TYPE = DEFAULT
+EXE1 = test2
+LIB1 = dllmain1
+LIB2 = dllmain2
+Description
+= Test to ensure proper operation of the DisableThreadLibraryCalls()
+= API. This tests to make sure the DisableThreadLibraryCalls() called
+= for one DLL loaded by a process takes effect *only* for that DLL
+= and not for any others, by checking the THREAD_ATTACH notifications
+= in each.
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/CMakeLists.txt b/src/pal/tests/palsuite/threading/DuplicateHandle/CMakeLists.txt
new file mode 100644
index 0000000000..b908c1246b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test10)
+add_subdirectory(test11)
+add_subdirectory(test12)
+add_subdirectory(test2)
+add_subdirectory(test3)
+add_subdirectory(test4)
+add_subdirectory(test5)
+add_subdirectory(test6)
+add_subdirectory(test7)
+add_subdirectory(test8)
+add_subdirectory(test9)
+
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/DuplicateHandle/test1/CMakeLists.txt
new file mode 100644
index 0000000000..04588b75fe
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_duplicatehandle_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_duplicatehandle_test1 coreclrpal)
+
+target_link_libraries(paltest_duplicatehandle_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test1/test1.c b/src/pal/tests/palsuite/threading/DuplicateHandle/test1/test1.c
new file mode 100644
index 0000000000..e080e98ae8
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test1/test1.c
@@ -0,0 +1,152 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test1.c (DuplicateHandle)
+**
+** Purpose: Tests the PAL implementation of the DuplicateHandle function.
+** This test will create two handles to file, one to write and
+** one to read what was written. Test on a closed handle and a
+** NULL handle, both should fail.
+**
+**
+**===================================================================*/
+#include <palsuite.h>
+
+int __cdecl main(int argc, char **argv)
+{
+ HANDLE hFile;
+ HANDLE hDupFile;
+ char buf[256];
+ char teststr[] = "A uNiQuE tEsT sTrInG";
+ char lpFileName[] = "testfile.txt";
+ DWORD dwBytesWritten;
+ DWORD dwBytesRead;
+ BOOL bRetVal;
+
+ /*Initalize the PAL*/
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return (FAIL);
+ }
+
+ /*Create a file handle with CreateFile*/
+ hFile = CreateFile(lpFileName,
+ GENERIC_WRITE|GENERIC_READ,
+ FILE_SHARE_WRITE|FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ Fail("ERROR: %u :unable to create file \"%s\".\n",
+ GetLastError(),
+ lpFileName);
+ }
+
+ /*Write test string to the file.*/
+ bRetVal = WriteFile(hFile, // handle to file
+ teststr, // data buffer
+ strlen(teststr), // number of bytes to write
+ &dwBytesWritten, // number of bytes written
+ NULL); // overlapped buffer
+
+ if (bRetVal == FALSE)
+ {
+ Trace("ERROR: %u : unable to write to file handle "
+ "hFile=0x%lx\n",
+ GetLastError(),
+ hFile);
+ CloseHandle(hFile);
+ Fail("");
+ }
+
+ /*Create a duplicate handle with DuplicateHandle.*/
+ if (!(DuplicateHandle(
+ GetCurrentProcess(),
+ hFile,
+ GetCurrentProcess(),
+ &hDupFile,
+ GENERIC_READ|GENERIC_WRITE,
+ FALSE,
+ DUPLICATE_SAME_ACCESS)))
+ {
+ Trace("ERROR: %u : Fail to create the duplicate handle"
+ " to hFile=0x%lx\n",
+ GetLastError(),
+ hFile);
+ CloseHandle(hFile);
+ Fail("");
+ }
+
+ memset(buf, 0, 256);
+
+ /*Read from the Duplicated handle.*/
+ bRetVal = ReadFile(hDupFile,
+ buf,
+ 256,
+ &dwBytesRead,
+ NULL);
+ if (bRetVal == FALSE)
+ {
+ Trace("ERROR: %u :unable to read from file handle "
+ "hFile=0x%lx\n",
+ GetLastError(),
+ hFile);
+ CloseHandle(hFile);
+ CloseHandle(hDupFile);
+ Fail("");
+ }
+
+ /*Compare what was written to what was read.*/
+ if (memcmp(teststr, buf, dwBytesRead) != 0)
+ {
+ Trace("ERROR: expected %s, got %s\n", teststr, buf);
+ CloseHandle(hFile);
+ CloseHandle(hDupFile);
+ Fail("");
+ }
+
+ /*Close the handles*/
+ CloseHandle(hFile);
+ CloseHandle(hDupFile);
+
+ /*Failure test: Create DuplicateHandle to a closed handle*/
+ if ((DuplicateHandle(
+ GetCurrentProcess(),
+ hFile,
+ GetCurrentProcess(),
+ &hDupFile,
+ GENERIC_READ|GENERIC_WRITE,
+ FALSE,
+ DUPLICATE_SAME_ACCESS)))
+ {
+ Fail("ERROR: %u :Created a duplicate handle to"
+ " a closed handle hFile=0x%lx\n",
+ GetLastError(),
+ hFile);
+ }
+
+ /*Failure test: Create DuplicateHandle to a NULL handle*/
+ hFile = NULL;
+ if ((DuplicateHandle(
+ GetCurrentProcess(),
+ hFile,
+ GetCurrentProcess(),
+ &hDupFile,
+ GENERIC_READ|GENERIC_WRITE,
+ FALSE,
+ DUPLICATE_SAME_ACCESS)))
+ {
+ Fail("ERROR: %u :Created a duplicate handle to "
+ " a NULL handle hFile=0x%lx\n",
+ GetLastError(),
+ hFile);
+ }
+
+ PAL_Terminate();
+ return (PASS);
+}
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test1/testinfo.dat b/src/pal/tests/palsuite/threading/DuplicateHandle/test1/testinfo.dat
new file mode 100644
index 0000000000..e22b0bea6a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test1/testinfo.dat
@@ -0,0 +1,15 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Threading
+Function = DuplicateHandle
+Name = Positive Test for DuplicateHandle
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Tests the PAL implementation of the DuplicateHandle function.
+= This test will create two handles to file, one to write and
+= one to read what was written. Test on a closed handle and a
+= NULL handle, both should fail.
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test10/CMakeLists.txt b/src/pal/tests/palsuite/threading/DuplicateHandle/test10/CMakeLists.txt
new file mode 100644
index 0000000000..ba16252cb4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test10/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test10.c
+)
+
+add_executable(paltest_duplicatehandle_test10
+ ${SOURCES}
+)
+
+add_dependencies(paltest_duplicatehandle_test10 coreclrpal)
+
+target_link_libraries(paltest_duplicatehandle_test10
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test10/test10.c b/src/pal/tests/palsuite/threading/DuplicateHandle/test10/test10.c
new file mode 100644
index 0000000000..108d748de6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test10/test10.c
@@ -0,0 +1,239 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test10.c (DuplicateHandle)
+**
+** Purpose: Tests the PAL implementation of the DuplicateHandle function.
+** This tests the operation of a duplicated Semaphore handle
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+enum wait_results
+{
+ WR_WAITING,
+ WR_GOT_MUTEX,
+ WR_TIMED_OUT,
+ WR_RELEASED
+};
+
+
+volatile int t1_result=WR_WAITING;
+volatile int t2_result=WR_WAITING;
+
+
+DWORD PALAPI ThreadTest1(LPVOID lpParam)
+{
+ DWORD dwWait;
+
+ dwWait = WaitForSingleObject((HANDLE)lpParam, 0);
+ if (dwWait == WAIT_OBJECT_0)
+ {
+ /* tell the main thread we got the mutex */
+ t1_result=WR_GOT_MUTEX;
+
+ /* wait for main thread to tell us to release the mutex */
+ while(WR_GOT_MUTEX == t1_result)
+ Sleep(1);
+ ReleaseSemaphore((HANDLE)lpParam, 1, NULL);
+
+ /* tell the main thread we released the mutex */
+ t1_result = WR_RELEASED;
+ }
+ else
+ {
+ t1_result = WR_TIMED_OUT;
+ }
+ return 0;
+}
+
+DWORD PALAPI ThreadTest2(LPVOID lpParam)
+{
+ DWORD dwWait;
+
+ dwWait = WaitForSingleObject((HANDLE)lpParam, 0 );
+ if (dwWait == WAIT_OBJECT_0)
+ {
+ ReleaseSemaphore((HANDLE)lpParam, 1, NULL);
+ t2_result = WR_GOT_MUTEX;
+ }
+ else
+ {
+ t2_result = WR_TIMED_OUT;
+ }
+
+ return 0;
+}
+
+
+int __cdecl main(int argc, char **argv)
+{
+
+ HANDLE hDupSemaphore;
+ HANDLE hSemaphore;
+ HANDLE hThread;
+ HANDLE hThread2;
+ BOOL bDupHandle=FALSE;
+ DWORD dwThreadId = 0;
+
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return(FAIL);
+ }
+
+ hSemaphore = CreateSemaphoreW( NULL,
+ 1,
+ 1,
+ NULL);
+ if (hSemaphore == NULL)
+ {
+ Fail("PALSUITE ERROR:%u: Unable to create mutex\n",
+ GetLastError());
+ }
+
+ /*Create Duplicate of the Semaphore above*/
+ bDupHandle = DuplicateHandle(GetCurrentProcess(),
+ hSemaphore,
+ GetCurrentProcess(),
+ &hDupSemaphore,
+ GENERIC_READ|GENERIC_WRITE,
+ FALSE,
+ DUPLICATE_SAME_ACCESS);
+ if (!bDupHandle)
+ {
+ Trace("PALSUITE ERROR:%u: Created the duplicate handle to "
+ "closed event handle hSemaphore=0x%lx\n",
+ GetLastError(),
+ hSemaphore);
+ CloseHandle(hSemaphore);
+ Fail("");
+ }
+
+ /*Create a thread to test the Semaphore*/
+ hThread = CreateThread(NULL,
+ 0,
+ &ThreadTest1,
+ hSemaphore,
+ 0,
+ &dwThreadId);
+ if (hThread == NULL)
+ {
+ Trace("PALSUITE ERROR:%u: unable to create thread\n",
+ GetLastError());
+ CloseHandle(hSemaphore);
+ CloseHandle(hDupSemaphore);
+ Fail("");
+ }
+
+ /* wait until thread has taken the mutex */
+ while (WR_WAITING == t1_result)
+ Sleep(1);
+
+ if(WR_TIMED_OUT == t1_result)
+ {
+ Trace("PALSUITE ERROR: %u: thread couldn't acquire the semaphore\n",
+ GetLastError());
+ CloseHandle(hSemaphore);
+ CloseHandle(hDupSemaphore);
+ CloseHandle(hThread);
+ Fail("");
+ }
+
+ /*Create a second thread to use the Semaphore's duplicate handle*/
+ /*This thread should block since the Semaphore is owned by another
+ thread*/
+ hThread2 = CreateThread(NULL,
+ 0,
+ &ThreadTest2,
+ hDupSemaphore,
+ 0,
+ &dwThreadId);
+
+ if (hThread2 == NULL)
+ {
+ Trace("PALSUITE ERROR:%u: unable to create thread\n",
+ GetLastError());
+ CloseHandle(hSemaphore);
+ CloseHandle(hDupSemaphore);
+ CloseHandle(hThread);
+ Fail("");
+ }
+
+ /* wait until thread has tried to take the mutex */
+ while (WR_WAITING == t2_result)
+ Sleep(1);
+
+ if (WR_TIMED_OUT != t2_result )
+ {
+ Trace("PALSUITE ERROR:%u: Able to take mutex %#x while its "
+ "duplicate %#x is held\n", GetLastError(), hDupSemaphore,
+ hSemaphore);
+ CloseHandle(hSemaphore);
+ CloseHandle(hDupSemaphore);
+ CloseHandle(hThread);
+ CloseHandle(hThread2);
+ Fail("");
+ }
+
+ /* reset second thread status */
+ t2_result = WR_WAITING;
+
+ /* tell thread 1 to release the mutex */
+ t1_result = WR_WAITING;
+
+ /* wait for thread 1 to release the mutex */
+ while (WR_WAITING == t1_result)
+ Sleep(1);
+
+ CloseHandle(hThread2);
+
+ /*Re-Create the second thread to reuse the duplicated Semaphore*/
+ /*Since the Semaphore has since been released, the thread should
+ put WR_GOT_MUTEX into t2_result */
+ hThread2 = CreateThread(NULL,
+ 0,
+ &ThreadTest2,
+ hDupSemaphore,
+ 0,
+ &dwThreadId);
+
+ if (hThread2 == NULL)
+ {
+ Trace("PALSUITE ERROR:%u: unable to create thread\n",
+ GetLastError());
+ CloseHandle(hSemaphore);
+ CloseHandle(hDupSemaphore);
+ CloseHandle(hThread);
+ Fail("");
+ }
+
+ /* wait until thread has taken the semaphore */
+ while (WR_WAITING == t2_result)
+ Sleep(1);
+
+ if (WR_GOT_MUTEX != t2_result )
+ {
+ Trace("PALSUITE ERROR:%u: Unable to take semaphore %#x after its"
+ " duplicate %#x was released\n", GetLastError(), hDupSemaphore,
+ hSemaphore);
+ CloseHandle(hSemaphore);
+ CloseHandle(hDupSemaphore);
+ CloseHandle(hThread);
+ CloseHandle(hThread2);
+ Fail("");
+ }
+
+ /*Cleanup.*/
+ CloseHandle(hSemaphore);
+ CloseHandle(hDupSemaphore);
+ CloseHandle(hThread);
+ CloseHandle(hThread2);
+
+ PAL_Terminate();
+ return (PASS);
+}
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test10/testinfo.dat b/src/pal/tests/palsuite/threading/DuplicateHandle/test10/testinfo.dat
new file mode 100644
index 0000000000..674c71c2b3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test10/testinfo.dat
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Threading
+Function = DuplicateHandle
+Name = Test for DuplicateHandle (CreateSemaphore)
+TYPE = DEFAULT
+EXE1 = test10
+Description
+= Tests the PAL implementation of the DuplicateHandle function.
+= This test duplication of a Semaphore handle. The test will
+= create a Semaphore and duplicate a handle to it.
+= Then two subthreads will be used to verify that they can selectively
+= block each other with different handles that refer to the
+= same Semaphore
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test11/CMakeLists.txt b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/CMakeLists.txt
new file mode 100644
index 0000000000..68ce7b23fb
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ test11.c
+)
+
+add_executable(paltest_duplicatehandle_test11
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_duplicatehandle_test11 coreclrpal)
+
+target_link_libraries(paltest_duplicatehandle_test11
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ childprocess.c
+)
+
+add_executable(paltest_duplicatehandle_test11_child
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_duplicatehandle_test11_child coreclrpal)
+
+target_link_libraries(paltest_duplicatehandle_test11_child
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test11/childprocess.c b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/childprocess.c
new file mode 100644
index 0000000000..d5b310e46c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/childprocess.c
@@ -0,0 +1,74 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: childprocess.c
+**
+** Purpose: Test to ensure DuplicateHandle works properly.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateMutexW
+** WaitForSingleObject
+** CloseHandle
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+#include "myexitcode.h"
+
+
+int __cdecl main( int argc, char **argv )
+{
+ HANDLE hMutex;
+ WCHAR wszMutexName[] = { 'T','E','S','T','1','1','\0' };
+ DWORD dwRet;
+ int i;
+
+ /* initialize the PAL */
+ if( PAL_Initialize(argc, argv) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* open a mutex to synchronize with the parent process */
+ hMutex = CreateMutexW( NULL, FALSE, wszMutexName );
+ if( hMutex == NULL )
+ {
+ Fail( "ERROR:%lu:CreateMutex() call failed\r\n", GetLastError() );
+ }
+
+ /* acquire the mutex lock */
+ dwRet = WaitForSingleObject( hMutex, 10000 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0",
+ dwRet );
+ if( ! CloseHandle( hMutex ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ Fail( "test failed\n" );
+ }
+
+
+ /* simulate some activity */
+ for( i=0; i<50000; i++ )
+ ;
+
+ /* close our mutex handle */
+ if( ! CloseHandle( hMutex ) )
+ {
+ Fail( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* return the predefined exit code */
+ return TEST_EXIT_CODE;
+}
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test11/myexitcode.h b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/myexitcode.h
new file mode 100644
index 0000000000..84801cbb54
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/myexitcode.h
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: duplicatehandle/test11/myexitcode.h
+**
+** Purpose: Define an exit code constant.
+**
+**
+**=========================================================*/
+#define TEST_EXIT_CODE 31
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test11/test11.c b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/test11.c
new file mode 100644
index 0000000000..b05244c4b8
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/test11.c
@@ -0,0 +1,364 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test11.c
+**
+** Purpose:
+**
+** Test to ensure proper operation of the DuplicateHandle API.
+** The test launches a trivial child process, then opens
+** a handle to it using OpenProcess. It then duplicates that
+** handle and uses it to wait for the child process to terminate,
+** and then checks the exit code of the child process in order to
+** verify that it was in fact a handle to the correct
+** process. The test tries to duplicate the handle again after
+** the process has been closed, to verify that failure ensues.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** Fail
+** ZeroMemory
+** GetCurrentDirectoryW
+** CreateProcessW
+** WaitForSingleObject
+** CreateMutexW
+** ReleaseMutex
+** CloseHandle
+** GetLastError
+** strlen
+** strncpy
+**
+**
+**===========================================================================*/
+#include <palsuite.h>
+#include "myexitcode.h"
+
+
+static const char* rgchPathDelim = "\\";
+
+
+int
+mkAbsoluteFilename( LPSTR dirName,
+ DWORD dwDirLength,
+ LPCSTR fileName,
+ DWORD dwFileLength,
+ LPSTR absPathName )
+{
+ DWORD sizeDN, sizeFN, sizeAPN;
+
+ sizeDN = strlen( dirName );
+ sizeFN = strlen( fileName );
+ sizeAPN = (sizeDN + 1 + sizeFN + 1);
+
+ /* ensure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */
+ if( sizeAPN > _MAX_PATH )
+ {
+ return ( 0 );
+ }
+
+ strncpy( absPathName, dirName, dwDirLength +1 );
+ strncpy( absPathName, rgchPathDelim, 2 );
+ strncpy( absPathName, fileName, dwFileLength +1 );
+
+ return (sizeAPN);
+
+}
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ const char* rgchChildFile = "childprocess";
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ DWORD dwError;
+ DWORD dwExitCode;
+ DWORD dwFileLength;
+ DWORD dwDirLength;
+ DWORD dwSize;
+ DWORD dwRet;
+
+ HANDLE hMutex;
+ HANDLE hChildProcess;
+ HANDLE hDupChildProcess;
+
+ char rgchDirName[_MAX_DIR];
+ char absPathBuf[_MAX_PATH];
+ char* rgchAbsPathName;
+
+ BOOL ret = FAIL;
+ BOOL bChildDone = FALSE;
+ WCHAR wszMutexName[] = { 'T','E','S','T','1','1','\0' };
+
+ /* initialize the PAL */
+ if( PAL_Initialize(argc, argv) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* create a mutex to synchronize with the child process */
+ hMutex = CreateMutexW( NULL, TRUE, wszMutexName );
+ if( hMutex == NULL )
+ {
+ Fail( "ERROR:%lu:CreateMutex() call failed\r\n", GetLastError() );
+ }
+
+ /* zero our process and startup info structures */
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof( si );
+ ZeroMemory( &pi, sizeof(pi) );
+
+ /* build the absolute path to the child process */
+ rgchAbsPathName = &absPathBuf[0];
+ dwFileLength = strlen( rgchChildFile );
+
+ dwDirLength = GetCurrentDirectory( _MAX_PATH, rgchDirName );
+ if( dwDirLength == 0 )
+ {
+ dwError = GetLastError();
+ if( ReleaseMutex( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
+ }
+ if( CloseHandle( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ Fail( "GetCurrentDirectory call failed with error code %d\n",
+ dwError );
+ }
+
+ dwSize = mkAbsoluteFilename( rgchDirName,
+ dwDirLength,
+ rgchChildFile,
+ dwFileLength,
+ rgchAbsPathName );
+ if( dwSize == 0 )
+ {
+ if( ReleaseMutex( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
+ }
+ if( CloseHandle( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ Fail( "Palsuite Code: mkAbsoluteFilename() call failed. Could ",
+ "not build absolute path name to file\n. Exiting.\n" );
+ }
+
+ /* launch the child process */
+ if( !CreateProcess( NULL, /* module name to execute */
+ rgchAbsPathName, /* command line */
+ NULL, /* process handle not */
+ /* inheritable */
+ NULL, /* thread handle not */
+ /*inheritable */
+ FALSE, /* handle inheritance */
+ CREATE_NEW_CONSOLE, /* dwCreationFlags */
+ NULL, /* use parent's environment */
+ NULL, /* use parent's starting */
+ /* directory */
+ &si, /* startup info struct */
+ &pi ) /* process info struct */
+ )
+ {
+ dwError = GetLastError();
+ if( ReleaseMutex( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
+ }
+ if( CloseHandle( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ Fail( "CreateProcess call failed with error code %d\n",
+ dwError );
+ }
+
+ /* open another handle to the child process */
+ hChildProcess = OpenProcess( PROCESS_ALL_ACCESS, /* access */
+ FALSE, /* inheritable */
+ pi.dwProcessId /* process id */
+ );
+ if( hChildProcess == NULL )
+ {
+ dwError = GetLastError();
+ if( ReleaseMutex( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
+ }
+ Trace( "ERROR:%lu:OpenProcess call failed\n", dwError );
+ goto cleanup3;
+ }
+
+ /* duplicate the child process handle */
+ if( ! DuplicateHandle( GetCurrentProcess(),
+ hChildProcess,
+ GetCurrentProcess(),
+ &hDupChildProcess,
+ GENERIC_READ|GENERIC_WRITE,
+ FALSE,
+ DUPLICATE_SAME_ACCESS) )
+ {
+ Trace( "ERROR:%lu:DuplicateHandle() call failed\n", GetLastError() );
+ if( ReleaseMutex( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
+ }
+ goto cleanup2;
+ }
+
+ /* release the mutex so the child can proceed */
+ if( ReleaseMutex( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
+ goto cleanup1;
+ }
+
+ /* wait for the child process to complete, using the new handle */
+ dwRet = WaitForSingleObject( hDupChildProcess, 10000 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject call returned %lu, "
+ "expected WAIT_OBJECT_0",
+ dwRet );
+ goto cleanup1;
+ }
+
+ /* remember that we waited until the child was finished */
+ bChildDone = TRUE;
+
+ /* check the exit code from the process -- this is a bit of an */
+ /* extra verification that we opened the correct process handle */
+ if( ! GetExitCodeProcess( hDupChildProcess, &dwExitCode ) )
+ {
+ Trace( "ERROR:%lu:GetExitCodeProcess call failed\n", GetLastError() );
+ goto cleanup1;
+ }
+
+ /* verification */
+ if( (dwExitCode & 0xFF) != (TEST_EXIT_CODE & 0xFF) )
+ {
+ Trace( "GetExitCodeProcess returned an incorrect exit code %d, "
+ "expected value is %d\n",
+ (dwExitCode & 0xFF),
+ (TEST_EXIT_CODE & 0xFF));
+ goto cleanup1;
+ }
+
+ /* close the duplicate handle */
+ if( ! CloseHandle( hDupChildProcess ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle call failed\n", GetLastError() );
+ goto cleanup2;
+ }
+
+ /* close the child process handle */
+ if( ! CloseHandle ( hChildProcess ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ goto cleanup3;
+ }
+
+ /* try to call duplicate handle on the closed child process handle */
+ if( DuplicateHandle( GetCurrentProcess(),
+ hChildProcess,
+ GetCurrentProcess(),
+ &hDupChildProcess,
+ GENERIC_READ|GENERIC_WRITE,
+ FALSE,
+ DUPLICATE_SAME_ACCESS) )
+ {
+ Trace( "ERROR:%lu:DuplicateHandle call succeeded on "
+ "a closed process handle, expected ERROR_INVALID_HANDLE\n" );
+ if( ! CloseHandle( hDupChildProcess ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle call failed\n", GetLastError() );
+ }
+ goto cleanup3;
+ }
+
+ /* verify that the last error was ERROR_INVALID_HANDLE */
+ dwRet = GetLastError();
+ if( dwRet != ERROR_INVALID_HANDLE )
+ {
+ Trace( "ERROR:DuplicateHandle returned %lu, "
+ "expected ERROR_INVALID_HANDLE\n",
+ dwRet );
+ goto cleanup3;
+ }
+
+
+ /* success if we get here */
+ ret = PASS;
+
+ /* skip the cleanup stuff that's already done */
+ goto cleanup3;
+
+
+cleanup1:
+ /* close our duplicate handle */
+ if( ! CloseHandle( hDupChildProcess ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle call failed\n", GetLastError() );
+ ret = FAIL;
+ }
+
+cleanup2:
+ /* wait on the child process to complete if necessary */
+ if( ! bChildDone )
+ {
+ dwRet = WaitForSingleObject( hChildProcess, 10000 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject call returned %lu, "
+ "expected WAIT_OBJECT_0",
+ dwRet );
+ ret = FAIL;
+ }
+ }
+
+ /* close our child process handle */
+ if( CloseHandle ( hChildProcess ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ ret = FAIL;
+ }
+
+cleanup3:
+ /* close all our other handles */
+ if( CloseHandle ( pi.hProcess ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ ret = FAIL;
+ }
+ if( CloseHandle ( pi.hThread ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ ret = FAIL;
+ }
+ if( CloseHandle( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ ret = FAIL;
+ }
+
+ if( ret == FAIL )
+ {
+ Fail( "test failed\n" );
+ }
+
+
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test11/testinfo.dat b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/testinfo.dat
new file mode 100644
index 0000000000..1937877880
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/testinfo.dat
@@ -0,0 +1,19 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = DuplicateHandle
+Name = Test for DuplicateHandle
+TYPE = DEFAULT
+EXE1 = test11
+EXE2 = childprocess
+Description
+= Test to ensure proper operation of the DuplicateHandle API.
+= The test launches a trivial child process, then opens
+= a handle to it using OpenProcess. It then duplicates that
+= handle and uses it to wait for the child process to terminate,
+= and then checks the exit code of the child process in order to
+= verify that it was in fact a handle to the correct= process. The test tries to duplicate the handle again after
+= the process has been closed, to verify that failure ensues.
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test12/CMakeLists.txt b/src/pal/tests/palsuite/threading/DuplicateHandle/test12/CMakeLists.txt
new file mode 100644
index 0000000000..961a9c64e6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test12/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test12.c
+)
+
+add_executable(paltest_duplicatehandle_test12
+ ${SOURCES}
+)
+
+add_dependencies(paltest_duplicatehandle_test12 coreclrpal)
+
+target_link_libraries(paltest_duplicatehandle_test12
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test12/test12.c b/src/pal/tests/palsuite/threading/DuplicateHandle/test12/test12.c
new file mode 100644
index 0000000000..519194bc3a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test12/test12.c
@@ -0,0 +1,129 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test12.c (DuplicateHandle)
+**
+** Purpose: Tests the PAL implementation of the DuplicateHandle function.
+** This test will create handle to file (to write) and close it,
+** then call duplicate handle to read what was written.
+**
+**
+**===================================================================*/
+#include <palsuite.h>
+
+int __cdecl main(int argc, char **argv)
+{
+ HANDLE hFile;
+ HANDLE hDupFile;
+ char buf[256];
+ char teststr[] = "A uNiQuE tEsT sTrInG";
+ char lpFileName[] = "testfile.txt";
+ DWORD dwBytesWritten;
+ DWORD dwBytesRead;
+ BOOL bRetVal;
+
+ /*Initalize the PAL*/
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return (FAIL);
+ }
+
+ /*Create a file handle with CreateFile*/
+ hFile = CreateFile(lpFileName,
+ GENERIC_WRITE|GENERIC_READ,
+ FILE_SHARE_WRITE|FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ Fail("ERROR: %u :unable to create file \"%s\".\n",
+ GetLastError(),
+ lpFileName);
+ }
+
+ /*Write test string to the file.*/
+ bRetVal = WriteFile(hFile, // handle to file
+ teststr, // data buffer
+ strlen(teststr), // number of bytes to write
+ &dwBytesWritten, // number of bytes written
+ NULL); // overlapped buffer
+
+ if (bRetVal == FALSE)
+ {
+ Trace("ERROR: %u : unable to write to file handle "
+ "hFile=0x%lx\n",
+ GetLastError(),
+ hFile);
+ CloseHandle(hFile);
+ Fail("");
+ }
+
+ /*Create a duplicate handle with DuplicateHandle.*/
+ if (!(DuplicateHandle(
+ GetCurrentProcess(),
+ hFile,
+ GetCurrentProcess(),
+ &hDupFile,
+ GENERIC_READ|GENERIC_WRITE,
+ FALSE,
+ DUPLICATE_SAME_ACCESS)))
+ {
+ Trace("ERROR: %u : Fail to create the duplicate handle"
+ " to hFile=0x%lx\n",
+ GetLastError(),
+ hFile);
+ CloseHandle(hFile);
+ Fail("");
+ }
+
+ if( !CloseHandle(hFile) )
+ {
+ Fail("Duplicate Handle:Unable to close original file: Error[%u]\n", GetLastError());
+ }
+
+ memset(buf, 0, 256);
+
+ /*Read from the Duplicated handle.*/
+ bRetVal = ReadFile(hDupFile,
+ buf,
+ 256,
+ &dwBytesRead,
+ NULL);
+ if (bRetVal == FALSE)
+ {
+ Trace("ERROR: %u :unable to read from file handle "
+ "hFile=0x%lx\n",
+ GetLastError(),
+ hDupFile);
+ CloseHandle(hDupFile);
+ Fail("");
+ }
+
+ /*Compare what was written to what was read.*/
+ if (memcmp(teststr, buf, dwBytesRead) != 0)
+ {
+ Trace("ERROR: expected %s, got %s\n", teststr, buf);
+ CloseHandle(hDupFile);
+ Fail("");
+ }
+
+ /*Close the handles*/
+ CloseHandle(hDupFile);
+
+ bRetVal = DeleteFileA(lpFileName);
+ if (bRetVal != TRUE)
+ {
+ Trace("Error:%u: DuplicateHandle, DeleteFileA: Couldn't delete DeleteFileA's"
+ " %s\n", GetLastError(), lpFileName);
+ Fail("");
+ }
+
+
+ PAL_Terminate();
+ return (PASS);
+}
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test12/testinfo.dat b/src/pal/tests/palsuite/threading/DuplicateHandle/test12/testinfo.dat
new file mode 100644
index 0000000000..3d73362eb3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test12/testinfo.dat
@@ -0,0 +1,15 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Threading
+Function = DuplicateHandle
+Name = Positive Test for DuplicateHandle
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Tests the PAL implementation of the DuplicateHandle function.
+= This test will create handle to file (to write),
+= then call duplicate handle, and close the original handle
+= and then use duplicated handle to read what was written.
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/DuplicateHandle/test2/CMakeLists.txt
new file mode 100644
index 0000000000..06529a6204
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_duplicatehandle_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_duplicatehandle_test2 coreclrpal)
+
+target_link_libraries(paltest_duplicatehandle_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test2/test2.c b/src/pal/tests/palsuite/threading/DuplicateHandle/test2/test2.c
new file mode 100644
index 0000000000..d1411e62d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test2/test2.c
@@ -0,0 +1,96 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test2.c (DuplicateHandle)
+**
+** Purpose: Tests the PAL implementation of the DuplicateHandle function.
+** This will test duplication of an CreateEvent handle. Test an
+** event in a signaled state to wait, and then set the duplicate
+** to nonsignaled state and perform the wait again. The wait on
+** the event should fail. Test the duplication of closed and NULL
+** events, these should fail.
+**
+**
+**===================================================================*/
+#include <palsuite.h>
+
+int __cdecl main(int argc, char **argv)
+{
+ HANDLE hEvent;
+ HANDLE hDupEvent;
+
+ /*Initalize the PAL.*/
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return (FAIL);
+ }
+
+ /*Create an Event, and set it in the signaled state.*/
+ hEvent = CreateEvent(0, TRUE, TRUE, 0);
+ if (hEvent == NULL)
+ {
+ Fail("ERROR: %u :unable to create event\n",
+ GetLastError());
+ }
+
+ /*Create a duplicate Event handle.*/
+ if (!(DuplicateHandle(GetCurrentProcess(),
+ hEvent,GetCurrentProcess(),
+ &hDupEvent,
+ GENERIC_READ|GENERIC_WRITE,
+ FALSE, DUPLICATE_SAME_ACCESS)))
+ {
+ Trace("ERROR: %u :Fail to create the duplicate handle"
+ " to hEvent=0x%lx\n",
+ GetLastError(),
+ hEvent);
+ CloseHandle(hEvent);
+ Fail("");
+ }
+
+ /*Perform wait on Event that is in signaled state.*/
+ if ((WaitForSingleObject(hEvent, 1000)) != WAIT_OBJECT_0)
+ {
+ Trace("ERROR: %u :WaitForSignalObject on Event=0x%lx set to "
+ " signaled state failed",
+ GetLastError(),
+ hEvent);
+ CloseHandle(hEvent);
+ CloseHandle(hDupEvent);
+ Fail("");
+ }
+
+ /*Set the Duplicate Event handle to nonsignaled state.*/
+ if ((ResetEvent(hDupEvent)) == 0)
+ {
+ Trace("ERROR: %u :unable to reset dup event\n",
+ GetLastError());
+ CloseHandle(hEvent);
+ CloseHandle(hDupEvent);
+ Fail("");
+ }
+
+ /*Perform wait on Event that is in signaled state.*/
+ if ((WaitForSingleObject(hEvent, 1000)) == WAIT_OBJECT_0)
+ {
+ Trace("ERROR: %u: WaitForSignalObject succeeded on Event=0x%lx "
+ " when Duplicate Event=0x%lx set to nonsignaled state"
+ " succeeded\n",
+ GetLastError(),
+ hEvent,
+ hDupEvent);
+ CloseHandle(hEvent);
+ CloseHandle(hDupEvent);
+ Fail("");
+ }
+
+ /*Close handles to events.*/
+ CloseHandle(hEvent);
+ CloseHandle(hDupEvent);
+
+ PAL_Terminate();
+ return (PASS);
+}
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test2/testinfo.dat b/src/pal/tests/palsuite/threading/DuplicateHandle/test2/testinfo.dat
new file mode 100644
index 0000000000..273440804e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test2/testinfo.dat
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Threading
+Function = DuplicateHandle
+Name = Test for DuplicateHandle
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Tests the PAL implementation of the DuplicateHandle function.
+= This will test duplication of an CreateEvent handle. Test an
+= event in a signaled state to wait, and then set the duplicate
+= to nonsignaled state and perform the wait again. The wait on
+= the event should fail. Test the duplication of closed and NULL
+= events, these should fail.
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/DuplicateHandle/test3/CMakeLists.txt
new file mode 100644
index 0000000000..7f961c2213
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test3.c
+)
+
+add_executable(paltest_duplicatehandle_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_duplicatehandle_test3 coreclrpal)
+
+target_link_libraries(paltest_duplicatehandle_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test3/test3.c b/src/pal/tests/palsuite/threading/DuplicateHandle/test3/test3.c
new file mode 100644
index 0000000000..fc91b5ec22
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test3/test3.c
@@ -0,0 +1,123 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test3.c (DuplicateHandle)
+**
+** Purpose: Tests the PAL implementation of the DuplicateHandle function.
+** This will test duplication of an OpenEvent handle. Test an
+** event in a signaled state to wait, and then set the duplicate
+** to nonsignaled state and perform the wait again. The wait on
+** the event should fail. Test the duplication of closed and NULL
+** events, these should fail.
+**
+**
+**===================================================================*/
+#include <palsuite.h>
+
+int __cdecl main(int argc, char **argv)
+{
+ HANDLE hCreateEvent;
+ HANDLE hOpenEvent;
+ HANDLE hDupEvent;
+ WCHAR lpEventName[]={'E','v','e','n','t','\0'};
+
+ /*Initialize the PAL.*/
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return (FAIL);
+ }
+
+ /*Create an Event, and set it in the signaled state.*/
+ hCreateEvent = CreateEventW(0,
+ TRUE,
+ TRUE,
+ lpEventName);
+ if (hCreateEvent == NULL)
+ {
+ Fail("ERROR: %u :unable to create event %s\n",
+ GetLastError(),
+ lpEventName);
+ }
+
+ /*Open another handle to hCreateHandle with OpenEvent*/
+ hOpenEvent = OpenEventW(EVENT_ALL_ACCESS,
+ TRUE,
+ lpEventName);
+ if (hOpenEvent == NULL)
+ {
+ Trace("ERROR: %u :unable to create handle with OpenEvent to %s\n",
+ GetLastError(),
+ lpEventName);
+ CloseHandle(hCreateEvent);
+ Fail("");
+ }
+
+ /*Create a duplicate Event handle*/
+ if (!(DuplicateHandle(GetCurrentProcess(),
+ hOpenEvent,
+ GetCurrentProcess(),
+ &hDupEvent,
+ GENERIC_READ|GENERIC_WRITE,
+ FALSE,
+ DUPLICATE_SAME_ACCESS)))
+ {
+ Trace("ERROR: %u :Fail to create the duplicate handle"
+ " to hCreateEvent=0x%lx\n",
+ GetLastError(),
+ hCreateEvent);
+ CloseHandle(hCreateEvent);
+ CloseHandle(hOpenEvent);
+ Fail("");
+ }
+
+ /*Perform wait on Event that is in signaled state*/
+ if ((WaitForSingleObject(hOpenEvent, 1000)) != WAIT_OBJECT_0)
+ {
+ Trace("ERROR: %u :WaitForSignalObject on hOpenEvent=0x%lx set to "
+ " signaled state failed\n",
+ GetLastError(),
+ hOpenEvent);
+ CloseHandle(hCreateEvent);
+ CloseHandle(hOpenEvent);
+ CloseHandle(hDupEvent);
+ Fail("");
+ }
+
+ /*Set the Duplicate Event handle to nonsignaled state*/
+ if ((ResetEvent(hDupEvent)) == 0)
+ {
+ Trace("ERROR: %u: unable to reset hDupEvent=0x%lx\n",
+ GetLastError(),
+ hDupEvent);
+ CloseHandle(hCreateEvent);
+ CloseHandle(hOpenEvent);
+ CloseHandle(hDupEvent);
+ Fail("");
+ }
+
+ /*Perform wait on Event that is in signaled state*/
+ if ((WaitForSingleObject(hOpenEvent, 1000)) == WAIT_OBJECT_0)
+ {
+ Trace("ERROR: %u :WaitForSignalObject succeeded on hOpenEvent=0x%lx "
+ " when Duplicate hDupEvent=0x%lx set to nonsignaled state"
+ " succeeded\n",
+ GetLastError(),
+ hOpenEvent,
+ hDupEvent);
+ CloseHandle(hCreateEvent);
+ CloseHandle(hOpenEvent);
+ CloseHandle(hDupEvent);
+ Fail("");
+ }
+
+ /*Close handles the events*/
+ CloseHandle(hCreateEvent);
+ CloseHandle(hOpenEvent);
+ CloseHandle(hDupEvent);
+
+ PAL_Terminate();
+ return (PASS);
+}
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test3/testinfo.dat b/src/pal/tests/palsuite/threading/DuplicateHandle/test3/testinfo.dat
new file mode 100644
index 0000000000..a10adb9a8e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test3/testinfo.dat
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Threading
+Function = DuplicateHandle
+Name = Test for DuplicateHandle
+TYPE = DEFAULT
+EXE1 = test3
+Description
+= Tests the PAL implementation of the DuplicateHandle function.
+= This will test duplication of an OpenEvent handle. Test an
+= event in a signaled state to wait, and then set the duplicate
+= to nonsignaled state and perform the wait again. The wait on
+= the event should fail. Test the duplication of closed and NULL
+= events, these should fail.
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test4/CMakeLists.txt b/src/pal/tests/palsuite/threading/DuplicateHandle/test4/CMakeLists.txt
new file mode 100644
index 0000000000..c3040d09ec
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test4/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test4.c
+)
+
+add_executable(paltest_duplicatehandle_test4
+ ${SOURCES}
+)
+
+add_dependencies(paltest_duplicatehandle_test4 coreclrpal)
+
+target_link_libraries(paltest_duplicatehandle_test4
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test4/test4.c b/src/pal/tests/palsuite/threading/DuplicateHandle/test4/test4.c
new file mode 100644
index 0000000000..14a72db461
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test4/test4.c
@@ -0,0 +1,239 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test4.c (DuplicateHandle)
+**
+** Purpose: Tests the PAL implementation of the DuplicateHandle function.
+** This test duplication of a Mutex handle. The test will comprise
+** of creating a Mutex and its duplicate and create a thread that
+** will get ownership. Another thread will be create that will
+** attempt to get ownership of the duplicate Mutex, this will
+** fail, since the Mutex is owned by another thread. The Mutex
+** will be released and then the thread will attempt to get
+** ownership of the duplicate Mutex, this will succeed.
+**
+**
+**===================================================================*/
+#include <palsuite.h>
+
+enum wait_results
+{
+ WR_WAITING,
+ WR_GOT_MUTEX,
+ WR_TIMED_OUT,
+ WR_RELEASED
+};
+
+
+volatile int t1_result=WR_WAITING;
+volatile int t2_result=WR_WAITING;
+
+
+DWORD PALAPI ThreadTest1(LPVOID lpParam)
+{
+ DWORD dwWait;
+
+ dwWait = WaitForSingleObject((HANDLE)lpParam, 0);
+ if (dwWait == WAIT_OBJECT_0)
+ {
+ /* tell the main thread we got the mutex */
+ t1_result=WR_GOT_MUTEX;
+
+ /* wait for main thread to tell us to release the mutex */
+ while(WR_GOT_MUTEX == t1_result)
+ Sleep(1);
+ ReleaseMutex((HANDLE)lpParam);
+
+ /* tell the main thread we released the mutex */
+ t1_result = WR_RELEASED;
+ }
+ else
+ {
+ t1_result = WR_TIMED_OUT;
+ }
+ return 0;
+}
+
+DWORD PALAPI ThreadTest2(LPVOID lpParam)
+{
+ DWORD dwWait;
+
+ dwWait = WaitForSingleObject((HANDLE)lpParam, 0 );
+ if (dwWait == WAIT_OBJECT_0)
+ {
+ ReleaseMutex((HANDLE)lpParam);
+ t2_result = WR_GOT_MUTEX;
+ }
+ else
+ {
+ t2_result = WR_TIMED_OUT;
+ }
+
+ return 0;
+}
+
+
+int __cdecl main(int argc, char **argv)
+{
+
+ HANDLE hDupMutex;
+ HANDLE hMutex;
+ HANDLE hThread;
+ HANDLE hThread2;
+ BOOL bDupHandle=FALSE;
+ DWORD dwThreadId = 0;
+
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return(FAIL);
+ }
+
+ /*Create Mutex without ownership*/
+ hMutex = CreateMutexW(NULL, // no security attributes
+ FALSE, // initially not owned
+ NULL); // name of mutex
+ if (hMutex == NULL)
+ {
+ Fail("ERROR:%u: Unable to create mutex\n",
+ GetLastError());
+ }
+
+ /*Create Duplicate of the Mutex above*/
+ bDupHandle = DuplicateHandle(GetCurrentProcess(),
+ hMutex,
+ GetCurrentProcess(),
+ &hDupMutex,
+ GENERIC_READ|GENERIC_WRITE,
+ FALSE,
+ DUPLICATE_SAME_ACCESS);
+ if (!bDupHandle)
+ {
+ Trace("ERROR:%u: Created the duplicate handle to "
+ "closed event handle hMutex=0x%lx\n",
+ GetLastError(),
+ hMutex);
+ CloseHandle(hMutex);
+ Fail("");
+ }
+
+ /*Create a thread to test the Mutex*/
+ hThread = CreateThread(NULL,
+ 0,
+ &ThreadTest1,
+ hMutex,
+ 0,
+ &dwThreadId);
+ if (hThread == NULL)
+ {
+ Trace("ERROR:%u: unable to create thread\n",
+ GetLastError());
+ CloseHandle(hMutex);
+ CloseHandle(hDupMutex);
+ Fail("");
+ }
+
+ /* wait until thread has taken the mutex */
+ while (WR_WAITING == t1_result)
+ Sleep(1);
+
+ if(WR_TIMED_OUT == t1_result)
+ {
+ Trace("ERROR: %u: thread 1 couldn't acquire the mutex\n");
+ CloseHandle(hMutex);
+ CloseHandle(hDupMutex);
+ CloseHandle(hThread);
+ Fail("");
+ }
+
+ /*Create a second thread to use the duplicate Mutex*/
+ /*This should fail since the Mutex is owned hThread*/
+ hThread2 = CreateThread(NULL,
+ 0,
+ &ThreadTest2,
+ hDupMutex,
+ 0,
+ &dwThreadId);
+
+ if (hThread2 == NULL)
+ {
+ Trace("ERROR:%u: unable to create thread\n",
+ GetLastError());
+ CloseHandle(hMutex);
+ CloseHandle(hDupMutex);
+ CloseHandle(hThread);
+ Fail("");
+ }
+
+ /* wait until thread has tried to take the mutex */
+ while (WR_WAITING == t2_result)
+ Sleep(1);
+
+ if (WR_TIMED_OUT != t2_result )
+ {
+ Trace("ERROR:%u: Able to take mutex %#x while its duplicate %#x is "
+ "held\n", hDupMutex, hMutex);
+ CloseHandle(hMutex);
+ CloseHandle(hDupMutex);
+ CloseHandle(hThread);
+ CloseHandle(hThread2);
+ Fail("");
+ }
+
+ /* reset second thread status */
+ t2_result = WR_WAITING;
+
+ /* tell thread 1 to release the mutex */
+ t1_result = WR_WAITING;
+
+ /* wait for thread 1 to release the mutex */
+ while (WR_WAITING == t1_result)
+ Sleep(1);
+
+ CloseHandle(hThread2);
+
+ /*Re-Create the second thread to reuse the duplicated Mutex*/
+ /*This test should pass, the Mutex has since been released*/
+ hThread2 = CreateThread(NULL,
+ 0,
+ &ThreadTest2,
+ hDupMutex,
+ 0,
+ &dwThreadId);
+
+ if (hThread2 == NULL)
+ {
+ Trace("ERROR:%u: unable to create thread\n",
+ GetLastError());
+ CloseHandle(hMutex);
+ CloseHandle(hDupMutex);
+ CloseHandle(hThread);
+ Fail("");
+ }
+
+ /* wait until thread has taken the mutex */
+ while (WR_WAITING == t2_result)
+ Sleep(1);
+
+ if (WR_GOT_MUTEX != t2_result )
+ {
+ Trace("ERROR:%u: Unable to take mutex %#x after its duplicate %#x was "
+ "released\n", hDupMutex, hMutex);
+ CloseHandle(hMutex);
+ CloseHandle(hDupMutex);
+ CloseHandle(hThread);
+ CloseHandle(hThread2);
+ Fail("");
+ }
+
+ /*Cleanup.*/
+ CloseHandle(hMutex);
+ CloseHandle(hDupMutex);
+ CloseHandle(hThread);
+ CloseHandle(hThread2);
+
+ PAL_Terminate();
+ return (PASS);
+}
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test4/testinfo.dat b/src/pal/tests/palsuite/threading/DuplicateHandle/test4/testinfo.dat
new file mode 100644
index 0000000000..64842f8713
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test4/testinfo.dat
@@ -0,0 +1,19 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Threading
+Function = DuplicateHandle
+Name = Test for DuplicateHandle (CreateMutex)
+TYPE = DEFAULT
+EXE1 = test4
+Description
+= Tests the PAL implementation of the DuplicateHandle function.
+= This test duplication of a Mutex handle. The test will comprise
+= of creating a Mutex and its duplicate and create a thread that will
+= get ownership. Another thread will be create that will attempt to
+= get ownership of the duplicate Mutex, this will fail, since the
+= Mutex is owned by another thread. The Mutex will be released and
+= then the thread will attempt to get ownership of the duplicate
+= Mutex, this will succeed.
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test5/CMakeLists.txt b/src/pal/tests/palsuite/threading/DuplicateHandle/test5/CMakeLists.txt
new file mode 100644
index 0000000000..bc468a4a75
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test5/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test5.c
+)
+
+add_executable(paltest_duplicatehandle_test5
+ ${SOURCES}
+)
+
+add_dependencies(paltest_duplicatehandle_test5 coreclrpal)
+
+target_link_libraries(paltest_duplicatehandle_test5
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test5/test5.c b/src/pal/tests/palsuite/threading/DuplicateHandle/test5/test5.c
new file mode 100644
index 0000000000..a588928f00
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test5/test5.c
@@ -0,0 +1,145 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test5.c (DuplicateHandle)
+**
+** Purpose: Tests the PAL implementation of the DuplicateHandle function,
+** with CreatePipe. This test will create a pipe and write to it,
+** the duplicate the read handle and read what was written.
+**
+** Depends: WriteFile
+** ReadFile
+** memcmp
+** CloseHandle
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+const char* cTestString = "one fish, two fish, red fish, blue fish.";
+
+int __cdecl main(int argc, char **argv)
+{
+ HANDLE hReadPipe = NULL;
+ HANDLE hWritePipe = NULL;
+ HANDLE hDupPipe = NULL;
+ BOOL bRetVal = FALSE;
+ DWORD dwBytesWritten;
+ DWORD dwBytesRead;
+ char buffer[256];
+
+ SECURITY_ATTRIBUTES lpPipeAttributes;
+
+ /*Initialize the PAL*/
+ if ((PAL_Initialize(argc, argv)) != 0)
+ {
+ return (FAIL);
+ }
+
+ /*Setup SECURITY_ATTRIBUTES structure for CreatePipe*/
+ lpPipeAttributes.nLength = sizeof(lpPipeAttributes);
+ lpPipeAttributes.lpSecurityDescriptor = NULL;
+ lpPipeAttributes.bInheritHandle = TRUE;
+
+ /*Create a Pipe*/
+ bRetVal = CreatePipe(&hReadPipe, /* read handle*/
+ &hWritePipe, /* write handle */
+ &lpPipeAttributes,/* security attributes*/
+ 0); /* pipe size*/
+ if (bRetVal == FALSE)
+ {
+ Fail("ERROR:%u:Unable to create pipe\n", GetLastError());
+ }
+
+ /*Write to the write pipe handle*/
+ bRetVal = WriteFile(hWritePipe, /* handle to write pipe*/
+ cTestString, /* buffer to write*/
+ strlen(cTestString),/* number of bytes to write*/
+ &dwBytesWritten, /* number of bytes written*/
+ NULL); /* overlapped buffer*/
+ if (bRetVal == FALSE)
+ {
+ Trace("ERROR:%u:unable to write to write pipe handle "
+ "hWritePipe=0x%lx\n", GetLastError(), hWritePipe);
+ CloseHandle(hReadPipe);
+ CloseHandle(hWritePipe);
+ Fail("");
+ }
+
+ /*Duplicate the pipe handle*/
+ if (!(DuplicateHandle(GetCurrentProcess(), /* source handle process*/
+ hReadPipe, /* handle to duplicate*/
+ GetCurrentProcess(), /* target process handle*/
+ &hDupPipe, /* duplicate handle*/
+ GENERIC_READ|GENERIC_WRITE,/* requested access*/
+ FALSE, /* handle inheritance*/
+ DUPLICATE_SAME_ACCESS))) /* optional actions*/
+ {
+ Trace("ERROR:%u:Fail to create the duplicate handle"
+ " to hReadPipe=0x%lx",
+ GetLastError(),
+ hReadPipe);
+ CloseHandle(hReadPipe);
+ CloseHandle(hWritePipe);
+ Fail("");
+ }
+
+ /*Read from the duplicated handle, 256 bytes, more bytes
+ than actually written. This will allow us to use the
+ value that ReadFile returns for comparision.*/
+ bRetVal = ReadFile(hDupPipe, /* handle to read pipe*/
+ buffer, /* buffer to write to*/
+ 256, /* number of bytes to read*/
+ &dwBytesRead, /* number of bytes read*/
+ NULL); /* overlapped buffer*/
+ if (bRetVal == FALSE)
+ {
+ Trace("ERROR:%u:unable read from the duplicated pipe "
+ "hDupPipe=0x%lx\n",
+ GetLastError(),
+ hDupPipe);
+ CloseHandle(hReadPipe);
+ CloseHandle(hWritePipe);
+ CloseHandle(hDupPipe);
+ Fail("");
+ }
+
+ /*Compare what was read with what was written.*/
+ if ((memcmp(cTestString, buffer, dwBytesRead)) != 0)
+ {
+ Trace("ERROR:%u: read \"%s\" expected \"%s\" \n",
+ GetLastError(),
+ buffer,
+ cTestString);
+ CloseHandle(hReadPipe);
+ CloseHandle(hWritePipe);
+ CloseHandle(hDupPipe);
+ Fail("");
+ }
+
+ /*Compare values returned from WriteFile and ReadFile.*/
+ if (dwBytesWritten != dwBytesRead)
+ {
+ Trace("ERROR:%u: WriteFile wrote \"%s\", but ReadFile read \"%s\","
+ " these should be the same\n",
+ GetLastError(),
+ buffer,
+ cTestString);
+ CloseHandle(hReadPipe);
+ CloseHandle(hWritePipe);
+ CloseHandle(hDupPipe);
+ Fail("");
+ }
+
+ /*Cleanup.*/
+ CloseHandle(hWritePipe);
+ CloseHandle(hReadPipe);
+ CloseHandle(hDupPipe);
+
+ PAL_Terminate();
+ return (PASS);
+}
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test5/testinfo.dat b/src/pal/tests/palsuite/threading/DuplicateHandle/test5/testinfo.dat
new file mode 100644
index 0000000000..97e42a9787
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test5/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Threading
+Function = DuplicateHandle
+Name = Test for DuplicateHandle (CreatePipe)
+TYPE = DEFAULT
+EXE1 = test5
+Description
+= Tests the PAL implementation of the DuplicateHandle function,
+= with CreatePipe. This test will create a pipe and write to it,
+= then duplicate the read handle and read what was written.
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test6/CMakeLists.txt b/src/pal/tests/palsuite/threading/DuplicateHandle/test6/CMakeLists.txt
new file mode 100644
index 0000000000..20f7822b1e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test6/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test6.c
+)
+
+add_executable(paltest_duplicatehandle_test6
+ ${SOURCES}
+)
+
+add_dependencies(paltest_duplicatehandle_test6 coreclrpal)
+
+target_link_libraries(paltest_duplicatehandle_test6
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test6/test6.c b/src/pal/tests/palsuite/threading/DuplicateHandle/test6/test6.c
new file mode 100644
index 0000000000..026f315a44
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test6/test6.c
@@ -0,0 +1,146 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test6.c (DuplicateHandle)
+**
+** Purpose: Tests the PAL implementation of the DuplicateHandle function,
+** with CreatePipe. This test will create a pipe, then duplicate
+** the write handle, write to the handle, and use the read to
+** verify.
+**
+** Depends: WriteFile
+** ReadFile
+** memcmp
+** CloseHandle
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+const char* cTestString = "one fish, two fish, red fish, blue fish.";
+
+int __cdecl main(int argc, char **argv)
+{
+ HANDLE hReadPipe = NULL;
+ HANDLE hWritePipe = NULL;
+ HANDLE hDupPipe = NULL;
+ BOOL bRetVal = FALSE;
+ DWORD dwBytesWritten;
+ DWORD dwBytesRead;
+ char buffer[256];
+
+ SECURITY_ATTRIBUTES lpPipeAttributes;
+
+ /*Initialize the PAL*/
+ if ((PAL_Initialize(argc, argv)) != 0)
+ {
+ return (FAIL);
+ }
+
+ /*Setup SECURITY_ATTRIBUTES structure for CreatePipe*/
+ lpPipeAttributes.nLength = sizeof(lpPipeAttributes);
+ lpPipeAttributes.lpSecurityDescriptor = NULL;
+ lpPipeAttributes.bInheritHandle = TRUE;
+
+ /*Create a Pipe*/
+ bRetVal = CreatePipe(&hReadPipe, /* read handle*/
+ &hWritePipe, /* write handle */
+ &lpPipeAttributes,/* security attributes*/
+ 0); /* pipe size*/
+ if (bRetVal == FALSE)
+ {
+ Fail("ERROR: %ld :Unable to create pipe\n", GetLastError());
+ }
+
+ /*Duplicate the pipe handle*/
+ if (!(DuplicateHandle(GetCurrentProcess(), /* source handle process*/
+ hWritePipe, /* handle to duplicate*/
+ GetCurrentProcess(), /* target process handle*/
+ &hDupPipe, /* duplicate handle*/
+ GENERIC_READ|GENERIC_WRITE,/* requested access*/
+ FALSE, /* handle inheritance*/
+ DUPLICATE_SAME_ACCESS))) /* optional actions*/
+ {
+ Trace("ERROR: %ld :Fail to create the duplicate handle"
+ " to hWritePipe=0x%lx",
+ GetLastError(),
+ hWritePipe);
+ CloseHandle(hReadPipe);
+ CloseHandle(hWritePipe);
+ Fail("");
+ }
+
+ /*Write to the duplicate write pipe handle*/
+ bRetVal = WriteFile(hDupPipe, /* handle to write pipe*/
+ cTestString, /* buffer to write*/
+ strlen(cTestString),/* number of bytes to write*/
+ &dwBytesWritten, /* number of bytes written*/
+ NULL); /* overlapped buffer*/
+ if (bRetVal == FALSE)
+ {
+ Trace("ERROR: %ld :unable to write to duplicate write pipe handle "
+ "hDupPipe=0x%lx\n",
+ GetLastError(),
+ hDupPipe);
+ CloseHandle(hReadPipe);
+ CloseHandle(hWritePipe);
+ CloseHandle(hDupPipe);
+ Fail("");
+ }
+
+ /*Read from the read handle, 256 bytes, more bytes
+ then actually written. This will give allow us to use
+ the value that ReadFile returns for comparision.*/
+ bRetVal = ReadFile(hReadPipe, /* handle to read pipe*/
+ buffer, /* buffer to write to*/
+ 256, /* number of bytes to read*/
+ &dwBytesRead, /* number of bytes read*/
+ NULL); /* overlapped buffer*/
+ if (bRetVal == FALSE)
+ {
+ Trace("ERROR: %ld : unable read hReadPipe=0x%lx\n",
+ GetLastError(), hReadPipe);
+ CloseHandle(hReadPipe);
+ CloseHandle(hWritePipe);
+ CloseHandle(hDupPipe);
+ Fail("");
+ }
+
+ /*Compare what was read with what was written.*/
+ if ((memcmp(cTestString, buffer, dwBytesRead)) != 0)
+ {
+ Trace("ERROR: read \"%s\" expected \"%s\" \n",
+ buffer,
+ cTestString);
+ CloseHandle(hReadPipe);
+ CloseHandle(hWritePipe);
+ CloseHandle(hDupPipe);
+ Fail("");
+ }
+
+ /*Compare values returned from WriteFile and ReadFile.*/
+ if (dwBytesWritten != dwBytesRead)
+ {
+ Trace("ERROR: WriteFile wrote \"%s\", but ReadFile read \"%s\","
+ " these should be the same\n",
+ buffer,
+ cTestString);
+ CloseHandle(hReadPipe);
+ CloseHandle(hWritePipe);
+ CloseHandle(hDupPipe);
+ Fail("");
+ }
+
+ /*Cleanup.*/
+ CloseHandle(hReadPipe);
+ CloseHandle(hWritePipe);
+ CloseHandle(hDupPipe);
+
+
+ PAL_Terminate();
+ return (PASS);
+}
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test6/testinfo.dat b/src/pal/tests/palsuite/threading/DuplicateHandle/test6/testinfo.dat
new file mode 100644
index 0000000000..6c49d64f89
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test6/testinfo.dat
@@ -0,0 +1,15 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Threading
+Function = DuplicateHandle
+Name = Test for DuplicateHandle (CreatePipe)
+TYPE = DEFAULT
+EXE1 = test6
+Description
+= Tests the PAL implementation of the DuplicateHandle function,
+= with CreatePipe. This test will create a pipe, then duplicate
+= the write handle, write to the handle, and use the read to
+= verify.
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test7/CMakeLists.txt b/src/pal/tests/palsuite/threading/DuplicateHandle/test7/CMakeLists.txt
new file mode 100644
index 0000000000..df3fdf9ae0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test7/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test7.c
+)
+
+add_executable(paltest_duplicatehandle_test7
+ ${SOURCES}
+)
+
+add_dependencies(paltest_duplicatehandle_test7 coreclrpal)
+
+target_link_libraries(paltest_duplicatehandle_test7
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test7/test7.c b/src/pal/tests/palsuite/threading/DuplicateHandle/test7/test7.c
new file mode 100644
index 0000000000..0477291922
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test7/test7.c
@@ -0,0 +1,149 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test7.c (DuplicateHandle)
+**
+** Purpose: Tests the PAL implementation of the DuplicateHandle function,
+** with a handle from CreateThread. The test will create a thread
+** handle and its duplicate. Then get the priorities of the threads,
+** set the priority of one and the change should be seen in the
+** other.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+DWORD PALAPI CreateTestThread(LPVOID lpParam);
+
+int __cdecl main(int argc, char* argv[])
+{
+ HANDLE hThread;
+ HANDLE hDupThread;
+ DWORD dwThreadId = 0;
+ LPTHREAD_START_ROUTINE lpStartAddress = &CreateTestThread;
+
+ int threadPriority;
+ int duplicatePriority;
+ int finalPriority;
+
+ /* Initialize the PAL.*/
+ if ((PAL_Initialize(argc, argv)) != 0)
+ {
+ return (FAIL);
+ }
+
+ /* Create a thread.*/
+ hThread = CreateThread(NULL, /* SD*/
+ (DWORD)0, /* initial stack size*/
+ lpStartAddress, /* thread function*/
+ NULL, /* thread argument*/
+ (DWORD)0, /* creation option*/
+ &dwThreadId); /* thread identifier*/
+ if (hThread == NULL)
+ {
+ Fail("ERROR:%u: Unable to create thread.\n",
+ GetLastError());
+ }
+
+ /* Duplicate the thread handle.*/
+ if (!(DuplicateHandle(GetCurrentProcess(), /* source handle process*/
+ hThread, /* handle to duplicate*/
+ GetCurrentProcess(), /* target process handle*/
+ &hDupThread, /* duplicate handle*/
+ (DWORD)0, /* requested access*/
+ FALSE, /* handle inheritance*/
+ DUPLICATE_SAME_ACCESS))) /* optional actions*/
+ {
+ Trace("ERROR: %ld :Fail to create the duplicate handle"
+ " to hThread=0x%lx",
+ GetLastError(),
+ hThread);
+ CloseHandle(hThread);
+ Fail("");
+ }
+
+ /* Get the priority of the thread.*/
+ threadPriority = GetThreadPriority(hThread);
+ if(threadPriority != 0)
+ {
+ Trace("ERROR: Thread priority of hThread=0x%lx should be "
+ "set to normal THREAD_PRIORITY_NORMAL=%d\n",
+ hThread,
+ THREAD_PRIORITY_NORMAL);
+ CloseHandle(hThread);
+ CloseHandle(hDupThread);
+ Fail("");
+ }
+
+ /* Get the priority of the duplicated handle, and compare it to
+ * the priority of the original thread. Should be the same.*/
+ duplicatePriority = GetThreadPriority(hThread);
+ if(duplicatePriority != threadPriority)
+ {
+ Trace("ERROR: Expected priority of hThread=0x%lx and hDupThread=0x%lx"
+ " to be the same. Priorities:hThread=\"%d\":hDupThread=\"%d\"\n",
+ hThread,
+ hDupThread,
+ threadPriority,
+ duplicatePriority);
+ CloseHandle(hThread);
+ CloseHandle(hDupThread);
+ Fail("");
+ }
+
+ /* Set the priority of the duplicate thread.*/
+ if(!SetThreadPriority (hDupThread,THREAD_PRIORITY_HIGHEST))
+ {
+ Trace("ERROR:%u: SetThreadPriority failed on hThread=0x%lx\n",
+ GetLastError(),
+ hDupThread);
+ CloseHandle(hThread);
+ CloseHandle(hDupThread);
+ Fail("");
+ }
+
+ /* Get the priority of the origianl thread, and
+ * compare it to what the duplicate was set to.*/
+ finalPriority = GetThreadPriority(hThread);
+ if (finalPriority != THREAD_PRIORITY_HIGHEST)
+ {
+ Trace("ERROR: Expected priority of hThread=0x%lw and "
+ "hDupThread=0x%lw to be set the same. Priorities:"
+ "hThread=\"%d\":hDupThread=\"%d\".\n",
+ hThread,
+ hDupThread,
+ threadPriority,
+ duplicatePriority);
+ CloseHandle(hThread);
+ CloseHandle(hDupThread);
+ Fail("");
+ }
+
+ /* Wait on the original thread.*/
+ if((WaitForSingleObject(hThread, 100)) != WAIT_OBJECT_0)
+ {
+ Trace("ERROR:%u: hThread=0x%lx is in a non-signalled "
+ "mode, yet created signalled.\n",
+ GetLastError(),
+ hThread);
+ CloseHandle(hThread);
+ CloseHandle(hDupThread);
+ Fail("");
+ }
+
+ /* Clean-up thread and Terminate the PAL.*/
+ CloseHandle(hThread);
+ CloseHandle(hDupThread);
+ PAL_Terminate();
+ return PASS;
+}
+
+/*Thread testing function, only return '0'*/
+DWORD PALAPI CreateTestThread(LPVOID lpParam)
+{
+ return (DWORD)0;
+}
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test7/testinfo.dat b/src/pal/tests/palsuite/threading/DuplicateHandle/test7/testinfo.dat
new file mode 100644
index 0000000000..b8092d6152
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test7/testinfo.dat
@@ -0,0 +1,16 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Threading
+Function = DuplicateHandle
+Name = Test for DuplicateHandle (CreateThread)
+TYPE = DEFAULT
+EXE1 = test7
+Description
+= Tests the PAL implementation of the DuplicateHandle function,
+= with a handle from CreateThread. The test will create a thread
+= handle and its duplicate. Then get the priorities of the threads,
+= set the priority of one and the change should be seen in the
+= other.
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test8/CMakeLists.txt b/src/pal/tests/palsuite/threading/DuplicateHandle/test8/CMakeLists.txt
new file mode 100644
index 0000000000..15ec23d272
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test8/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test8.c
+)
+
+add_executable(paltest_duplicatehandle_test8
+ ${SOURCES}
+)
+
+add_dependencies(paltest_duplicatehandle_test8 coreclrpal)
+
+target_link_libraries(paltest_duplicatehandle_test8
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test8/test8.c b/src/pal/tests/palsuite/threading/DuplicateHandle/test8/test8.c
new file mode 100644
index 0000000000..6748c5dffd
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test8/test8.c
@@ -0,0 +1,164 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test8.c (DuplicateHandle)
+**
+** Purpose: Tests the PAL implementation of the DuplicateHandle function,
+** with a handle from GetCurrentThread. The test will create a thread
+** handle, get the current thread and its duplicate. Then get the
+** priorities of the threads, set the priority of one and the change
+** should be seen in the other.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+DWORD PALAPI CreateTestThread(LPVOID lpParam);
+
+int __cdecl main(int argc, char* argv[])
+{
+ HANDLE hThread;
+ HANDLE hCurrentThread;
+ HANDLE hDupThread;
+ DWORD dwThreadId = 0;
+ LPTHREAD_START_ROUTINE lpStartAddress = &CreateTestThread;
+
+ int threadPriority;
+ int duplicatePriority;
+ int finalPriority;
+
+ /* Initialize the PAL.*/
+ if ((PAL_Initialize(argc, argv)) != 0)
+ {
+ return (FAIL);
+ }
+
+#if !HAVE_SCHED_OTHER_ASSIGNABLE
+ /* Defining thread priority for SCHED_OTHER is implementation defined.
+ Some platforms like NetBSD cannot reassign it as they are dynamic.
+ */
+ printf("paltest_duplicatehandle_test8 has been disabled on this platform\n");
+#else
+
+ /* Create a thread.*/
+ hThread = CreateThread(NULL, /* SD*/
+ (DWORD)0, /* initial stack size*/
+ lpStartAddress, /* thread function*/
+ NULL, /* thread argument*/
+ (DWORD)0, /* creation option*/
+ &dwThreadId); /* thread identifier*/
+ if (hThread == NULL)
+ {
+ Fail("ERROR:%u: Unable to create thread.\n",
+ GetLastError());
+ }
+
+ /*Get a psuedo handle to the current thread.*/
+ hCurrentThread = GetCurrentThread();
+
+ /* Duplicate the psuedo thread handle.*/
+ if (!(DuplicateHandle(GetCurrentProcess(), /* source handle process*/
+ hCurrentThread, /* handle to duplicate*/
+ GetCurrentProcess(), /* target process handle*/
+ &hDupThread, /* duplicate handle*/
+ (DWORD)0, /* requested access*/
+ FALSE, /* handle inheritance*/
+ DUPLICATE_SAME_ACCESS))) /* optional actions*/
+ {
+ Trace("ERROR: %ld :Fail to create the duplicate handle"
+ " to hThread=0x%lx",
+ GetLastError(),
+ hThread);
+ CloseHandle(hThread);
+ Fail("");
+ }
+
+ /* Get the priority of the thread.*/
+ threadPriority = GetThreadPriority(hCurrentThread);
+ if(threadPriority != 0)
+ {
+ Trace("ERROR: Thread priority of hCurrentThread=0x%lx should be "
+ "set to normal THREAD_PRIORITY_NORMAL=%d\n",
+ hCurrentThread,
+ THREAD_PRIORITY_NORMAL);
+ CloseHandle(hThread);
+ CloseHandle(hDupThread);
+ Fail("");
+ }
+
+ /* Get the priority of the duplicated handle, and compare it to
+ * the priority of the original thread. Should be the same.*/
+ duplicatePriority = GetThreadPriority(hCurrentThread);
+ if(duplicatePriority != threadPriority)
+ {
+ Trace("ERROR: Expected priority of hCurrentThread=0x%lx and "
+ "hDupThread=0x%lx to be the same. Priorities:hThread="
+ "\"%d\":hDupThread=\"%d\"\n",
+ hCurrentThread,
+ hDupThread,
+ threadPriority,
+ duplicatePriority);
+ CloseHandle(hThread);
+ CloseHandle(hDupThread);
+ Fail("");
+ }
+
+ /* Set the priority of the original thread.*/
+ if(!SetThreadPriority (hCurrentThread,THREAD_PRIORITY_HIGHEST))
+ {
+ Trace("ERROR:%u: SetThreadPriority failed on hCurrentThread=0x%lx\n",
+ GetLastError(),
+ hCurrentThread);
+ CloseHandle(hThread);
+ CloseHandle(hDupThread);
+ Fail("");
+ }
+
+ /* Get the priority of the duplicate thread, and
+ * compare it to what the original was set to.*/
+ finalPriority = GetThreadPriority(hDupThread);
+ if (finalPriority != THREAD_PRIORITY_HIGHEST)
+ {
+ Trace("ERROR: Expected priority of hCurrentThread=0x%lw and "
+ "hDupThread=0x%lw to be set the same. Priorities:"
+ "hCurrentThread=\"%d\":hDupThread=\"%d\".\n",
+ hCurrentThread,
+ hDupThread,
+ threadPriority,
+ duplicatePriority);
+ CloseHandle(hThread);
+ CloseHandle(hDupThread);
+ Fail("");
+ }
+
+ /* Wait on the original thread.*/
+ if((WaitForSingleObject(hThread, 100)) != WAIT_OBJECT_0)
+ {
+ Trace("ERROR:%u: hCurrentThread=0x%lx is in a non-signalled "
+ "mode, yet created signalled.\n",
+ GetLastError(),
+ hThread);
+ CloseHandle(hThread);
+ CloseHandle(hDupThread);
+ Fail("");
+ }
+
+ /* Clean-up thread and Terminate the PAL.*/
+ CloseHandle(hThread);
+ CloseHandle(hDupThread);
+
+#endif
+
+ PAL_Terminate();
+ return PASS;
+}
+
+/*Thread testing function, only return '0'*/
+DWORD PALAPI CreateTestThread(LPVOID lpParam)
+{
+ return (DWORD)0;
+}
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test8/testinfo.dat b/src/pal/tests/palsuite/threading/DuplicateHandle/test8/testinfo.dat
new file mode 100644
index 0000000000..ae1353af18
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test8/testinfo.dat
@@ -0,0 +1,16 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Threading
+Function = DuplicateHandle
+Name = Test for DuplicateHandle (GetCurrentThread)
+TYPE = DEFAULT
+EXE1 = test8
+Description
+=Tests the PAL implementation of the DuplicateHandle function,
+=with a handle from GetCurrentThread. The test will create a thread
+=handle, get the current thread and its duplicate. Then get the
+=priorities of the threads, set the priority of one and the change
+=should be seen in the other.
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test9/CMakeLists.txt b/src/pal/tests/palsuite/threading/DuplicateHandle/test9/CMakeLists.txt
new file mode 100644
index 0000000000..e4442e327c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test9/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test9.c
+)
+
+add_executable(paltest_duplicatehandle_test9
+ ${SOURCES}
+)
+
+add_dependencies(paltest_duplicatehandle_test9 coreclrpal)
+
+target_link_libraries(paltest_duplicatehandle_test9
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test9/test9.c b/src/pal/tests/palsuite/threading/DuplicateHandle/test9/test9.c
new file mode 100644
index 0000000000..f15871c57d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test9/test9.c
@@ -0,0 +1,127 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test9.c (DuplicateHandle)
+**
+** Purpose: Tests the PAL implementation of the DuplicateHandle function,
+** with a handle from GetCurrentProcess. The test will create a
+** process, duplicate it, then using ReadProcessMemory will
+** read from the memory location of the CreateProcess process
+** memory and the DuplicateHandle process memory. If the
+** duplication is correct the memory will be the same for both.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+int __cdecl main(int argc, char* argv[])
+{
+ HANDLE hProcess;
+ HANDLE hDupProcess;
+ char lpBuffer[64];
+ char lpDupBuffer[64];
+ SIZE_T lpNumberOfBytesRead;
+ SIZE_T lpDupNumberOfBytesRead;
+ char lpTestBuffer[] = "abcdefghijklmnopqrstuvwxyz";
+
+ /* Initalize the PAL.
+ */
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /* Initalize the buffers.
+ */
+ ZeroMemory( &lpBuffer, sizeof(lpBuffer) );
+ ZeroMemory( &lpDupBuffer, sizeof(lpDupBuffer) );
+
+ /* Get current proces, this will be duplicated.
+ */
+ hProcess = GetCurrentProcess();
+ if(hProcess == NULL)
+ {
+ Fail("ERROR: Unable to get the current process\n");
+ }
+
+ /* Duplicate the current process handle.
+ */
+ if (!(DuplicateHandle(GetCurrentProcess(), /* source handle process*/
+ hProcess, /* handle to duplicate*/
+ GetCurrentProcess(), /* target process handle*/
+ &hDupProcess, /* duplicate handle*/
+ (DWORD)0, /* requested access*/
+ FALSE, /* handle inheritance*/
+ DUPLICATE_SAME_ACCESS))) /* optional actions*/
+ {
+ Trace("ERROR:%u: Failed to create the duplicate handle"
+ " to hProcess=0x%lx",
+ GetLastError(),
+ hProcess);
+ CloseHandle(hProcess);
+ Fail("");
+ }
+
+ /* Get memory read of the current process.
+ */
+ if ((ReadProcessMemory(hDupProcess, &lpTestBuffer,
+ lpDupBuffer, sizeof(lpDupBuffer), &lpDupNumberOfBytesRead)) == 0)
+ {
+ Trace("ERROR:%u: Unable to read the process memory of "
+ "hDupProcess=0x%lx.\n",
+ GetLastError(),
+ hDupProcess);
+ CloseHandle(hProcess);
+ CloseHandle(hDupProcess);
+ Fail("");
+ }
+
+ /* Get read memory of the created process.
+ */
+ if ((ReadProcessMemory(hProcess, &lpTestBuffer,
+ lpBuffer, sizeof(lpBuffer), &lpNumberOfBytesRead)) == 0)
+ {
+ Trace("ERROR:%u: Unable to read the process memory of "
+ "hProcess=0x%lx.\n",
+ GetLastError(),
+ hProcess);
+ CloseHandle(hProcess);
+ CloseHandle(hDupProcess);
+ Fail("");
+ }
+
+ /* Compare the number of bytes that were read by each
+ * ReadProcessMemory.*/
+ if (lpDupNumberOfBytesRead != lpNumberOfBytesRead)
+ {
+ Trace("ERROR: ReadProcessMemory read different numbers of bytes "
+ "from duplicate process handles.\n");
+ CloseHandle(hProcess);
+ CloseHandle(hDupProcess);
+ Fail("");
+ }
+
+ /* Compare the two buffers to make sure they are equal.
+ */
+ if ((strcmp(lpBuffer, lpDupBuffer)) != 0)
+ {
+ Trace("ERROR: ReadProcessMemory read different numbers of bytes "
+ "from duplicate process handles. hProcess read \"%s\" and "
+ "hDupProcess read \"%s\"\n",
+ lpBuffer,
+ lpDupBuffer);
+ CloseHandle(hProcess);
+ CloseHandle(hDupProcess);
+ Fail("");
+ }
+
+ /* Clean-up thread and Terminate the PAL.*/
+ CloseHandle(hProcess);
+ CloseHandle(hDupProcess);
+ PAL_Terminate();
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/DuplicateHandle/test9/testinfo.dat b/src/pal/tests/palsuite/threading/DuplicateHandle/test9/testinfo.dat
new file mode 100644
index 0000000000..c7122908fd
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test9/testinfo.dat
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Threading
+Function = DuplicateHandle
+Name = Test for DuplicateHandle (GetCurrentProcess)
+TYPE = DEFAULT
+EXE1 = test9
+Description
+= Tests the PAL implementation of the DuplicateHandle function,
+= with a handle from GetCurrentProcess. The test will create a
+= process, duplicate it, then using ReadProcessMemory will
+= read from the memory location of the CreateProcess process
+= memory and the DuplicateHandle process memory. If the
+= duplication is correct the memory will be the same for both.
diff --git a/src/pal/tests/palsuite/threading/ExitProcess/CMakeLists.txt b/src/pal/tests/palsuite/threading/ExitProcess/CMakeLists.txt
new file mode 100644
index 0000000000..1962ade358
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+
diff --git a/src/pal/tests/palsuite/threading/ExitProcess/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/ExitProcess/test1/CMakeLists.txt
new file mode 100644
index 0000000000..decddb8512
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ ExitProcess.c
+)
+
+add_executable(paltest_exitprocess_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_exitprocess_test1 coreclrpal)
+
+target_link_libraries(paltest_exitprocess_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/ExitProcess/test1/ExitProcess.c b/src/pal/tests/palsuite/threading/ExitProcess/test1/ExitProcess.c
new file mode 100644
index 0000000000..2b089a0b83
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test1/ExitProcess.c
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: exitprocess/test1/exitprocess.c
+**
+** Purpose: Test to ensure ExitProcess returns the argument given
+** to it.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+int __cdecl main( int argc, char **argv )
+
+{
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ ExitProcess(PASS);
+
+ Fail ("ExitProcess(0) failed to exit.\n Test Failed.\n");
+
+ return ( FAIL);
+
+}
diff --git a/src/pal/tests/palsuite/threading/ExitProcess/test1/testinfo.dat b/src/pal/tests/palsuite/threading/ExitProcess/test1/testinfo.dat
new file mode 100644
index 0000000000..d8b85abad6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test1/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ExitProcess
+Name = Positive Test for ExitProcess
+TYPE = DEFAULT
+EXE1 = exitprocess
+Description
+= Test the ExitProcess function. The test runs the ExitProcess function
+= with the TEST_RETURN enumeration value PASS
diff --git a/src/pal/tests/palsuite/threading/ExitProcess/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/ExitProcess/test2/CMakeLists.txt
new file mode 100644
index 0000000000..881c7d0eee
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_exitprocess_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_exitprocess_test2 coreclrpal)
+
+target_link_libraries(paltest_exitprocess_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/ExitProcess/test2/test2.c b/src/pal/tests/palsuite/threading/ExitProcess/test2/test2.c
new file mode 100644
index 0000000000..8023ad7bab
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test2/test2.c
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test2.c
+**
+** Purpose: Positive test for ExitProcess.
+**
+** Dependencies: none
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* call ExitProcess() -- should work without PAL_Initialize() */
+ ExitProcess(PASS);
+
+
+ /* return failure if we reach here -- note no attempt at */
+ /* meaningful output because we never called PAL_Initialize(). */
+ return FAIL;
+}
diff --git a/src/pal/tests/palsuite/threading/ExitProcess/test2/testinfo.dat b/src/pal/tests/palsuite/threading/ExitProcess/test2/testinfo.dat
new file mode 100644
index 0000000000..0aa07eb15a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test2/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ExitProcess
+Name = Positive test for ExitProcess
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Test to ensure proper operation of the ExitProcess()
+= API by ensuring it works before PAL_Initialize() is
+= called.
diff --git a/src/pal/tests/palsuite/threading/ExitProcess/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/ExitProcess/test3/CMakeLists.txt
new file mode 100644
index 0000000000..638408e986
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test3.c
+)
+
+add_executable(paltest_exitprocess_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_exitprocess_test3 coreclrpal)
+
+target_link_libraries(paltest_exitprocess_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/ExitProcess/test3/test3.c b/src/pal/tests/palsuite/threading/ExitProcess/test3/test3.c
new file mode 100644
index 0000000000..aea485e5e3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test3/test3.c
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test3.c
+**
+** Purpose: Positive test for ExitProcess.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* initialize the PAL */
+ if( PAL_Initialize(argc, argv) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* call ExitProcess() -- should work after PAL_Terminate() */
+ ExitProcess( PASS );
+
+
+ /* return failure if we reach here -- note no attempt at */
+ /* meaningful output because we've called PAL_Terminte(). */
+ return FAIL;
+}
diff --git a/src/pal/tests/palsuite/threading/ExitProcess/test3/testinfo.dat b/src/pal/tests/palsuite/threading/ExitProcess/test3/testinfo.dat
new file mode 100644
index 0000000000..c857d885cc
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test3/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ExitProcess
+Name = Positive test for ExitProcess
+TYPE = DEFAULT
+EXE1 = test3
+Description
+= Test to ensure proper operation of the ExitProcess()
+= API by ensuring it works after PAL_Terminate() is
+= called.
diff --git a/src/pal/tests/palsuite/threading/ExitThread/CMakeLists.txt b/src/pal/tests/palsuite/threading/ExitThread/CMakeLists.txt
new file mode 100644
index 0000000000..ef14ea5352
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+
diff --git a/src/pal/tests/palsuite/threading/ExitThread/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/ExitThread/test1/CMakeLists.txt
new file mode 100644
index 0000000000..15e0c1b62d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_exitthread_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_exitthread_test1 coreclrpal)
+
+target_link_libraries(paltest_exitthread_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/ExitThread/test1/test1.c b/src/pal/tests/palsuite/threading/ExitThread/test1/test1.c
new file mode 100644
index 0000000000..2963745bef
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test1/test1.c
@@ -0,0 +1,114 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test1.c
+**
+** Purpose: Test for ExitThread. Create a thread and then call
+** exit thread within the threading function. Ensure that it exits
+** immediatly.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+DWORD dwExitThreadTestParameter = 0;
+
+DWORD PALAPI ExitThreadTestThread( LPVOID lpParameter)
+{
+ DWORD dwRet = 0;
+
+ /* Save parameter for test */
+ dwExitThreadTestParameter = (DWORD)lpParameter;
+
+ /* Call the ExitThread function */
+ ExitThread(dwRet);
+
+ /* If we didn't exit, get caught in this loop. But, the
+ program will exit.
+ */
+ while (!dwRet)
+ {
+ Fail("ERROR: Entered an infinite loop because ExitThread "
+ "failed to exit from the thread. Forcing exit from "
+ "the test now.");
+ }
+
+ return dwRet;
+}
+
+BOOL ExitThreadTest()
+{
+ BOOL bRet = FALSE;
+ DWORD dwRet = 0;
+
+ LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL;
+ DWORD dwStackSize = 0;
+ LPTHREAD_START_ROUTINE lpStartAddress = &ExitThreadTestThread;
+ LPVOID lpParameter = lpStartAddress;
+ DWORD dwCreationFlags = 0; //run immediately
+ DWORD dwThreadId = 0;
+
+ HANDLE hThread = 0;
+
+ dwExitThreadTestParameter = 0;
+
+ /* Create a Thread. We'll need this to test that we're able
+ to exit the thread.
+ */
+ hThread = CreateThread( lpThreadAttributes,
+ dwStackSize, lpStartAddress, lpParameter,
+ dwCreationFlags, &dwThreadId );
+
+ if (hThread != INVALID_HANDLE_VALUE)
+ {
+ dwRet = WaitForSingleObject(hThread,INFINITE);
+
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Trace("ExitThreadTest:WaitForSingleObject failed "
+ "(%x)\n",GetLastError());
+ }
+ else
+ {
+ /* Check to ensure that the parameter set in the Thread
+ function is correct.
+ */
+ if (dwExitThreadTestParameter != (DWORD)lpParameter)
+ {
+ Trace("ERROR: The paramater passed should have been "
+ "%d but turned up as %d.",
+ dwExitThreadTestParameter, lpParameter);
+ }
+ else
+ {
+ bRet = TRUE;
+ }
+ }
+ }
+ else
+ {
+ Trace("ExitThreadTest:CreateThread failed (%x)\n",GetLastError());
+ }
+
+ return bRet;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!ExitThreadTest())
+ {
+ Fail ("Test failed\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+}
diff --git a/src/pal/tests/palsuite/threading/ExitThread/test1/testinfo.dat b/src/pal/tests/palsuite/threading/ExitThread/test1/testinfo.dat
new file mode 100644
index 0000000000..a526f8e127
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test1/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ExitThread
+Name = Positive Test for ExitThread
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Test for ExitThread. Create a thread and then call
+= exit thread within the threading function. Ensure that it exits
+= immediatly.
diff --git a/src/pal/tests/palsuite/threading/ExitThread/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/ExitThread/test2/CMakeLists.txt
new file mode 100644
index 0000000000..aa05382feb
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test2/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ test2.c
+)
+
+add_executable(paltest_exitthread_test2
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_exitthread_test2 coreclrpal)
+
+target_link_libraries(paltest_exitthread_test2
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ childprocess.c
+)
+
+add_executable(paltest_exitthread_test2_child
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_exitthread_test2_child coreclrpal)
+
+target_link_libraries(paltest_exitthread_test2_child
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/ExitThread/test2/childprocess.c b/src/pal/tests/palsuite/threading/ExitThread/test2/childprocess.c
new file mode 100644
index 0000000000..7fbe208f91
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test2/childprocess.c
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: childprocess.c
+**
+** Purpose: Test to ensure ExitThread returns the right
+** value when shutting down the last thread of a process.
+** All this program does is call ExitThread() with a predefined
+** value.
+**
+** Dependencies: none
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+#include "myexitcode.h"
+
+int __cdecl main( int argc, char **argv )
+{
+ /* initialize the PAL */
+ if( PAL_Initialize(argc, argv) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* exit the current thread with a magic test value -- it should */
+ /* terminate the process and return that test value from this */
+ /* program. */
+ ExitThread( TEST_EXIT_CODE );
+
+ /* technically we should never get here */
+ PAL_Terminate();
+
+ /* return failure */
+ return FAIL;
+}
diff --git a/src/pal/tests/palsuite/threading/ExitThread/test2/myexitcode.h b/src/pal/tests/palsuite/threading/ExitThread/test2/myexitcode.h
new file mode 100644
index 0000000000..566becb9a0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test2/myexitcode.h
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================================
+**
+** Source: myexitcode.h
+**
+** Purpose: Define an exit code.
+**
+**
+**==========================================================================*/
+
+#define TEST_EXIT_CODE 316
diff --git a/src/pal/tests/palsuite/threading/ExitThread/test2/test2.c b/src/pal/tests/palsuite/threading/ExitThread/test2/test2.c
new file mode 100644
index 0000000000..c31af8a079
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test2/test2.c
@@ -0,0 +1,168 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test2.c
+**
+** Purpose: Test to ensure ExitThread() called from the last thread of
+** a process shuts down that process and returns the proper
+** exit code as specified in the ExitThread() call.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** Fail
+** ZeroMemory
+** GetCurrentDirectoryW
+** CreateProcessW
+** WaitForSingleObject
+** GetLastError
+** strlen
+** strncpy
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+#include "myexitcode.h"
+
+
+static const char* rgchPathDelim = "\\";
+
+
+int
+mkAbsoluteFilename( LPSTR dirName,
+ DWORD dwDirLength,
+ LPCSTR fileName,
+ DWORD dwFileLength,
+ LPSTR absPathName )
+{
+ DWORD sizeDN, sizeFN, sizeAPN;
+
+ sizeDN = strlen( dirName );
+ sizeFN = strlen( fileName );
+ sizeAPN = (sizeDN + 1 + sizeFN + 1);
+
+ /* ensure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */
+ if( sizeAPN > _MAX_PATH )
+ {
+ return ( 0 );
+ }
+
+ strncpy( absPathName, dirName, dwDirLength +1 );
+ strncpy( absPathName, rgchPathDelim, 2 );
+ strncpy( absPathName, fileName, dwFileLength +1 );
+
+ return (sizeAPN);
+
+}
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ const char* rgchChildFile = "childprocess";
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ DWORD dwError;
+ DWORD dwExitCode;
+ DWORD dwFileLength;
+ DWORD dwDirLength;
+ DWORD dwSize;
+ DWORD dwExpected = TEST_EXIT_CODE;
+
+ char rgchDirName[_MAX_DIR];
+ char absPathBuf[_MAX_PATH];
+ char* rgchAbsPathName;
+
+ /* initialize the PAL */
+ if( PAL_Initialize(argc, argv) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* zero our process and startup info structures */
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof( si );
+ ZeroMemory( &pi, sizeof(pi) );
+
+ /* build the absolute path to the child process */
+ rgchAbsPathName = &absPathBuf[0];
+ dwFileLength = strlen( rgchChildFile );
+
+ dwDirLength = GetCurrentDirectory( _MAX_PATH, rgchDirName );
+ if( dwDirLength == 0 )
+ {
+ dwError = GetLastError();
+ Fail( "GetCurrentDirectory call failed with error code %d\n",
+ dwError );
+ }
+
+ dwSize = mkAbsoluteFilename( rgchDirName,
+ dwDirLength,
+ rgchChildFile,
+ dwFileLength,
+ rgchAbsPathName );
+ if( dwSize == 0 )
+ {
+ Fail( "Palsuite Code: mkAbsoluteFilename() call failed. Could ",
+ "not build absolute path name to file\n. Exiting.\n" );
+ }
+
+ /* launch the child process */
+ if( !CreateProcess( NULL, /* module name to execute */
+ rgchAbsPathName, /* command line */
+ NULL, /* process handle not */
+ /* inheritable */
+ NULL, /* thread handle not */
+ /* inheritable */
+ FALSE, /* handle inheritance */
+ CREATE_NEW_CONSOLE, /* dwCreationFlags */
+ NULL, /* use parent's environment */
+ NULL, /* use parent's starting */
+ /* directory */
+ &si, /* startup info struct */
+ &pi ) /* process info struct */
+ )
+ {
+ dwError = GetLastError();
+ Fail( "CreateProcess call failed with error code %d\n",
+ dwError );
+ }
+
+ /* wait for the child process to complete */
+ WaitForSingleObject ( pi.hProcess, INFINITE );
+
+ /* check the exit code from the process */
+ if( ! GetExitCodeProcess( pi.hProcess, &dwExitCode ) )
+ {
+ dwError = GetLastError();
+ CloseHandle ( pi.hProcess );
+ CloseHandle ( pi.hThread );
+ Fail( "GetExitCodeProcess call failed with error code %d\n",
+ dwError );
+ }
+
+ /* close process and thread handle */
+ CloseHandle ( pi.hProcess );
+ CloseHandle ( pi.hThread );
+
+ /* check for the expected exit code */
+ /* exit code for some systems is as small as a char, so that's all */
+ /* we'll compare for checking success */
+ if( LOBYTE(LOWORD(dwExitCode)) != LOBYTE(LOWORD(dwExpected)) )
+ {
+ Fail( "GetExitCodeProcess returned an incorrect exit code %d, "
+ "expected value is %d\n",
+ LOWORD(dwExitCode), dwExpected );
+ }
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/ExitThread/test2/testinfo.dat b/src/pal/tests/palsuite/threading/ExitThread/test2/testinfo.dat
new file mode 100644
index 0000000000..4b5bdc2ac6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test2/testinfo.dat
@@ -0,0 +1,19 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ExitThread
+Name = Test for ExitThread
+TYPE = DEFAULT
+EXE1 = test2
+EXE2 = childprocess
+Description
+= Test to ensure proper operation of the ExitThread
+= API. This test launches a simple child process that exits
+= by calling ExitThread() with a known value, and checks
+= that the correct value is returned to the parent process.
+= This verifies that when the last thread of a process exits
+= via ExitThread, the process exits with the proper return
+= code.
diff --git a/src/pal/tests/palsuite/threading/ExitThread/test3/dllmain.c b/src/pal/tests/palsuite/threading/ExitThread/test3/dllmain.c
new file mode 100644
index 0000000000..923650d1d3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test3/dllmain.c
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: dllmain.c
+**
+** Purpose: Test to ensure DllMain() is called with THREAD_DETACH
+** when a thread in the application calls ExitThread().
+**
+** Dependencies: none
+**
+
+**
+**===========================================================================*/
+
+#include <palsuite.h>
+
+/* count of the number of times DllMain() was called with THREAD_DETACH */
+static int g_detachCount = 0;
+
+
+/* standard DllMain() */
+BOOL PALAPI DllMain(HINSTANCE hinstDLL, DWORD reason, LPVOID lpvReserved)
+{
+ switch( reason )
+ {
+ case DLL_PROCESS_ATTACH:
+ {
+ break;
+ }
+
+ case DLL_PROCESS_DETACH:
+ {
+ break;
+ }
+
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* increment g_detachCount */
+ g_detachCount++;
+ break;
+ }
+ return TRUE;
+}
+
+#ifdef WIN32
+BOOL __stdcall _DllMainCRTStartup(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ return DllMain(hinstDLL, fdwReason, lpvReserved);
+}
+#endif
+
+
+/* function to return the current detach count */
+#ifdef WIN32
+__declspec(dllexport)
+#endif
+int PALAPI GetDetachCount( void )
+{
+ return g_detachCount;
+}
diff --git a/src/pal/tests/palsuite/threading/ExitThread/test3/test3.c b/src/pal/tests/palsuite/threading/ExitThread/test3/test3.c
new file mode 100644
index 0000000000..8a71c7cc99
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test3/test3.c
@@ -0,0 +1,162 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test3.c
+**
+** Purpose: Test to ensure ExitThread() results in any loaded dynamic
+** libraries having their entry point called with a THREAD_DETACH
+** notification.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** Fail
+** GetCurrentDirectoryW
+** CreateThread
+** ResumeThread
+** LoadLibrary
+** FreeLibrary
+** GetProcAddress
+** WaitForSingleObject
+** GetLastError
+** strlen
+** strncpy
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+/* SHLEXT is defined only for Unix variants */
+
+#if defined(SHLEXT)
+#define rgchLibraryFile "dllmain"SHLEXT
+#define szFunction "GetDetachCount"
+#else
+#define rgchLibraryFile "dllmain"
+#define szFunction "_GetDetachCount@0"
+#endif
+
+/* define our test function type */
+typedef int ( PALAPI *LPTESTFUNC )( void );
+
+
+/**
+ * ThreadFunc
+ *
+ * Dummy thread function for causing DLL thread notifications.
+ */
+DWORD PALAPI ThreadFunc( LPVOID param )
+{
+ /* simulate some brief "work" */
+ int i;
+ for( i=0; i<100000; i++ )
+ ;
+
+ ExitThread( 0 );
+ return (0);
+}
+
+
+/* main program entry point */
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+
+ HANDLE hLib = NULL;
+ LPTESTFUNC pFunc;
+ int detachCount1 = 0;
+ int detachCount2 = 0;
+
+ HANDLE hThread = NULL;
+ DWORD IDThread;
+
+ /* initialize the PAL */
+ if( PAL_Initialize(argc, argv) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* Load the test library */
+ hLib = LoadLibrary( rgchLibraryFile );
+ if(hLib == NULL)
+ {
+ Fail("ERROR: Unable to load library %s\n", rgchLibraryFile );
+ }
+
+
+ /* Get the address of our test function in the dll */
+ pFunc = (LPTESTFUNC)GetProcAddress( hLib, szFunction );
+ if( pFunc == NULL )
+ {
+ Trace( "ERROR:%lu%:Unable to load function \"%s\" library \"%s\"\n",
+ GetLastError(),
+ szFunction,
+ rgchLibraryFile );
+ if( ! FreeLibrary( hLib ) ) {
+ Trace( "FreeLibrary() failed with error code %lu\n",
+ GetLastError() );
+ }
+ Fail( "Exiting\n" );
+ }
+
+ /* Execute the test function to get the detach count */
+ detachCount1 = pFunc();
+
+ /* run another dummy thread to cause notification of the library */
+ hThread = CreateThread( NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE) ThreadFunc, /* thread function */
+ (LPVOID) NULL, /* pass thread index as */
+ /* function argument */
+ CREATE_SUSPENDED, /* create suspended */
+ &IDThread ); /* returns thread id */
+
+ /* Check the return value for success. */
+ if( hThread == NULL )
+ {
+ /* error creating thread */
+ Trace( "Unexpected CreateThread error %d\n",
+ GetLastError() );
+ if( ! FreeLibrary( hLib ) ) {
+ Trace( "FreeLibrary() failed with error code %lu\n",
+ GetLastError() );
+ }
+ Fail( "Exiting\n" );
+ }
+
+ /* Resume the suspended thread */
+ ResumeThread( hThread );
+
+ /* wait for the thread to complete */
+ WaitForSingleObject( hThread, INFINITE );
+
+ /* Execute the test function to get the new detach count */
+ detachCount2 = pFunc();
+
+ /* Unload the test library */
+ if( !FreeLibrary( hLib ) )
+ {
+ Fail( "ERROR:%u: Unable to free library \"%s\"\n",
+ GetLastError(),
+ rgchLibraryFile );
+ }
+
+ /* validate the result */
+ if( detachCount2 != (detachCount1 + 1) )
+ {
+ Fail( "FAIL: unexpected DLL detach count %d, expected %d\n",
+ detachCount2,
+ (detachCount1 + 1) );
+ }
+
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/ExitThread/test3/testinfo.dat b/src/pal/tests/palsuite/threading/ExitThread/test3/testinfo.dat
new file mode 100644
index 0000000000..1c9e8c7567
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test3/testinfo.dat
@@ -0,0 +1,16 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ExitThread
+Name = Test for ExitThread
+TYPE = DEFAULT
+EXE1 = test3
+LIB1 = dllmain
+Description
+= Test to ensure proper operation of the ExitThread
+= API. This tests to make sure ExitThread() results
+= in any loaded dynamic libraries having their entry
+= point called with a THREAD_DETACH notification.
diff --git a/src/pal/tests/palsuite/threading/GetCurrentProcess/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetCurrentProcess/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcess/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/GetCurrentProcess/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetCurrentProcess/test1/CMakeLists.txt
new file mode 100644
index 0000000000..44105348be
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcess/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ process.c
+)
+
+add_executable(paltest_getcurrentprocess_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_getcurrentprocess_test1 coreclrpal)
+
+target_link_libraries(paltest_getcurrentprocess_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/GetCurrentProcess/test1/process.c b/src/pal/tests/palsuite/threading/GetCurrentProcess/test1/process.c
new file mode 100644
index 0000000000..17d9af6282
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcess/test1/process.c
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: GetCurrentProcess/test1/process.c
+**
+** Purpose: Test for to see if the process GetCurrentProcess
+** returns a handle to the current process or not.
+**
+** Dependencies: TerminateProcess
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+INT __cdecl main( int argc, char **argv )
+{
+
+ HANDLE hProcess;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ hProcess = GetCurrentProcess();
+ Trace ("Testing the handle returned by GetCurrentProcess\n");
+ if ( 0 == ( TerminateProcess ( hProcess, PASS ) ) )
+ {
+ Fail ("Testing GetCurrentProcess, the TerminateProcess function "
+ "failed.\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/GetCurrentProcess/test1/testinfo.dat b/src/pal/tests/palsuite/threading/GetCurrentProcess/test1/testinfo.dat
new file mode 100644
index 0000000000..8eb2759fb9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcess/test1/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = GetCurrentProcess
+Name = Positive Test for GetCurrentProcess
+TYPE = DEFAULT
+EXE1 = process
+Description
+= Test to see if the function GetCurrentProcess returns a handle to the
+= current process or not.
diff --git a/src/pal/tests/palsuite/threading/GetCurrentProcessId/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetCurrentProcessId/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcessId/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/CMakeLists.txt
new file mode 100644
index 0000000000..e5c31e6af2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ processId.c
+)
+
+add_executable(paltest_getcurrentprocessid_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_getcurrentprocessid_test1 coreclrpal)
+
+target_link_libraries(paltest_getcurrentprocessid_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/processId.c b/src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/processId.c
new file mode 100644
index 0000000000..cc689b3f8b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/processId.c
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: getcurrentprocessid/test1/processid.c
+**
+** Purpose: Test to ensure GetCurrentProcessId returns the current
+** process id number. This test compares the result of
+** GetCurrentProcessId to getpid.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+INT __cdecl main( int argc, char **argv )
+{
+
+ DWORD dwProcessId;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ dwProcessId = GetCurrentProcessId();
+
+ if ( 0 >= dwProcessId )
+ {
+ Fail ("%s has dwProcessId has id value %d\n", argv[0],
+ dwProcessId );
+ }
+ Trace ("%s has dwProcessId %d\nPassing test as dwProcessId is > 0\n"
+ , argv[0], dwProcessId);
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/testinfo.dat b/src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/testinfo.dat
new file mode 100644
index 0000000000..db615c0bf9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = GetCurrentProcessId
+Name = Positive Test for GetCurrentProcessId
+TYPE = DEFAULT
+EXE1 = processid
+Description
+= Test to ensure GetCurrentProcessId returns the current process id number.
+= This test compares the result of GetCurrentProcessId to getpid.
diff --git a/src/pal/tests/palsuite/threading/GetCurrentThread/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetCurrentThread/CMakeLists.txt
new file mode 100644
index 0000000000..ef14ea5352
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThread/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+
diff --git a/src/pal/tests/palsuite/threading/GetCurrentThread/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetCurrentThread/test1/CMakeLists.txt
new file mode 100644
index 0000000000..84cbccbca0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThread/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ thread.c
+)
+
+add_executable(paltest_getcurrentthread_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_getcurrentthread_test1 coreclrpal)
+
+target_link_libraries(paltest_getcurrentthread_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/GetCurrentThread/test1/testinfo.dat b/src/pal/tests/palsuite/threading/GetCurrentThread/test1/testinfo.dat
new file mode 100644
index 0000000000..29c9767ed0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThread/test1/testinfo.dat
@@ -0,0 +1,12 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = GetCurrentThread
+Name = Positive Test for GetCurrentThread
+TYPE = DEFAULT
+EXE1 = thread
+Description
+= Test to ensure GetCurrentThread returns a handle to the current thread.
diff --git a/src/pal/tests/palsuite/threading/GetCurrentThread/test1/thread.c b/src/pal/tests/palsuite/threading/GetCurrentThread/test1/thread.c
new file mode 100644
index 0000000000..b2bb97fd67
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThread/test1/thread.c
@@ -0,0 +1,93 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: GetCurrentThread/test1/thread.c
+**
+** Purpose: Test to ensure GetCurrentThread returns a handle to
+** the current thread.
+**
+** Dependencies: GetThreadPriority
+** SetThreadPriority
+** Fail
+** Trace
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+int __cdecl main( int argc, char **argv )
+{
+
+ HANDLE hThread;
+ int nPriority;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+#if !HAVE_SCHED_OTHER_ASSIGNABLE
+ /* Defining thread priority for SCHED_OTHER is implementation defined.
+ Some platforms like NetBSD cannot reassign it as they are dynamic.
+ */
+ printf("paltest_getcurrentthread_test1 has been disabled on this platform\n");
+#else
+ hThread = GetCurrentThread();
+
+ nPriority = GetThreadPriority(hThread);
+
+ if ( THREAD_PRIORITY_NORMAL != nPriority )
+ {
+ if ( THREAD_PRIORITY_ERROR_RETURN == nPriority )
+ {
+ Fail ("GetThreadPriority function call failed for %s\n"
+ "GetLastError returned %d\n", argv[0], GetLastError());
+ }
+ else
+ {
+ Fail ("GetThreadPriority function call failed for %s\n"
+ "The priority returned was %d\n", argv[0], nPriority);
+ }
+ }
+ else
+ {
+ nPriority = 0;
+
+ if (0 == SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST))
+ {
+ Fail ("Unable to set thread priority. Either handle doesn't"
+ " point to current thread \nor SetThreadPriority "
+ "function failed. Failing test.\n");
+ }
+
+ nPriority = GetThreadPriority(hThread);
+
+ if ( THREAD_PRIORITY_ERROR_RETURN == nPriority )
+ {
+ Fail ("GetThreadPriority function call failed for %s\n"
+ "GetLastError returned %d\n", argv[0], GetLastError());
+ }
+ else if ( THREAD_PRIORITY_HIGHEST == nPriority )
+ {
+ Trace ("GetCurrentThread returns handle to the current "
+ "thread.\n");
+ exit ( PASS );
+ }
+ else
+ {
+ Fail ("Unable to set thread priority. Either handle doesn't"
+ " point to current thread \nor SetThreadPriority "
+ "function failed. Failing test.\n");
+ }
+ }
+#endif
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/GetCurrentThread/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetCurrentThread/test2/CMakeLists.txt
new file mode 100644
index 0000000000..3fc3c3e7c6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThread/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_getcurrentthread_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_getcurrentthread_test2 coreclrpal)
+
+target_link_libraries(paltest_getcurrentthread_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/GetCurrentThread/test2/test2.c b/src/pal/tests/palsuite/threading/GetCurrentThread/test2/test2.c
new file mode 100644
index 0000000000..beeb5ec241
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThread/test2/test2.c
@@ -0,0 +1,144 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test2.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** Fail
+** CreateThread
+** SetThreadPriority
+** GetThreadPriority
+** ResumeThread
+** WaitForSingleObject
+** GetLastError
+**
+** Purpose:
+**
+** Test to ensure proper operation of the GetCurrentThread()
+** API. The test launches a thread in suspended mode, and sets
+** its priority to a non-default value using the handle returned
+** by CreateThread(). The new thread calls GetCurrentThred() to
+** retrieve a handle to itself, and calls GetThreadPriority()
+** to verify that its priority matches what it was set to on
+** the main execution thread.
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+/* we store the return code from the child thread here because */
+/* we're missing the GetExitCodeThread() API */
+
+static int g_priority = 0;
+
+/**
+ * ThreadFunc
+ *
+ * Thread function that calls GetCurrentThread() to get a pseudo-handle
+ * to itself, then checks its priority and exits with that value.
+ */
+DWORD PALAPI ThreadFunc( LPVOID param )
+{
+ int priority;
+ HANDLE hThread;
+
+ /* call GetCurrentThread() to get a pseudo-handle to */
+ /* the current thread */
+ hThread = GetCurrentThread();
+ if( hThread == NULL )
+ {
+ Fail( "GetCurrentThread() call failed\n" );
+ }
+
+
+ /* get the current thread priority */
+ priority = GetThreadPriority( hThread );
+ if( priority == THREAD_PRIORITY_ERROR_RETURN )
+ {
+ /* GetThreadPriority call failed */
+ Fail( "ERROR:%lu:GetThreadPriority() call failed\n", GetLastError() );
+ }
+
+ /* store this globally because we don't have GetExitCodeThread() */
+ g_priority = priority;
+ return (DWORD)priority;
+}
+
+
+/**
+ * main
+ *
+ * executable entry point
+ */
+INT __cdecl main( INT argc, CHAR **argv )
+{
+ HANDLE hThread = NULL;
+ DWORD IDThread;
+ DWORD dwRet;
+
+ SIZE_T i = 0;
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+#if !HAVE_SCHED_OTHER_ASSIGNABLE
+ /* Defining thread priority for SCHED_OTHER is implementation defined.
+ Some platforms like NetBSD cannot reassign it as they are dynamic.
+ */
+ printf("paltest_getcurrentthread_test2 has been disabled on this platform\n");
+#else
+ /* Create multiple threads. */
+ hThread = CreateThread( NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE) ThreadFunc, /* thread function */
+ (LPVOID) i, /* pass thread index as */
+ /* function argument */
+ CREATE_SUSPENDED, /* create suspended */
+ &IDThread ); /* returns thread identifier */
+
+ /* Check the return value for success. */
+ if( hThread == NULL )
+ {
+ /* ERROR */
+ Fail( "ERROR:%lu:CreateThread failed\n", GetLastError() );
+ }
+
+ /* set the thread priority of the new thread to the highest value */
+ if( ! SetThreadPriority( hThread, THREAD_PRIORITY_TIME_CRITICAL) )
+ {
+ Fail( "ERROR:%lu:SetThreadPriority() call failed\n", GetLastError() );
+ }
+
+ /* let the child thread run now */
+ ResumeThread( hThread );
+
+
+ /* wait for the thread to finish */
+ dwRet = WaitForSingleObject( hThread, INFINITE );
+ if( dwRet == WAIT_FAILED )
+ {
+ /* ERROR */
+ Fail( "ERROR:%lu:WaitForSingleObject call failed\n", GetLastError() );
+ }
+
+ /* validate the thread's exit code */
+ if( g_priority != THREAD_PRIORITY_TIME_CRITICAL )
+ {
+ /* ERROR */
+ Fail( "FAIL:Unexpected thread priority %d returned, expected %d\n",
+ g_priority, THREAD_PRIORITY_TIME_CRITICAL );
+ }
+#endif
+
+ PAL_Terminate();
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/GetCurrentThread/test2/testinfo.dat b/src/pal/tests/palsuite/threading/GetCurrentThread/test2/testinfo.dat
new file mode 100644
index 0000000000..96a6d403bb
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThread/test2/testinfo.dat
@@ -0,0 +1,18 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = GetCurrentThread
+Name = Test for GetCurrentThread
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Test to ensure proper operation of the GetCurrentThread()
+= API. The test launches a thread in suspended mode, and sets
+= its priority to a non-default value using the handle returned
+= by CreateThread(). The new thread calls GetCurrentThred() to
+= retrieve a handle to itself, and calls GetThreadPriority()
+= to verify that its priority matches what it was set to on
+= the main execution thread.
diff --git a/src/pal/tests/palsuite/threading/GetCurrentThreadId/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetCurrentThreadId/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThreadId/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/CMakeLists.txt
new file mode 100644
index 0000000000..490bff8f5f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ threadId.c
+)
+
+add_executable(paltest_getcurrentthreadid_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_getcurrentthreadid_test1 coreclrpal)
+
+target_link_libraries(paltest_getcurrentthreadid_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/testinfo.dat b/src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/testinfo.dat
new file mode 100644
index 0000000000..4d1e056b71
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = GetCurrentThreadId
+Name = Positive Test for GetCurrentThreadId
+TYPE = DEFAULT
+EXE1 = threadid
+Description
+= Test to ensure GetCurrentThreadId returns the threadId of the current
+= thread.
diff --git a/src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/threadId.c b/src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/threadId.c
new file mode 100644
index 0000000000..acbb1ff373
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/threadId.c
@@ -0,0 +1,82 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: getcurrentthreadid/test1/threadid.c
+**
+** Purpose: Test to ensure GetCurrentThreadId returns the threadId of the
+** current thread.
+**
+** Dependencies: CloseHandle
+** WaitForSingleObject
+** CreateThread
+**
+
+**
+**=========================================================*/
+
+
+#include <palsuite.h>
+
+DWORD dwThreadIdTF;
+
+DWORD PALAPI ThreadFunction ( LPVOID lpParam )
+{
+ Trace ("thread code executed\n");
+ dwThreadIdTF = GetCurrentThreadId();
+ return 0;
+}
+
+int __cdecl main( int argc, char **argv )
+{
+ extern DWORD dwThreadIdTF;
+ DWORD dwThreadIdCT;
+ HANDLE hThread;
+ DWORD dwThreadParam = 1;
+ DWORD dwThreadWait;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ hThread = CreateThread(
+ NULL,
+ 0,
+ ThreadFunction,
+ &dwThreadParam,
+ 0,
+ &dwThreadIdCT);
+
+ if ( NULL == hThread )
+ {
+ Fail ( "CreateThread() call failed - returned NULL");
+ }
+ else
+ {
+ dwThreadWait = WaitForSingleObject( hThread, INFINITE );
+
+ Trace ("dwThreadWait returned %d\n", dwThreadWait );
+
+ if ( dwThreadIdCT == dwThreadIdTF )
+ {
+ Trace ( "ThreadId numbers match - GetCurrentThreadId"
+ " works. dwThreadIdCT == dwThreadIdTF == %d\n",
+ dwThreadIdTF );
+ PAL_Terminate();
+ return ( PASS );
+ }
+ else
+ {
+ Fail ( "ThreadId numbers don't match - "
+ "GetCurrentThreadId fails dwThreadIdCT = %d "
+ "and dwThreadIdTF = %d\n", dwThreadIdCT, dwThreadIdTF);
+ }
+ }
+
+ PAL_TerminateEx(FAIL);
+ return (FAIL);
+
+}
diff --git a/src/pal/tests/palsuite/threading/GetExitCodeProcess/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetExitCodeProcess/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetExitCodeProcess/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/CMakeLists.txt
new file mode 100644
index 0000000000..adddd97e6d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ test1.c
+)
+
+add_executable(paltest_getexitcodeprocess_test1
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_getexitcodeprocess_test1 coreclrpal)
+
+target_link_libraries(paltest_getexitcodeprocess_test1
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ childProcess.c
+)
+
+add_executable(paltest_getexitcodeprocess_test1_child
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_getexitcodeprocess_test1_child coreclrpal)
+
+target_link_libraries(paltest_getexitcodeprocess_test1_child
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/childProcess.c b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/childProcess.c
new file mode 100644
index 0000000000..fe1b38fb31
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/childProcess.c
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: childprocess.c
+**
+** Purpose: Test to ensure GetExitCodeProcess returns the right
+** value. All this program does is return a predefined value.
+**
+** Dependencies: none
+**
+
+**
+**=========================================================*/
+
+#include <pal.h>
+#include "myexitcode.h"
+
+int __cdecl main( int argc, char **argv )
+{
+ int i;
+
+ // simulate some activity
+ for( i=0; i<10000; i++ )
+ ;
+
+ // return the predefined exit code
+ return TEST_EXIT_CODE;
+}
diff --git a/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/myexitcode.h b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/myexitcode.h
new file mode 100644
index 0000000000..60a140d1f3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/myexitcode.h
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================================
+**
+** Source: myexitcode.h
+**
+** Purpose: Define an exit code.
+**
+**
+**==========================================================================*/
+
+#define TEST_EXIT_CODE 104
diff --git a/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/test1.c b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/test1.c
new file mode 100644
index 0000000000..0f98cf8f57
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/test1.c
@@ -0,0 +1,163 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test1.c
+**
+** Purpose: Test to ensure GetExitCodeProcess works properly.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** Fail
+** ZeroMemory
+** GetCurrentDirectoryW
+** CreateProcessW
+** WaitForSingleObject
+** GetLastError
+** strlen
+** strncpy
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+#include "myexitcode.h"
+
+
+static const char* rgchPathDelim = "\\";
+
+
+int
+mkAbsoluteFilename( LPSTR dirName,
+ DWORD dwDirLength,
+ LPCSTR fileName,
+ DWORD dwFileLength,
+ LPSTR absPathName )
+{
+ DWORD sizeDN, sizeFN, sizeAPN;
+
+ sizeDN = strlen( dirName );
+ sizeFN = strlen( fileName );
+ sizeAPN = (sizeDN + 1 + sizeFN + 1);
+
+ /* ensure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */
+ if( sizeAPN > _MAX_PATH )
+ {
+ return ( 0 );
+ }
+
+ strncpy( absPathName, dirName, dwDirLength +1 );
+ strncpy( absPathName, rgchPathDelim, 2 );
+ strncpy( absPathName, fileName, dwFileLength +1 );
+
+ return (sizeAPN);
+
+}
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ const char* rgchChildFile = "childprocess";
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ DWORD dwError;
+ DWORD dwExitCode;
+ DWORD dwFileLength;
+ DWORD dwDirLength;
+ DWORD dwSize;
+
+ char rgchDirName[_MAX_DIR];
+ char absPathBuf[_MAX_PATH];
+ char* rgchAbsPathName;
+
+ /* initialize the PAL */
+ if( PAL_Initialize(argc, argv) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* zero our process and startup info structures */
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof( si );
+ ZeroMemory( &pi, sizeof(pi) );
+
+ /* build the absolute path to the child process */
+ rgchAbsPathName = &absPathBuf[0];
+ dwFileLength = strlen( rgchChildFile );
+
+ dwDirLength = GetCurrentDirectory( _MAX_PATH, rgchDirName );
+ if( dwDirLength == 0 )
+ {
+ dwError = GetLastError();
+ Fail( "GetCurrentDirectory call failed with error code %d\n",
+ dwError );
+ }
+
+ dwSize = mkAbsoluteFilename( rgchDirName,
+ dwDirLength,
+ rgchChildFile,
+ dwFileLength,
+ rgchAbsPathName );
+ if( dwSize == 0 )
+ {
+ Fail( "Palsuite Code: mkAbsoluteFilename() call failed. Could ",
+ "not build absolute path name to file\n. Exiting.\n" );
+ }
+
+ /* launch the child process */
+ if( !CreateProcess( NULL, /* module name to execute */
+ rgchAbsPathName, /* command line */
+ NULL, /* process handle not */
+ /* inheritable */
+ NULL, /* thread handle not */
+ /* inheritable */
+ FALSE, /* handle inheritance */
+ CREATE_NEW_CONSOLE, /* dwCreationFlags */
+ NULL, /* use parent's environment */
+ NULL, /* use parent's starting */
+ /* directory */
+ &si, /* startup info struct */
+ &pi ) /* process info struct */
+ )
+ {
+ dwError = GetLastError();
+ Fail( "CreateProcess call failed with error code %d\n",
+ dwError );
+ }
+
+ /* wait for the child process to complete */
+ WaitForSingleObject ( pi.hProcess, INFINITE );
+
+ /* check the exit code from the process */
+ if( ! GetExitCodeProcess( pi.hProcess, &dwExitCode ) )
+ {
+ dwError = GetLastError();
+ CloseHandle ( pi.hProcess );
+ CloseHandle ( pi.hThread );
+ Fail( "GetExitCodeProcess call failed with error code %d\n",
+ dwError );
+ }
+
+ /* close process and thread handle */
+ CloseHandle ( pi.hProcess );
+ CloseHandle ( pi.hThread );
+
+ /* check for the expected exit code */
+ if( dwExitCode != TEST_EXIT_CODE )
+ {
+ Fail( "GetExitCodeProcess returned an incorrect exit code %d, "
+ "expected value is %d\n",
+ dwExitCode, TEST_EXIT_CODE );
+ }
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/testinfo.dat b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/testinfo.dat
new file mode 100644
index 0000000000..d06719f0b0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/testinfo.dat
@@ -0,0 +1,16 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = GetExitCodeProcess
+Name = Test for GetExitCodeProcess
+TYPE = DEFAULT
+EXE1 = test1
+EXE2 = childprocess
+Description
+= Test to ensure proper operation of the GetExitCodeProcess
+= API. This test launches a simple child process that exits
+= with a known value, and checks that the correct value is
+= returned by the function.
diff --git a/src/pal/tests/palsuite/threading/GetProcessTimes/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetProcessTimes/CMakeLists.txt
new file mode 100644
index 0000000000..f4796dc1d3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetProcessTimes/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test2)
+
diff --git a/src/pal/tests/palsuite/threading/GetProcessTimes/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetProcessTimes/test2/CMakeLists.txt
new file mode 100644
index 0000000000..0ef7260204
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetProcessTimes/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_getprocesstimes_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_getprocesstimes_test2 coreclrpal)
+
+target_link_libraries(paltest_getprocesstimes_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/GetProcessTimes/test2/test2.c b/src/pal/tests/palsuite/threading/GetProcessTimes/test2/test2.c
new file mode 100644
index 0000000000..687facc584
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetProcessTimes/test2/test2.c
@@ -0,0 +1,122 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test2.c
+**
+** Purpose: Test to ensure GetProcessTimes works properly.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** Fail
+** ZeroMemory
+** CompareFileTime
+** GetLastError
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ int i, j, k;
+ int *total;
+
+ HANDLE hProcess;
+ FILETIME createTime;
+ FILETIME exitTime;
+ FILETIME kernelTime1;
+ FILETIME userTime1;
+ FILETIME kernelTime2;
+ FILETIME userTime2;
+
+ DWORD dwError;
+
+ /* initialize the PAL */
+ if( PAL_Initialize(argc, argv) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* get our own process handle */
+ hProcess = GetCurrentProcess();
+ if( hProcess == NULL )
+ {
+ Fail( "GetCurrentProcess() returned a NULL handle.\n" );
+ }
+
+ /* zero our time structures */
+ ZeroMemory( &createTime, sizeof(createTime) );
+ ZeroMemory( &exitTime, sizeof(exitTime) );
+ ZeroMemory( &kernelTime1, sizeof(kernelTime1) );
+ ZeroMemory( &userTime1, sizeof(userTime1) );
+ ZeroMemory( &kernelTime2, sizeof(kernelTime2) );
+ ZeroMemory( &userTime2, sizeof(userTime2) );
+
+ /* check the process times for the child process */
+ if( ! GetProcessTimes( hProcess,
+ &createTime,
+ &exitTime,
+ &kernelTime1,
+ &userTime1 ) )
+ {
+ dwError = GetLastError();
+ Fail( "GetProcessTimes() call failed with error code %d\n",
+ dwError );
+ }
+
+
+ /* simulate some activity */
+ for( i=0; i<1000; i++ )
+ {
+ for( j=0; j<1000; j++ )
+ {
+ /* do kernel work to increase system usage counters */
+ total = malloc(1024 * 1024);
+
+ *total = j * i;
+ for( k=0; k<1000; k++ )
+ {
+ *total += k + i;
+ }
+
+ free(total);
+ }
+ }
+
+ /* check the process times for the child process */
+ if( ! GetProcessTimes( hProcess,
+ &createTime,
+ &exitTime,
+ &kernelTime2,
+ &userTime2 ) )
+ {
+ dwError = GetLastError();
+ Fail( "GetProcessTimes() call failed with error code %d\n",
+ dwError );
+ }
+
+
+ /* very simple logical checking of the results */
+ if( CompareFileTime( &kernelTime1, &kernelTime2 ) > 0 )
+ {
+ Fail( "Unexpected kernel time value reported.\n" );
+ }
+
+ if( CompareFileTime( &userTime1, &userTime2 ) > 0 )
+ {
+ Fail( "Unexpected user time value reported.\n" );
+ }
+
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/GetProcessTimes/test2/testinfo.dat b/src/pal/tests/palsuite/threading/GetProcessTimes/test2/testinfo.dat
new file mode 100644
index 0000000000..d0d3b75f06
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetProcessTimes/test2/testinfo.dat
@@ -0,0 +1,15 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = GetProcessTimes
+Name = Test for GetProcessTimes
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Test to ensure proper operation of the GetProcessTimes()
+= API. This test simply checks the kernel/user times for the
+= the current process, which is the only thing supported
+= for this function under the PAL.
diff --git a/src/pal/tests/palsuite/threading/GetThreadTimes/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetThreadTimes/CMakeLists.txt
new file mode 100644
index 0000000000..5e1ef7f28b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetThreadTimes/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
diff --git a/src/pal/tests/palsuite/threading/GetThreadTimes/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/GetThreadTimes/test1/CMakeLists.txt
new file mode 100644
index 0000000000..d7e2eb2a88
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetThreadTimes/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_getthreadtimes_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_getthreadtimes_test1 coreclrpal)
+
+target_link_libraries(paltest_getthreadtimes_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/GetThreadTimes/test1/test1.c b/src/pal/tests/palsuite/threading/GetThreadTimes/test1/test1.c
new file mode 100644
index 0000000000..6b62c05ec7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetThreadTimes/test1/test1.c
@@ -0,0 +1,102 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source:
+**
+** Source : test1.c
+**
+** Purpose: Test for GetThreadTimes() function
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+int __cdecl main(int argc, char *argv[]) {
+ int ret = FAIL;
+
+ //Test is failing unreliably, so for now we always return pass.
+ if (TRUE){
+ ret = PASS;
+ goto EXIT;
+ }
+
+ FILETIME kernelTime1, userTime1, kernelTime2, userTime2;
+ /* Delta = .01 sec */
+ LONG64 Actual, Expected, Delta = 850000000;
+ Actual = 0;
+ Expected = 0;
+ const ULONG64 MSEC_TO_NSEC = 1000000;
+
+ /*
+ * Initialize the PAL and return FAILURE if this fails
+ */
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ HANDLE cThread = GetCurrentThread();
+
+ int i;
+ /* Take 2000 tiny measurements */
+ for (i = 0; i < 2000; i++){
+ ULONG64 Time1, Time2;
+
+ Sleep(1);
+
+ /* Grab a FirstCount, then loop for a bit to make the clock increase */
+ if (!GetThreadTimes(cThread, NULL, NULL, &kernelTime1, &userTime1))
+ {
+ Fail("ERROR: GetThreadTimes returned failure.\n");
+ }
+ LONG64 x, Init;
+ /* Init is in milliseconds, so we will convert later */
+ Init = (ULONG64)GetTickCount();
+ /* Spin for < 1 Quantum so we don't get interrupted */
+ x = Init + 3;
+ volatile int counter;
+ do {
+ for (counter = 0; counter < 100000; counter++)
+ {
+ // spin to consume CPU time
+ }
+
+ } while (x > GetTickCount());
+ Expected += (GetTickCount() - Init) * MSEC_TO_NSEC;
+ /* Get a second count */
+ if (!GetThreadTimes(cThread, NULL, NULL, &kernelTime2, &userTime2))
+ {
+ Fail("ERROR: GetThreadTimes returned failure.\n");
+ }
+
+ Time1 = ((ULONG64)kernelTime1.dwHighDateTime << 32);
+ Time1 += (ULONG64)kernelTime1.dwLowDateTime;
+ Time1 += ((ULONG64)userTime1.dwHighDateTime << 32);
+ Time1 += (ULONG64)userTime1.dwLowDateTime;
+
+ Time2 = ((ULONG64)kernelTime2.dwHighDateTime << 32);
+ Time2 += (ULONG64)kernelTime2.dwLowDateTime;
+ Time2 += ((ULONG64)userTime2.dwHighDateTime << 32);
+ Time2 += (ULONG64)userTime2.dwLowDateTime;
+
+ Actual += (Time2 - Time1) * 100;
+ }
+
+ if(labs(Expected - Actual) > Delta)
+ {
+ Fail("ERROR: The measured time (%llu millisecs) was not within Delta %llu "
+ "of the expected time (%llu millisecs).\n",
+ (Actual / MSEC_TO_NSEC), (Delta / MSEC_TO_NSEC), (Expected / MSEC_TO_NSEC));
+ }
+ //printf("%llu, %llu\n", Expected, Actual);
+ PAL_Terminate();
+ ret = PASS;
+
+EXIT:
+ return ret;
+}
diff --git a/src/pal/tests/palsuite/threading/NamedMutex/CMakeLists.txt b/src/pal/tests/palsuite/threading/NamedMutex/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/NamedMutex/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/NamedMutex/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/NamedMutex/test1/CMakeLists.txt
new file mode 100644
index 0000000000..315144e1e8
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/NamedMutex/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ namedmutex.cpp
+ nopal.cpp
+)
+
+add_executable(paltest_namedmutex_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_namedmutex_test1 coreclrpal)
+
+target_link_libraries(paltest_namedmutex_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/NamedMutex/test1/namedmutex.cpp b/src/pal/tests/palsuite/threading/NamedMutex/test1/namedmutex.cpp
new file mode 100644
index 0000000000..3726214701
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/NamedMutex/test1/namedmutex.cpp
@@ -0,0 +1,1058 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// These test cases test named mutexes, including positive
+// and negative cases, cross - thread and cross - process, mutual
+// exclusion, abandon detection, etc.
+
+#include <palsuite.h>
+
+#ifndef _countof
+#define _countof(a) (sizeof(a) / sizeof(a[0]))
+#endif // !_countof
+
+const char *const SessionPrefix = "Local\\";
+const char *const GlobalPrefix = "Global\\";
+
+const char *const NamePrefix = "paltest_namedmutex_test1_";
+const char *const TempNamePrefix = "paltest_namedmutex_test1_temp_";
+const char *const InvalidNamePrefix0 = "paltest\\namedmutex_";
+const char *const InvalidNamePrefix1 = "paltest/namedmutex_";
+const char *const ParentEventNamePrefix0 = "paltest_namedmutex_test1_pe0_";
+const char *const ParentEventNamePrefix1 = "paltest_namedmutex_test1_pe1_";
+const char *const ChildEventNamePrefix0 = "paltest_namedmutex_test1_ce0_";
+const char *const ChildEventNamePrefix1 = "paltest_namedmutex_test1_ce1_";
+const char *const ChildRunningEventNamePrefix = "paltest_namedmutex_test1_cr_";
+
+const char *const GlobalShmFilePathPrefix = "/tmp/.dotnet/shm/global/";
+
+#define MaxPathSize (200)
+const DWORD PollLoopSleepMilliseconds = 100;
+const DWORD FailTimeoutMilliseconds = 30000;
+DWORD g_expectedTimeoutMilliseconds = 500;
+
+bool g_isParent = true;
+bool g_isStress = false;
+char g_processPath[4096], g_processCommandLinePath[4096];
+DWORD g_parentPid = static_cast<DWORD>(-1);
+
+extern char *(*test_strcpy)(char *dest, const char *src);
+extern int (*test_strcmp)(const char *s1, const char *s2);
+extern size_t (*test_strlen)(const char *s);
+extern int (*test_sprintf)(char *str, const char *format, ...);
+extern int (*test_sscanf)(const char *str, const char *format, ...);
+extern int(*test_close)(int fd);
+extern int (*test_unlink)(const char *pathname);
+extern unsigned int test_getpid();
+extern int test_kill(unsigned int pid);
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Test helpers
+
+extern bool TestFileExists(const char *path);
+extern bool WriteHeaderInfo(const char *path, char sharedMemoryType, char version, int *fdRef);
+
+#define TestAssert(expression) \
+ do \
+ { \
+ if (!(expression)) \
+ { \
+ if (!g_isParent) \
+ { \
+ Trace("Child process: "); \
+ } \
+ Trace("'paltest_namedmutex_test1' failed at line %u. Expression: " #expression "\n", __LINE__); \
+ fflush(stdout); \
+ return false; \
+ } \
+ } while(false)
+
+char *BuildName(const char *testName, char *buffer, const char *prefix0, const char *prefix1 = nullptr)
+{
+ size_t nameLength = 0;
+ const char *prefixes[] = {prefix0, prefix1};
+ for (int i = 0; i < 2; ++i)
+ {
+ const char *prefix = prefixes[i];
+ if (prefix == nullptr)
+ {
+ break;
+ }
+ test_strcpy(&buffer[nameLength], prefix);
+ nameLength += test_strlen(prefix);
+ }
+
+ if (g_isStress)
+ {
+ // Append the test name so that tests can run in parallel
+ nameLength += test_sprintf(&buffer[nameLength], "%s", testName);
+ buffer[nameLength++] = '_';
+ }
+
+ nameLength += test_sprintf(&buffer[nameLength], "%u", g_parentPid);
+ return buffer;
+}
+
+char *BuildGlobalShmFilePath(const char *testName, char *buffer, const char *namePrefix)
+{
+ size_t pathLength = 0;
+ test_strcpy(&buffer[pathLength], GlobalShmFilePathPrefix);
+ pathLength += test_strlen(GlobalShmFilePathPrefix);
+ test_strcpy(&buffer[pathLength], namePrefix);
+ pathLength += test_strlen(namePrefix);
+
+ if (g_isStress)
+ {
+ // Append the test name so that tests can run in parallel
+ pathLength += test_sprintf(&buffer[pathLength], "%s", testName);
+ buffer[pathLength++] = '_';
+ }
+
+ pathLength += test_sprintf(&buffer[pathLength], "%u", g_parentPid);
+ return buffer;
+}
+
+class AutoCloseMutexHandle
+{
+private:
+ HANDLE m_handle;
+
+public:
+ AutoCloseMutexHandle(HANDLE handle = nullptr) : m_handle(handle)
+ {
+ }
+
+ ~AutoCloseMutexHandle()
+ {
+ Close();
+ }
+
+public:
+ HANDLE GetHandle() const
+ {
+ return m_handle;
+ }
+
+ bool Release()
+ {
+ return !!ReleaseMutex(m_handle);
+ }
+
+ void Close()
+ {
+ if (m_handle != nullptr)
+ {
+ CloseHandle(m_handle);
+ m_handle = nullptr;
+ }
+ }
+
+ void Abandon()
+ {
+ // Don't close the handle
+ m_handle = nullptr;
+ }
+
+ AutoCloseMutexHandle &operator =(HANDLE handle)
+ {
+ Close();
+ m_handle = handle;
+ return *this;
+ }
+
+ operator HANDLE() const
+ {
+ return m_handle;
+ }
+
+private:
+ AutoCloseMutexHandle(const AutoCloseMutexHandle &other);
+ AutoCloseMutexHandle(AutoCloseMutexHandle &&other);
+ AutoCloseMutexHandle &operator =(const AutoCloseMutexHandle &other);
+};
+
+void TestCreateMutex(AutoCloseMutexHandle &m, const char *name, bool initiallyOwned = false)
+{
+ m.Close();
+ m = CreateMutexA(nullptr, initiallyOwned, name);
+}
+
+HANDLE TestOpenMutex(const char *name)
+{
+ return OpenMutexA(SYNCHRONIZE, false, name);
+}
+
+bool StartProcess(const char *funcName)
+{
+ // Command line format: <processPath> <parentPid> <testFunctionName> [stress]
+
+ size_t processCommandLinePathLength = 0;
+ g_processCommandLinePath[processCommandLinePathLength++] = '\"';
+ test_strcpy(&g_processCommandLinePath[processCommandLinePathLength], g_processPath);
+ processCommandLinePathLength += test_strlen(g_processPath);
+ g_processCommandLinePath[processCommandLinePathLength++] = '\"';
+ g_processCommandLinePath[processCommandLinePathLength++] = ' ';
+ processCommandLinePathLength += test_sprintf(&g_processCommandLinePath[processCommandLinePathLength], "%u", g_parentPid);
+ g_processCommandLinePath[processCommandLinePathLength++] = ' ';
+ test_strcpy(&g_processCommandLinePath[processCommandLinePathLength], funcName);
+ processCommandLinePathLength += test_strlen(funcName);
+
+ if (g_isStress)
+ {
+ test_strcpy(&g_processCommandLinePath[processCommandLinePathLength], " stress");
+ processCommandLinePathLength += _countof("stress") - 1;
+ }
+
+ STARTUPINFO si;
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ PROCESS_INFORMATION pi;
+ memset(&pi, 0, sizeof(pi));
+ if (!CreateProcessA(nullptr, g_processCommandLinePath, nullptr, nullptr, false, 0, nullptr, nullptr, &si, &pi))
+ {
+ return false;
+ }
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return true;
+}
+
+bool StartThread(LPTHREAD_START_ROUTINE func, void *arg = nullptr, HANDLE *threadHandleRef = nullptr)
+{
+ DWORD threadId;
+ HANDLE handle = CreateThread(nullptr, 0, func, arg, 0, &threadId);
+ if (handle != nullptr)
+ {
+ if (threadHandleRef == nullptr)
+ {
+ CloseHandle(handle);
+ }
+ else
+ {
+ *threadHandleRef = handle;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool WaitForMutexToBeCreated(const char *testName, AutoCloseMutexHandle &m, const char *eventNamePrefix)
+{
+ char eventName[MaxPathSize];
+ BuildName(testName, eventName, GlobalPrefix, eventNamePrefix);
+ DWORD startTime = GetTickCount();
+ while (true)
+ {
+ m = TestOpenMutex(eventName);
+ if (m != nullptr)
+ {
+ return true;
+ }
+ if (GetTickCount() - startTime >= FailTimeoutMilliseconds)
+ {
+ return false;
+ }
+ Sleep(PollLoopSleepMilliseconds);
+ }
+}
+
+// The following functions are used for parent/child tests, where the child runs in a separate thread or process. The tests are
+// organized such that one the parent or child is ever running code, and they yield control and wait for the other. Since the
+// named mutex is the only type of cross-process sync object available, they are used as events to synchronize. The parent and
+// child have a pair of event mutexes each, which they own initially. To release the other waiting thread/process, the
+// thread/process releases one of its mutexes, which the other thread/process would be waiting on. To wait, the thread/process
+// waits on one of the other thread/process' mutexes. All the while, they ping-pong between the two mutexes. YieldToChild() and
+// YieldToParent() below control the releasing, waiting, and ping-ponging, to help create a deterministic path through the
+// parent and child tests while both are running concurrently.
+
+bool AcquireChildRunningEvent(const char *testName, AutoCloseMutexHandle &childRunningEvent)
+{
+ char name[MaxPathSize];
+ TestCreateMutex(childRunningEvent, BuildName(testName, name, GlobalPrefix, ChildRunningEventNamePrefix));
+ TestAssert(WaitForSingleObject(childRunningEvent, FailTimeoutMilliseconds) == WAIT_OBJECT_0);
+ return true;
+}
+
+bool InitializeParent(const char *testName, AutoCloseMutexHandle parentEvents[2], AutoCloseMutexHandle childEvents[2])
+{
+ // Create parent events
+ char name[MaxPathSize];
+ for (int i = 0; i < 2; ++i)
+ {
+ TestCreateMutex(
+ parentEvents[i],
+ BuildName(testName, name, GlobalPrefix, i == 0 ? ParentEventNamePrefix0 : ParentEventNamePrefix1),
+ true);
+ TestAssert(parentEvents[i] != nullptr);
+ TestAssert(GetLastError() != ERROR_ALREADY_EXISTS);
+ }
+
+ // Wait for the child to create and acquire locks on its events so that the parent can wait on them
+ TestAssert(WaitForMutexToBeCreated(testName, childEvents[0], ChildEventNamePrefix0));
+ TestAssert(WaitForMutexToBeCreated(testName, childEvents[1], ChildEventNamePrefix1));
+ return true;
+}
+
+bool UninitializeParent(const char *testName, AutoCloseMutexHandle parentEvents[2], bool releaseParentEvents = true)
+{
+ if (releaseParentEvents)
+ {
+ TestAssert(parentEvents[0].Release());
+ TestAssert(parentEvents[1].Release());
+ }
+
+ // Wait for the child to finish its test. Child tests will release and close 'childEvents' before releasing
+ // 'childRunningEvent', so after this wait, the parent process can freely start another child that will deterministically
+ // recreate the 'childEvents', which the next parent test will wait on, upon its initialization.
+ AutoCloseMutexHandle childRunningEvent;
+ TestAssert(AcquireChildRunningEvent(testName, childRunningEvent));
+ TestAssert(childRunningEvent.Release());
+ return true;
+}
+
+bool InitializeChild(
+ const char *testName,
+ AutoCloseMutexHandle &childRunningEvent,
+ AutoCloseMutexHandle parentEvents[2],
+ AutoCloseMutexHandle childEvents[2])
+{
+ TestAssert(AcquireChildRunningEvent(testName, childRunningEvent));
+
+ // Create child events
+ char name[MaxPathSize];
+ for (int i = 0; i < 2; ++i)
+ {
+ TestCreateMutex(
+ childEvents[i],
+ BuildName(testName, name, GlobalPrefix, i == 0 ? ChildEventNamePrefix0 : ChildEventNamePrefix1),
+ true);
+ TestAssert(childEvents[i] != nullptr);
+ TestAssert(GetLastError() != ERROR_ALREADY_EXISTS);
+ }
+
+ // Wait for the parent to create and acquire locks on its events so that the child can wait on them
+ TestAssert(WaitForMutexToBeCreated(testName, parentEvents[0], ParentEventNamePrefix0));
+ TestAssert(WaitForMutexToBeCreated(testName, parentEvents[1], ParentEventNamePrefix1));
+
+ // Parent/child tests start with the parent, so after initialization, wait for the parent to tell the child test to start
+ TestAssert(WaitForSingleObject(parentEvents[0], FailTimeoutMilliseconds) == WAIT_OBJECT_0);
+ TestAssert(parentEvents[0].Release());
+ return true;
+}
+
+bool UninitializeChild(
+ AutoCloseMutexHandle &childRunningEvent,
+ AutoCloseMutexHandle parentEvents[2],
+ AutoCloseMutexHandle childEvents[2])
+{
+ // Release and close 'parentEvents' and 'childEvents' before releasing 'childRunningEvent' to avoid races, see
+ // UnitializeParent() for more info
+ TestAssert(childEvents[0].Release());
+ TestAssert(childEvents[1].Release());
+ childEvents[0].Close();
+ childEvents[1].Close();
+ parentEvents[0].Close();
+ parentEvents[1].Close();
+ TestAssert(childRunningEvent.Release());
+ return true;
+}
+
+bool YieldToChild(AutoCloseMutexHandle parentEvents[2], AutoCloseMutexHandle childEvents[2], int &ei)
+{
+ TestAssert(parentEvents[ei].Release());
+ TestAssert(WaitForSingleObject(childEvents[ei], FailTimeoutMilliseconds) == WAIT_OBJECT_0);
+ TestAssert(childEvents[ei].Release());
+ TestAssert(WaitForSingleObject(parentEvents[ei], 0) == WAIT_OBJECT_0);
+ ei = 1 - ei;
+ return true;
+}
+
+bool YieldToParent(AutoCloseMutexHandle parentEvents[2], AutoCloseMutexHandle childEvents[2], int &ei)
+{
+ TestAssert(childEvents[ei].Release());
+ ei = 1 - ei;
+ TestAssert(WaitForSingleObject(parentEvents[ei], FailTimeoutMilliseconds) == WAIT_OBJECT_0);
+ TestAssert(parentEvents[ei].Release());
+ TestAssert(WaitForSingleObject(childEvents[1 - ei], 0) == WAIT_OBJECT_0);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Tests
+
+bool NameTests()
+{
+ const char *testName = "NameTests";
+
+ AutoCloseMutexHandle m;
+ char name[MaxPathSize];
+
+ // Empty name
+ TestCreateMutex(m, "");
+ TestAssert(m != nullptr);
+
+ // Normal name
+ TestCreateMutex(m, BuildName(testName, name, NamePrefix));
+ TestAssert(m != nullptr);
+ TestAssert(AutoCloseMutexHandle(TestOpenMutex(BuildName(testName, name, NamePrefix))) != nullptr);
+ TestCreateMutex(m, BuildName(testName, name, SessionPrefix, NamePrefix));
+ TestAssert(m != nullptr);
+ TestAssert(AutoCloseMutexHandle(TestOpenMutex(BuildName(testName, name, SessionPrefix, NamePrefix))) != nullptr);
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix));
+ TestAssert(m != nullptr);
+ TestAssert(AutoCloseMutexHandle(TestOpenMutex(BuildName(testName, name, GlobalPrefix, NamePrefix))) != nullptr);
+
+ // Name too long. The maximum allowed length depends on the file system, so we're not checking for that.
+ {
+ char name[257];
+ memset(name, 'a', _countof(name) - 1);
+ name[_countof(name) - 1] = '\0';
+ TestCreateMutex(m, name);
+ TestAssert(m == nullptr);
+ TestAssert(GetLastError() == ERROR_FILENAME_EXCED_RANGE);
+ TestAssert(AutoCloseMutexHandle(TestOpenMutex(name)) == nullptr);
+ TestAssert(GetLastError() == ERROR_FILENAME_EXCED_RANGE);
+ }
+
+ // Invalid characters in name
+ TestCreateMutex(m, BuildName(testName, name, InvalidNamePrefix0));
+ TestAssert(m == nullptr);
+ TestAssert(GetLastError() == ERROR_INVALID_NAME);
+ TestAssert(AutoCloseMutexHandle(TestOpenMutex(BuildName(testName, name, InvalidNamePrefix0))) == nullptr);
+ TestAssert(GetLastError() == ERROR_INVALID_NAME);
+ TestCreateMutex(m, BuildName(testName, name, InvalidNamePrefix1));
+ TestAssert(m == nullptr);
+ TestAssert(GetLastError() == ERROR_INVALID_NAME);
+ TestAssert(AutoCloseMutexHandle(TestOpenMutex(BuildName(testName, name, InvalidNamePrefix1))) == nullptr);
+ TestAssert(GetLastError() == ERROR_INVALID_NAME);
+ TestCreateMutex(m, BuildName(testName, name, SessionPrefix, InvalidNamePrefix0));
+ TestAssert(m == nullptr);
+ TestAssert(GetLastError() == ERROR_INVALID_NAME);
+ TestAssert(AutoCloseMutexHandle(TestOpenMutex(BuildName(testName, name, SessionPrefix, InvalidNamePrefix0))) == nullptr);
+ TestAssert(GetLastError() == ERROR_INVALID_NAME);
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, InvalidNamePrefix1));
+ TestAssert(m == nullptr);
+ TestAssert(GetLastError() == ERROR_INVALID_NAME);
+ TestAssert(AutoCloseMutexHandle(TestOpenMutex(BuildName(testName, name, GlobalPrefix, InvalidNamePrefix1))) == nullptr);
+ TestAssert(GetLastError() == ERROR_INVALID_NAME);
+
+ // Creating a second reference to the same named mutex yields an error indicating that it was opened, not created
+ {
+ TestCreateMutex(m, BuildName(testName, name, NamePrefix));
+ TestAssert(m != nullptr);
+ AutoCloseMutexHandle m2;
+ TestCreateMutex(m2, BuildName(testName, name, NamePrefix));
+ TestAssert(m2 != nullptr);
+ TestAssert(GetLastError() == ERROR_ALREADY_EXISTS);
+ }
+
+ return true;
+}
+
+bool HeaderMismatchTests()
+{
+ const char *testName = "HeaderMismatchTests";
+
+ AutoCloseMutexHandle m, m2;
+ char name[MaxPathSize];
+ int fd;
+
+ // Create and hold onto a mutex during this test to create the shared memory directory
+ TestCreateMutex(m2, BuildName(testName, name, GlobalPrefix, TempNamePrefix));
+ TestAssert(m2 != nullptr);
+
+ // Unknown shared memory type
+ TestAssert(WriteHeaderInfo(BuildGlobalShmFilePath(testName, name, NamePrefix), -1, 1, &fd));
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix));
+ TestAssert(m == nullptr);
+ TestAssert(GetLastError() == ERROR_INVALID_HANDLE);
+ TestAssert(test_close(fd) == 0);
+ TestAssert(test_unlink(BuildGlobalShmFilePath(testName, name, NamePrefix)) == 0);
+
+ // Mismatched version
+ TestAssert(WriteHeaderInfo(BuildGlobalShmFilePath(testName, name, NamePrefix), 0, -1, &fd));
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix));
+ TestAssert(m == nullptr);
+ TestAssert(GetLastError() == ERROR_INVALID_HANDLE);
+ TestAssert(test_close(fd) == 0);
+ TestAssert(test_unlink(BuildGlobalShmFilePath(testName, name, NamePrefix)) == 0);
+
+ return true;
+}
+
+bool MutualExclusionTests_Parent()
+{
+ const char *testName = "MutualExclusionTests";
+
+ AutoCloseMutexHandle parentEvents[2], childEvents[2];
+ TestAssert(InitializeParent(testName, parentEvents, childEvents));
+ int ei = 0;
+ char name[MaxPathSize];
+ AutoCloseMutexHandle m;
+
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix));
+ TestAssert(m != nullptr);
+
+ // Recursive locking with various timeouts
+ TestAssert(WaitForSingleObject(m, 0) == WAIT_OBJECT_0);
+ TestAssert(WaitForSingleObject(m, FailTimeoutMilliseconds) == WAIT_OBJECT_0);
+ TestAssert(WaitForSingleObject(m, static_cast<DWORD>(-1)) == WAIT_OBJECT_0);
+ TestAssert(m.Release());
+ TestAssert(m.Release());
+ TestAssert(m.Release());
+ TestAssert(!m.Release()); // try to release the lock while nobody owns it, and verify recursive lock counting
+ TestAssert(GetLastError() == ERROR_NOT_OWNER);
+
+ TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child takes the lock
+
+ TestAssert(WaitForSingleObject(m, 0) == WAIT_TIMEOUT); // try to lock the mutex without waiting
+ TestAssert(WaitForSingleObject(m, g_expectedTimeoutMilliseconds) == WAIT_TIMEOUT); // try to lock the mutex with a timeout
+ TestAssert(!m.Release()); // try to release the lock while another thread owns it
+ TestAssert(GetLastError() == ERROR_NOT_OWNER);
+
+ TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child releases the lock
+
+ TestAssert(WaitForSingleObject(m, static_cast<DWORD>(-1)) == WAIT_OBJECT_0); // lock the mutex with no timeout and release
+ TestAssert(m.Release());
+
+ UninitializeParent(testName, parentEvents);
+ return true;
+}
+
+DWORD MutualExclusionTests_Child(void *arg = nullptr)
+{
+ const char *testName = "MutualExclusionTests";
+
+ AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2];
+ TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents));
+ int ei = 0;
+
+ {
+ char name[MaxPathSize];
+ AutoCloseMutexHandle m;
+
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix));
+ TestAssert(m != nullptr);
+ TestAssert(WaitForSingleObject(m, 0) == WAIT_OBJECT_0); // lock the mutex
+ YieldToParent(parentEvents, childEvents, ei); // parent attempts to lock/release, and fails
+ TestAssert(m.Release()); // release the lock
+ }
+
+ UninitializeChild(childRunningEvent, parentEvents, childEvents);
+ return 0;
+}
+
+bool MutualExclusionTests()
+{
+ const char *testName = "MutualExclusionTests";
+
+ {
+ AutoCloseMutexHandle m;
+ char name[MaxPathSize];
+
+ // Releasing a lock that is not owned by any thread fails
+ TestCreateMutex(m, BuildName(testName, name, NamePrefix));
+ TestAssert(m != nullptr);
+ TestAssert(!m.Release());
+ TestAssert(GetLastError() == ERROR_NOT_OWNER);
+
+ // Acquire a lock during upon creation, and release
+ TestCreateMutex(m, BuildName(testName, name, NamePrefix), true);
+ TestAssert(m != nullptr);
+ TestAssert(m.Release());
+
+ // Multi-waits including a named mutex are not supported
+ AutoCloseMutexHandle m2;
+ TestCreateMutex(m2, nullptr);
+ TestAssert(m2 != nullptr);
+ HANDLE waitHandles[] = {m2.GetHandle(), m.GetHandle()};
+ TestAssert(
+ WaitForMultipleObjects(
+ _countof(waitHandles),
+ waitHandles,
+ false /* waitAll */,
+ FailTimeoutMilliseconds) ==
+ WAIT_FAILED);
+ TestAssert(GetLastError() == ERROR_NOT_SUPPORTED);
+ TestAssert(
+ WaitForMultipleObjects(
+ _countof(waitHandles),
+ waitHandles,
+ true /* waitAll */,
+ FailTimeoutMilliseconds) ==
+ WAIT_FAILED);
+ TestAssert(GetLastError() == ERROR_NOT_SUPPORTED);
+ }
+
+ // When another thread or process owns the lock, this process should not be able to acquire a lock, and the converse
+ TestAssert(StartThread(MutualExclusionTests_Child));
+ TestAssert(MutualExclusionTests_Parent());
+ TestAssert(StartProcess("MutualExclusionTests_Child"));
+ TestAssert(MutualExclusionTests_Parent());
+
+ return true;
+}
+
+bool LifetimeTests_Parent()
+{
+ const char *testName = "LifetimeTests";
+
+ AutoCloseMutexHandle parentEvents[2], childEvents[2];
+ TestAssert(InitializeParent(testName, parentEvents, childEvents));
+ int ei = 0;
+ char name[MaxPathSize];
+ AutoCloseMutexHandle m;
+
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix)); // create first reference to mutex
+ TestAssert(m != nullptr);
+ TestAssert(TestFileExists(BuildGlobalShmFilePath(testName, name, NamePrefix)));
+ TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child creates second reference to mutex using CreateMutex
+ m.Close(); // close first reference
+ TestAssert(TestFileExists(BuildGlobalShmFilePath(testName, name, NamePrefix)));
+ TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child closes second reference
+ TestAssert(!TestFileExists(BuildGlobalShmFilePath(testName, name, NamePrefix)));
+
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix)); // create first reference to mutex
+ TestAssert(m != nullptr);
+ TestAssert(TestFileExists(BuildGlobalShmFilePath(testName, name, NamePrefix)));
+ TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child creates second reference to mutex using OpenMutex
+ m.Close(); // close first reference
+ TestAssert(TestFileExists(BuildGlobalShmFilePath(testName, name, NamePrefix)));
+ TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child closes second reference
+ TestAssert(!TestFileExists(BuildGlobalShmFilePath(testName, name, NamePrefix)));
+
+ UninitializeParent(testName, parentEvents);
+ return true;
+}
+
+DWORD LifetimeTests_Child(void *arg = nullptr)
+{
+ const char *testName = "LifetimeTests";
+
+ AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2];
+ TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents));
+ int ei = 0;
+
+ {
+ char name[MaxPathSize];
+ AutoCloseMutexHandle m;
+
+ // ... parent creates first reference to mutex
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix)); // create second reference to mutex using CreateMutex
+ TestAssert(m != nullptr);
+ TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent closes first reference
+ m.Close(); // close second reference
+
+ TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent verifies, and creates first reference to mutex again
+ m = TestOpenMutex(BuildName(testName, name, GlobalPrefix, NamePrefix)); // create second reference to mutex using OpenMutex
+ TestAssert(m != nullptr);
+ TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent closes first reference
+ m.Close(); // close second reference
+
+ TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent verifies
+ }
+
+ UninitializeChild(childRunningEvent, parentEvents, childEvents);
+ return 0;
+}
+
+bool LifetimeTests()
+{
+ const char *testName = "LifetimeTests";
+
+ {
+ AutoCloseMutexHandle m;
+ char name[MaxPathSize];
+
+ // Shm file should be created and deleted
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix));
+ TestAssert(m != nullptr);
+ TestAssert(TestFileExists(BuildGlobalShmFilePath(testName, name, NamePrefix)));
+ m.Close();
+ TestAssert(!TestFileExists(BuildGlobalShmFilePath(testName, name, NamePrefix)));
+ }
+
+ // Shm file should not be deleted until last reference is released
+ TestAssert(StartThread(LifetimeTests_Child));
+ TestAssert(LifetimeTests_Parent());
+ TestAssert(StartProcess("LifetimeTests_Child"));
+ TestAssert(LifetimeTests_Parent());
+
+ return true;
+}
+
+DWORD AbandonTests_Child_TryLock(void *arg = nullptr);
+
+bool AbandonTests_Parent()
+{
+ const char *testName = "AbandonTests";
+
+ char name[MaxPathSize];
+ AutoCloseMutexHandle m;
+ {
+ AutoCloseMutexHandle parentEvents[2], childEvents[2];
+ TestAssert(InitializeParent(testName, parentEvents, childEvents));
+ int ei = 0;
+
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix));
+ TestAssert(m != nullptr);
+ TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child locks mutex
+ TestAssert(parentEvents[0].Release());
+ TestAssert(parentEvents[1].Release()); // child sleeps for short duration and abandons the mutex
+ TestAssert(WaitForSingleObject(m, FailTimeoutMilliseconds) == WAIT_ABANDONED_0); // attempt to lock and see abandoned mutex
+
+ UninitializeParent(testName, parentEvents, false /* releaseParentEvents */); // parent events are released above
+ }
+
+ // Verify that the mutex lock is owned by this thread, by starting a new thread and trying to lock it
+ StartThread(AbandonTests_Child_TryLock);
+ {
+ AutoCloseMutexHandle parentEvents[2], childEvents[2];
+ TestAssert(InitializeParent(testName, parentEvents, childEvents));
+ int ei = 0;
+
+ TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child tries to lock mutex
+
+ UninitializeParent(testName, parentEvents);
+ }
+
+ // Verify that the mutex lock is owned by this thread, by starting a new process and trying to lock it
+ StartProcess("AbandonTests_Child_TryLock");
+ AutoCloseMutexHandle parentEvents[2], childEvents[2];
+ TestAssert(InitializeParent(testName, parentEvents, childEvents));
+ int ei = 0;
+
+ TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child tries to lock mutex
+
+ // Continue verification
+ TestAssert(m.Release());
+ TestAssert(WaitForSingleObject(m, FailTimeoutMilliseconds) == WAIT_OBJECT_0); // lock again to see it's not abandoned anymore
+ TestAssert(m.Release());
+
+ UninitializeParent(testName, parentEvents, false /* releaseParentEvents */); // parent events are released above
+
+ // Since the child abandons the mutex, and a child process may not release the file lock on the shared memory file before
+ // indicating completion to the parent, make sure to delete the shared memory file by repeatedly opening/closing the mutex
+ // until the parent process becomes the last process to reference the mutex and closing it deletes the file.
+ DWORD startTime = GetTickCount();
+ while (true)
+ {
+ m.Close();
+ if (!TestFileExists(BuildGlobalShmFilePath(testName, name, NamePrefix)))
+ {
+ break;
+ }
+
+ TestAssert(GetTickCount() - startTime < FailTimeoutMilliseconds);
+ m = TestOpenMutex(BuildName(testName, name, GlobalPrefix, NamePrefix));
+ }
+
+ return true;
+}
+
+DWORD AbandonTests_Child_GracefulExit_Close(void *arg = nullptr)
+{
+ const char *testName = "AbandonTests";
+
+ AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2];
+ TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents));
+ int ei = 0;
+
+ {
+ char name[MaxPathSize];
+ AutoCloseMutexHandle m;
+
+ // ... parent waits for child to lock mutex
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix));
+ TestAssert(m != nullptr);
+ TestAssert(WaitForSingleObject(m, 0) == WAIT_OBJECT_0);
+ TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent waits on mutex
+ Sleep(g_expectedTimeoutMilliseconds); // wait for parent to wait on mutex
+ m.Close(); // close mutex without releasing lock
+ }
+
+ UninitializeChild(childRunningEvent, parentEvents, childEvents);
+ return 0;
+}
+
+DWORD AbandonTests_Child_GracefulExit_NoClose(void *arg = nullptr)
+{
+ const char *testName = "AbandonTests";
+
+ // This test needs to run in a separate process because it does not close the mutex handle. Running it in a separate thread
+ // causes the mutex object to retain a reference until the process terminates.
+ TestAssert(test_getpid() != g_parentPid);
+
+ AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2];
+ TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents));
+ int ei = 0;
+
+ {
+ char name[MaxPathSize];
+ AutoCloseMutexHandle m;
+
+ // ... parent waits for child to lock mutex
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix));
+ TestAssert(m != nullptr);
+ TestAssert(WaitForSingleObject(m, 0) == WAIT_OBJECT_0);
+ TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent waits on mutex
+ Sleep(g_expectedTimeoutMilliseconds); // wait for parent to wait on mutex
+ m.Abandon(); // don't close the mutex
+ }
+
+ UninitializeChild(childRunningEvent, parentEvents, childEvents);
+ return 0;
+}
+
+DWORD AbandonTests_Child_AbruptExit(void *arg = nullptr)
+{
+ const char *testName = "AbandonTests";
+
+ DWORD currentPid = test_getpid();
+ TestAssert(currentPid != g_parentPid); // this test needs to run in a separate process
+
+ {
+ AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2];
+ TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents));
+ int ei = 0;
+
+ {
+ char name[MaxPathSize];
+ AutoCloseMutexHandle m;
+
+ // ... parent waits for child to lock mutex
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix));
+ TestAssert(m != nullptr);
+ TestAssert(WaitForSingleObject(m, 0) == WAIT_OBJECT_0);
+ TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent waits on mutex
+ Sleep(g_expectedTimeoutMilliseconds); // wait for parent to wait on mutex
+ m.Abandon(); // don't close the mutex
+ }
+
+ UninitializeChild(childRunningEvent, parentEvents, childEvents);
+ }
+
+ TestAssert(test_kill(currentPid) == 0); // abandon the mutex abruptly
+ return 0;
+}
+
+DWORD AbandonTests_Child_TryLock(void *arg)
+{
+ const char *testName = "AbandonTests";
+
+ AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2];
+ TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents));
+ int ei = 0;
+
+ {
+ char name[MaxPathSize];
+ AutoCloseMutexHandle m;
+
+ // ... parent waits for child to lock mutex
+ TestCreateMutex(m, BuildName(testName, name, GlobalPrefix, NamePrefix));
+ TestAssert(m != nullptr);
+ TestAssert(WaitForSingleObject(m, 0) == WAIT_TIMEOUT); // try to lock the mutex while the parent holds the lock
+ TestAssert(WaitForSingleObject(m, g_expectedTimeoutMilliseconds) == WAIT_TIMEOUT);
+ }
+
+ UninitializeChild(childRunningEvent, parentEvents, childEvents);
+ return 0;
+}
+
+bool AbandonTests()
+{
+ const char *testName = "AbandonTests";
+
+ // Abandon by graceful exit where the lock owner closes the mutex before releasing it, unblocks a waiter
+ TestAssert(StartThread(AbandonTests_Child_GracefulExit_Close));
+ TestAssert(AbandonTests_Parent());
+ TestAssert(StartProcess("AbandonTests_Child_GracefulExit_Close"));
+ TestAssert(AbandonTests_Parent());
+
+ // Abandon by graceful exit without closing the mutex unblocks a waiter
+ TestAssert(StartProcess("AbandonTests_Child_GracefulExit_NoClose"));
+ TestAssert(AbandonTests_Parent());
+
+ // Abandon by abrupt exit unblocks a waiter
+ TestAssert(StartProcess("AbandonTests_Child_AbruptExit"));
+ TestAssert(AbandonTests_Parent());
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Test harness
+
+bool (*const (TestList[]))() =
+{
+ NameTests,
+ HeaderMismatchTests,
+ MutualExclusionTests,
+ LifetimeTests,
+ AbandonTests
+};
+
+bool RunTests()
+{
+ bool allPassed = true;
+ for (SIZE_T i = 0; i < _countof(TestList); ++i)
+ {
+ if (!TestList[i]())
+ {
+ allPassed = false;
+ }
+ }
+ return allPassed;
+}
+
+DWORD g_stressDurationMilliseconds = 0;
+LONG g_stressTestCounts[_countof(TestList)] = {0};
+LONG g_stressResult = true;
+
+DWORD StressTest(void *arg)
+{
+ // Run the specified test continuously for the stress duration
+ SIZE_T testIndex = reinterpret_cast<SIZE_T>(arg);
+ DWORD startTime = GetTickCount();
+ do
+ {
+ ++g_stressTestCounts[testIndex];
+ if (!TestList[testIndex]())
+ {
+ InterlockedExchange(&g_stressResult, false);
+ break;
+ }
+ } while (
+ InterlockedCompareExchange(&g_stressResult, false, false) == true &&
+ GetTickCount() - startTime < g_stressDurationMilliseconds);
+ return 0;
+}
+
+bool StressTests(DWORD durationMinutes)
+{
+ g_isStress = true;
+ g_expectedTimeoutMilliseconds = 1;
+ g_stressDurationMilliseconds = durationMinutes * (60 * 1000);
+
+ // Start a thread for each test
+ HANDLE threadHandles[_countof(TestList)];
+ for (SIZE_T i = 0; i < _countof(threadHandles); ++i)
+ {
+ TestAssert(StartThread(StressTest, reinterpret_cast<void *>(i), &threadHandles[i]));
+ }
+
+ while (true)
+ {
+ DWORD waitResult =
+ WaitForMultipleObjects(_countof(threadHandles), threadHandles, true /* bWaitAll */, 10 * 1000 /* dwMilliseconds */);
+ TestAssert(waitResult == WAIT_OBJECT_0 || waitResult == WAIT_TIMEOUT);
+ if (waitResult == WAIT_OBJECT_0)
+ {
+ break;
+ }
+
+ Trace("'paltest_namedmutex_test1' stress test counts: ");
+ for (SIZE_T i = 0; i < _countof(g_stressTestCounts); ++i)
+ {
+ if (i != 0)
+ {
+ Trace(", ");
+ }
+ Trace("%u", g_stressTestCounts[i]);
+ }
+ Trace("\n");
+ fflush(stdout);
+ }
+
+ for (SIZE_T i = 0; i < _countof(threadHandles); ++i)
+ {
+ CloseHandle(threadHandles[i]);
+ }
+ return static_cast<bool>(g_stressResult);
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ if (argc < 1 || argc > 4)
+ {
+ return FAIL;
+ }
+
+ if (PAL_Initialize(argc, argv) != 0)
+ {
+ return FAIL;
+ }
+
+ test_strcpy(g_processPath, argv[0]);
+
+ if (argc == 1)
+ {
+ // Unit test arguments: <processPath>
+
+ g_parentPid = test_getpid();
+ int result = RunTests() ? PASS : FAIL;
+ ExitProcess(result);
+ return result;
+ }
+
+ if (test_strcmp(argv[1], "stress") == 0)
+ {
+ // Stress test arguments: <processPath> stress [durationMinutes]
+
+ DWORD durationMinutes = 1;
+ if (argc >= 3 && test_sscanf(argv[2], "%u", &durationMinutes) != 1)
+ {
+ ExitProcess(FAIL);
+ return FAIL;
+ }
+
+ g_parentPid = test_getpid();
+ int result = StressTests(durationMinutes) ? PASS : FAIL;
+ ExitProcess(result);
+ return result;
+ }
+
+ // Child test process arguments: <processPath> <parentPid> <testFunctionName> [stress]
+
+ g_isParent = false;
+
+ // Get parent process' ID from argument
+ if (test_sscanf(argv[1], "%u", &g_parentPid) != 1)
+ {
+ ExitProcess(FAIL);
+ return FAIL;
+ }
+
+ if (argc >= 4 && test_strcmp(argv[3], "stress") == 0)
+ {
+ g_isStress = true;
+ }
+
+ if (test_strcmp(argv[2], "MutualExclusionTests_Child") == 0)
+ {
+ MutualExclusionTests_Child();
+ }
+ else if (test_strcmp(argv[2], "LifetimeTests_Child") == 0)
+ {
+ LifetimeTests_Child();
+ }
+ else if (test_strcmp(argv[2], "AbandonTests_Child_GracefulExit_Close") == 0)
+ {
+ AbandonTests_Child_GracefulExit_Close();
+ }
+ else if (test_strcmp(argv[2], "AbandonTests_Child_GracefulExit_NoClose") == 0)
+ {
+ AbandonTests_Child_GracefulExit_NoClose();
+ }
+ else if (test_strcmp(argv[2], "AbandonTests_Child_AbruptExit") == 0)
+ {
+ AbandonTests_Child_AbruptExit();
+ }
+ else if (test_strcmp(argv[2], "AbandonTests_Child_TryLock") == 0)
+ {
+ AbandonTests_Child_TryLock();
+ }
+ ExitProcess(PASS);
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/NamedMutex/test1/nopal.cpp b/src/pal/tests/palsuite/threading/NamedMutex/test1/nopal.cpp
new file mode 100644
index 0000000000..46a5d87341
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/NamedMutex/test1/nopal.cpp
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// Contains wrappers for functions whose required headers conflict with the PAL
+
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define _countof(a) (sizeof(a) / sizeof(a[0]))
+
+#define PAGE_SIZE (4096)
+
+auto test_strcpy = strcpy;
+auto test_strcmp = strcmp;
+auto test_strlen = strlen;
+auto test_sprintf = sprintf;
+auto test_sscanf = sscanf;
+auto test_close = close;
+auto test_unlink = unlink;
+
+unsigned int test_getpid()
+{
+ return getpid();
+}
+
+int test_kill(unsigned int pid)
+{
+ return kill(pid, SIGKILL);
+}
+
+bool TestFileExists(const char *path)
+{
+ int fd = open(path, O_RDWR);
+ if (fd == -1)
+ return false;
+ close(fd);
+ return true;
+}
+
+bool WriteHeaderInfo(const char *path, char sharedMemoryType, char version, int *fdRef)
+{
+ int fd = open(path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ if (fd == -1)
+ return false;
+ *fdRef = fd;
+ if (ftruncate(fd, PAGE_SIZE) != 0)
+ return false;
+ if (lseek(fd, 0, SEEK_SET) != 0)
+ return false;
+
+ // See SharedMemorySharedDataHeader for format
+ char buffer[] = {sharedMemoryType, version};
+ if (write(fd, buffer, _countof(buffer)) != _countof(buffer))
+ return false;
+
+ return flock(fd, LOCK_SH | LOCK_NB) == 0;
+}
diff --git a/src/pal/tests/palsuite/threading/NamedMutex/test1/testinfo.dat b/src/pal/tests/palsuite/threading/NamedMutex/test1/testinfo.dat
new file mode 100644
index 0000000000..e3090093ea
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/NamedMutex/test1/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = Named mutex
+Name = Tests for named mutexes
+TYPE = DEFAULT
+EXE1 = namedmutex
+Description
+= These test cases test named mutexes, including positive
+= and negative cases, cross-thread and cross-process, mutual
+= exclusion, abandon detection, etc.
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/CMakeLists.txt b/src/pal/tests/palsuite/threading/OpenEventW/CMakeLists.txt
new file mode 100644
index 0000000000..8083faf655
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/CMakeLists.txt
@@ -0,0 +1,8 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+add_subdirectory(test4)
+add_subdirectory(test5)
+
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/OpenEventW/test1/CMakeLists.txt
new file mode 100644
index 0000000000..2bb61e3044
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_openeventw_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_openeventw_test1 coreclrpal)
+
+target_link_libraries(paltest_openeventw_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test1/test1.c b/src/pal/tests/palsuite/threading/OpenEventW/test1/test1.c
new file mode 100644
index 0000000000..9dcb3a4a68
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test1/test1.c
@@ -0,0 +1,134 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================================
+**
+** Source: test1.c
+**
+** Purpose: Test for OpenEventW. This test creates an event,
+** opens a handle to the same event, then waits on both handles
+** in both a signalled and non-signalled state to verify they're.
+** pointing to the same event object.
+**
+**
+**==========================================================================*/
+#include <palsuite.h>
+
+int __cdecl main(int argc, char **argv)
+{
+ BOOL bRet = FAIL;
+ DWORD dwRet;
+ HANDLE hEvent;
+ HANDLE hOpenEvent;
+ WCHAR theName[] = {'E','v','e','n','t','\0'};
+ LPCWSTR lpName = theName;
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* Create an event (with a 0 intial state!) and ensure that the
+ HANDLE is valid
+ */
+ hEvent = CreateEventW( NULL, TRUE, FALSE, lpName );
+ if( hEvent == NULL )
+ {
+ Fail( "ERROR:%lu:CreateEvent call failed\n", GetLastError() );
+ }
+
+
+ /* Call OpenEventW to get another HANDLE on
+ this event. Ensure the HANDLE is valid.
+ */
+ hOpenEvent = OpenEventW( EVENT_ALL_ACCESS, TRUE, lpName );
+ if( hOpenEvent == NULL )
+ {
+ Trace( "ERROR:%lu:OpenEventW call failed\n", GetLastError() );
+ goto cleanup2;
+ }
+
+ /* wait on the original event to verify that it's not signalled */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ Trace( "ERROR:WaitForSingleObject returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ goto cleanup;
+ }
+
+ /* wait on the opened event to verify that it's not signalled either */
+ dwRet = WaitForSingleObject( hOpenEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ Trace( "ERROR:WaitForSingleObject returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ goto cleanup;
+ }
+
+
+ /* Set this opened HANDLE */
+ if( ! SetEvent( hOpenEvent ) )
+ {
+ Trace( "ERROR:%lu:SetEvent call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* wait on the original event to verify that it's signalled */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ goto cleanup;
+ }
+
+ /* wait on the opened event to verify that it's signalled too */
+ dwRet = WaitForSingleObject( hOpenEvent, 0 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ goto cleanup;
+ }
+
+ /* success if we get here */
+ bRet = PASS;
+
+cleanup:
+ /* close the opened handle */
+ if( ! CloseHandle( hOpenEvent ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle call failed\n", GetLastError() );
+ bRet = FAIL;
+ }
+
+cleanup2:
+ /* close the original event handle */
+ if( ! CloseHandle( hEvent ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle call failed\n", GetLastError() );
+ bRet = FAIL;
+ }
+
+ /* check for failure */
+ if( bRet == FAIL )
+ {
+ Fail( "test failed\n" );
+ }
+
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* return success */
+ return ( PASS );
+
+}
+
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test1/testinfo.dat b/src/pal/tests/palsuite/threading/OpenEventW/test1/testinfo.dat
new file mode 100644
index 0000000000..cc9be71042
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test1/testinfo.dat
@@ -0,0 +1,15 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = OpenEventW
+Name = Positive Test for OpenEventW
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Purpose: Test for OpenEventW. This test creates an event,
+= opens a handle to the same event, then waits on both handles
+= in both a signalled and non-signalled state to verify they're
+= pointing to the same event object.
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/OpenEventW/test2/CMakeLists.txt
new file mode 100644
index 0000000000..48fa1f2c4b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_openeventw_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_openeventw_test2 coreclrpal)
+
+target_link_libraries(paltest_openeventw_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test2/test2.c b/src/pal/tests/palsuite/threading/OpenEventW/test2/test2.c
new file mode 100644
index 0000000000..9cbf872b95
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test2/test2.c
@@ -0,0 +1,194 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test2.c
+**
+** Purpose: Positive test for OpenEventW.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateEvent
+** CloseHandle
+** WaitForSingleObject
+**
+** Purpose:
+**
+** Test to ensure proper operation of the OpenEventW()
+** API by creating a new named event and verifying that
+** it can be used interchangeably by setting the event
+** with the original handle and waiting on it with the
+** new one, then resetting it with the new one and waiting
+** on it with the original one.
+**
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ BOOL ret = FAIL;
+ DWORD dwRet = 0;
+ HANDLE hEvent = NULL;
+ HANDLE hTestEvent = NULL;
+ LPSECURITY_ATTRIBUTES lpEventAttributes = NULL;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = FALSE;
+ WCHAR wcName[] = {'W','o','o','B','a','b','y','\0'};
+ LPWSTR lpName = wcName;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* create an event which we can use with SetEvent */
+ hEvent = CreateEventW( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ lpName );
+
+ if( hEvent == NULL )
+ {
+ /* ERROR */
+ Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ }
+
+ /* open a new handle to our event */
+ hTestEvent = OpenEventW(EVENT_ALL_ACCESS, /* we want all rights */
+ FALSE, /* no inherit */
+ lpName );
+
+ if( hTestEvent == NULL )
+ {
+ /* ERROR */
+ Trace( "ERROR:%lu:OpenEventW() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* verify that the event isn't signalled yet by waiting on both */
+ /* handles to the event object */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ goto cleanup;
+ }
+
+ dwRet = WaitForSingleObject( hTestEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ goto cleanup;
+ }
+
+
+ /* set the event using the original handle */
+ if( ! SetEvent( hEvent ) )
+ {
+ /* ERROR */
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* verify that the event is signalled by waiting on both handles */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ goto cleanup;
+ }
+
+ dwRet = WaitForSingleObject( hTestEvent, 0 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ goto cleanup;
+ }
+
+ /* reset the event using the new handle */
+ if( ! ResetEvent( hTestEvent ) )
+ {
+ /* ERROR */
+ Trace( "ERROR:%lu:ResetEvent() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* verify that the event isn't signalled by waiting on both handles */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ goto cleanup;
+ }
+
+ dwRet = WaitForSingleObject( hTestEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ goto cleanup;
+ }
+
+
+ /* test was successful */
+ ret = PASS;
+
+
+cleanup:
+ /* close the new event handle */
+ if( hTestEvent != NULL )
+ {
+ if( ! CloseHandle( hTestEvent ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ }
+
+ /* close the original event handle */
+ if( ! CloseHandle( hEvent ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+
+ /* failure message */
+ if( ret != PASS )
+ {
+ Fail( "Test failed\n" );
+ }
+
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success or failure */
+ return ret;
+}
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test2/testinfo.dat b/src/pal/tests/palsuite/threading/OpenEventW/test2/testinfo.dat
new file mode 100644
index 0000000000..ad3f22eea0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test2/testinfo.dat
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = OpenEventW
+Name = Positive test for OpenEventW
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Test to ensure proper operation of the OpenEventW()
+= API by creating a new named event and verifying that
+= it can be used interchangeably by setting the event
+= with the original handle and waiting on it with the
+= new one, then resetting it with the new one and waiting
+= on it with the original one.
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/OpenEventW/test3/CMakeLists.txt
new file mode 100644
index 0000000000..433a3a255c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test3/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ test3.c
+)
+
+add_executable(paltest_openeventw_test3
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_openeventw_test3 coreclrpal)
+
+target_link_libraries(paltest_openeventw_test3
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ childprocess.c
+)
+
+add_executable(paltest_openeventw_test3_child
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_openeventw_test3_child coreclrpal)
+
+target_link_libraries(paltest_openeventw_test3_child
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test3/childprocess.c b/src/pal/tests/palsuite/threading/OpenEventW/test3/childprocess.c
new file mode 100644
index 0000000000..b5149e006f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test3/childprocess.c
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: childprocess.c
+**
+** Purpose: Test to ensure that OpenEventW() works when
+** opening an event created by another process. The test
+** program launches this program as a child, which creates
+** a named, initially-unset event. The child waits up to
+** 10 seconds for the parent process to open that event
+** and set it, and returns PASS if the event was set or FAIL
+** otherwise. The parent process checks the return value
+** from the child to verify that the opened event was
+** properly used across processes.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateEventW
+** WaitForSingleObject
+** CloseHandle
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+int __cdecl main( int argc, char **argv )
+{
+ /* local variables */
+ HANDLE hEvent = NULL;
+ WCHAR wcName[] = {'P','A','L','R','o','c','k','s','\0'};
+ LPWSTR lpName = wcName;
+
+ int result = PASS;
+
+ /* initialize the PAL */
+ if( PAL_Initialize(argc, argv) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* open a handle to the event created in the child process */
+ hEvent = OpenEventW( EVENT_ALL_ACCESS, /* we want all rights */
+ FALSE, /* no inherit */
+ lpName );
+
+ if( hEvent == NULL )
+ {
+ /* ERROR */
+ Trace( "ERROR:%lu:OpenEventW() call failed\n", GetLastError() );
+ result = FAIL;
+ goto parentwait;
+ }
+
+ /* set the event -- should take effect in the child process */
+ if( ! SetEvent( hEvent ) )
+ {
+ /* ERROR */
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ result = FAIL;
+ }
+
+parentwait:
+ /* close the event handle */
+ if( ! CloseHandle( hEvent ) )
+ {
+ /* ERROR */
+ Fail( "ERROR:%lu:CloseHandle() call failed in child\n",
+ GetLastError());
+ }
+
+ /* terminate the PAL */
+ PAL_TerminateEx(result);
+
+ /* return success or failure */
+ return result;
+}
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test3/test3.c b/src/pal/tests/palsuite/threading/OpenEventW/test3/test3.c
new file mode 100644
index 0000000000..10a678a107
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test3/test3.c
@@ -0,0 +1,187 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test3.c
+**
+** Purpose: Test to ensure that OpenEventW() works when
+** opening an event created by another process. This test
+** program launches a child process which creates a
+** named, initially-unset event. The child waits up to
+** 10 seconds for the parent process to open that event
+** and set it, and returns PASS if the event was set or FAIL
+** otherwise. The parent process checks the return value
+** from the child to verify that the opened event was
+** properly used across processes.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** Fail
+** ZeroMemory
+** GetCurrentDirectoryW
+** CreateProcessW
+** WaitForSingleObject
+** GetExitCodeProcess
+** GetLastError
+** strlen
+** strncpy
+**
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+#define TIMEOUT 60000
+
+int __cdecl main( int argc, char **argv )
+{
+ BOOL ret = FAIL;
+ LPSECURITY_ATTRIBUTES lpEventAttributes = NULL;
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ DWORD dwExitCode;
+
+ DWORD dwRet = 0;
+ HANDLE hEvent = NULL;
+ WCHAR wcName[] = {'P','A','L','R','o','c','k','s','\0'};
+ LPWSTR lpName = wcName;
+ char lpCommandLine[MAX_PATH] = "";
+
+ /* initialize the PAL */
+ if( PAL_Initialize(argc, argv) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* zero our process and startup info structures */
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof( si );
+ ZeroMemory( &pi, sizeof(pi) );
+
+ /* create an event which we can use with SetEvent */
+ hEvent = CreateEventW( lpEventAttributes,
+ TRUE, /* manual reset */
+ FALSE, /* unsignalled */
+ lpName );
+
+ if( hEvent == NULL )
+ {
+ /* ERROR */
+ Fail( "ERROR:%lu:CreateEventW() call failed in child\n",
+ GetLastError());
+ }
+
+ ZeroMemory( lpCommandLine, MAX_PATH );
+ if ( _snprintf( lpCommandLine, MAX_PATH-1, "childprocess ") < 0 )
+ {
+ Fail ("Error: Insufficient lpCommandline for\n");
+ }
+
+ /* launch the child process */
+ if( !CreateProcess( NULL, /* module name to execute */
+ lpCommandLine, /* command line */
+ NULL, /* process handle not */
+ /* inheritable */
+ NULL, /* thread handle not */
+ /* inheritable */
+ FALSE, /* handle inheritance */
+ CREATE_NEW_CONSOLE, /* dwCreationFlags */
+ NULL, /* use parent's environment */
+ NULL, /* use parent's starting */
+ /* directory */
+ &si, /* startup info struct */
+ &pi ) /* process info struct */
+ )
+ {
+ Fail( "ERROR:%lu:CreateProcess call failed\n",
+ GetLastError() );
+ }
+
+ /* verify that the event is signalled by the child process */
+ dwRet = WaitForSingleObject( hEvent, TIMEOUT );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ ret = FAIL;
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+
+ goto cleanup;
+
+ if( !CloseHandle( hEvent ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed in child\n",
+ GetLastError());
+ }
+ goto cleanup;
+ }
+
+ /* wait for the child process to complete */
+ dwRet = WaitForSingleObject ( pi.hProcess, TIMEOUT );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ ret = FAIL;
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected %lu\n",
+ dwRet,
+ WAIT_OBJECT_0 );
+ goto cleanup;
+ }
+
+ /* check the exit code from the process */
+ if( ! GetExitCodeProcess( pi.hProcess, &dwExitCode ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:GetExitCodeProcess call failed\n",
+ GetLastError() );
+ goto cleanup;
+ }
+
+ /* check for success */
+ ret = (dwExitCode == PASS) ? PASS : FAIL;
+
+cleanup:
+ if( hEvent != NULL )
+ {
+ if( ! CloseHandle ( hEvent ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle call failed on event handle\n",
+ GetLastError() );
+ ret = FAIL;
+ }
+ }
+
+
+ /* close process and thread handle */
+ if( ! CloseHandle ( pi.hProcess ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle call failed on process handle\n",
+ GetLastError() );
+ ret = FAIL;
+ }
+
+ if( ! CloseHandle ( pi.hThread ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle call failed on thread handle\n",
+ GetLastError() );
+ ret = FAIL;
+ }
+
+ /* output a convenient error message and exit if we failed */
+ if( ret == FAIL )
+ {
+ Fail( "test failed\n" );
+ }
+
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* return success */
+ return ret;
+}
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test3/testinfo.dat b/src/pal/tests/palsuite/threading/OpenEventW/test3/testinfo.dat
new file mode 100644
index 0000000000..96b2c06644
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test3/testinfo.dat
@@ -0,0 +1,21 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = OpenEventW
+Name = Test for OpenEventW
+TYPE = DEFAULT
+EXE1 = test3
+EXE2 = childprocess
+Description
+= Purpose: Test to ensure that OpenEventW() works when
+= opening an event created by another process. This test
+= program launches a child process which creates a
+= named, initially-unset event. The child waits up to
+= 10 seconds for the parent process to open that event
+= and set it, and returns PASS if the event was set or FAIL
+= otherwise. The parent process checks the return value
+= from the child to verify that the opened event was
+= properly used across processes.
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test4/CMakeLists.txt b/src/pal/tests/palsuite/threading/OpenEventW/test4/CMakeLists.txt
new file mode 100644
index 0000000000..ff17fea23b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test4/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test4.c
+)
+
+add_executable(paltest_openeventw_test4
+ ${SOURCES}
+)
+
+add_dependencies(paltest_openeventw_test4 coreclrpal)
+
+target_link_libraries(paltest_openeventw_test4
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test4/test4.c b/src/pal/tests/palsuite/threading/OpenEventW/test4/test4.c
new file mode 100644
index 0000000000..ae657a0511
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test4/test4.c
@@ -0,0 +1,112 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test4.c
+**
+** Purpose: Positive test for OpenEventW.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateEvent
+** CloseHandle
+** WaitForSingleObject
+**
+** Purpose:
+**
+** Test to ensure proper operation of the OpenEventW()
+** API by trying to open an event with a name that is
+** already taken by a non-event object.
+**
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ BOOL bRet = PASS;
+ DWORD dwLastError = 0;
+ HANDLE hMutex = NULL;
+ HANDLE hTestEvent = NULL;
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL;
+ BOOL bInitialState = TRUE;
+ WCHAR wcName[] = {'I','m','A','M','u','t','e','x','\0'};
+ LPWSTR lpName = wcName;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* create a mutex object */
+ hMutex = CreateMutexW( lpSecurityAttributes,
+ bInitialState,
+ lpName );
+
+ if( hMutex == NULL )
+ {
+ /* ERROR */
+ Fail( "ERROR:%lu:CreateMutexW() call failed\n", GetLastError() );
+ }
+
+ /* open a new handle to our event */
+ hTestEvent = OpenEventW(EVENT_ALL_ACCESS, /* we want all rights */
+ FALSE, /* no inherit */
+ lpName );
+
+ if( hTestEvent != NULL )
+ {
+ /* ERROR */
+ Trace( "ERROR:OpenEventW() call succeeded against a named "
+ "mutex, should have returned NULL\n" );
+ if( ! CloseHandle( hTestEvent ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed \n", GetLastError() );
+ }
+ bRet = FAIL;
+ }
+ else
+ {
+ dwLastError = GetLastError();
+ if( dwLastError != ERROR_INVALID_HANDLE )
+ {
+ /* ERROR */
+ Trace( "ERROR:OpenEventW() call failed against a named "
+ "mutex, but returned an unexpected result: %lu\n",
+ dwLastError );
+ bRet = FAIL;
+ }
+ }
+
+
+ /* close the mutex handle */
+ if( ! CloseHandle( hMutex ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed \n", GetLastError() );
+ bRet = FAIL;
+ }
+
+
+ /* fail here if we weren't successful */
+ if( bRet == FAIL )
+ {
+ Fail( "" );
+ }
+
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success or failure */
+ return PASS;
+}
+
+
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test4/testinfo.dat b/src/pal/tests/palsuite/threading/OpenEventW/test4/testinfo.dat
new file mode 100644
index 0000000000..1b3f2d83c6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test4/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = OpenEventW
+Name = Negative test for OpenEventW
+TYPE = DEFAULT
+EXE1 = test4
+Description
+= Test to ensure proper operation of the OpenEventW()
+= API by trying to open an event with a name that is
+= already taken by a non-event object.
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test5/CMakeLists.txt b/src/pal/tests/palsuite/threading/OpenEventW/test5/CMakeLists.txt
new file mode 100644
index 0000000000..b78d7fbfc0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test5/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test5.c
+)
+
+add_executable(paltest_openeventw_test5
+ ${SOURCES}
+)
+
+add_dependencies(paltest_openeventw_test5 coreclrpal)
+
+target_link_libraries(paltest_openeventw_test5
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test5/test5.c b/src/pal/tests/palsuite/threading/OpenEventW/test5/test5.c
new file mode 100644
index 0000000000..43b585765c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test5/test5.c
@@ -0,0 +1,197 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test5.c
+**
+** Purpose: Positive test for OpenEventW.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateEvent
+** CloseHandle
+** WaitForSingleObject
+**
+** Purpose:
+**
+** Test to ensure proper operation of the OpenEventW()
+** API by creating a new named event with CreateEventA()
+** and verifying that it can be opened with OpenEventW().
+** It should be possible to use the event handles
+** interchangeably, we test by setting the event with the
+** original handle and waiting on it with the new one,
+** then resetting it with the new one and waiting
+** on it with the original one.
+**
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ BOOL ret = FAIL;
+ DWORD dwRet = 0;
+ HANDLE hEvent = NULL;
+ HANDLE hTestEvent = NULL;
+ LPSECURITY_ATTRIBUTES lpEventAttributes = NULL;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = FALSE;
+ LPSTR lpNameA = "ShakeIt";
+ WCHAR wcName[] = {'S','h','a','k','e','I','t','\0'};
+ LPWSTR lpNameW = wcName;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* create an event which we can use with SetEvent */
+ hEvent = CreateEventA( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ lpNameA );
+
+ if( hEvent == NULL )
+ {
+ /* ERROR */
+ Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ }
+
+ /* open a new handle to our event */
+ hTestEvent = OpenEventW(EVENT_ALL_ACCESS, /* we want all rights */
+ FALSE, /* no inherit */
+ lpNameW );
+
+ if( hTestEvent == NULL )
+ {
+ /* ERROR */
+ Trace( "ERROR:%lu:OpenEventW() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* verify that the event isn't signalled yet by waiting on both */
+ /* handles to the event object */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ goto cleanup;
+ }
+
+ dwRet = WaitForSingleObject( hTestEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ goto cleanup;
+ }
+
+
+ /* set the event using the original handle */
+ if( ! SetEvent( hEvent ) )
+ {
+ /* ERROR */
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* verify that the event is signalled by waiting on both handles */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ goto cleanup;
+ }
+
+ dwRet = WaitForSingleObject( hTestEvent, 0 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ goto cleanup;
+ }
+
+ /* reset the event using the new handle */
+ if( ! ResetEvent( hTestEvent ) )
+ {
+ /* ERROR */
+ Trace( "ERROR:%lu:ResetEvent() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* verify that the event isn't signalled by waiting on both handles */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ goto cleanup;
+ }
+
+ dwRet = WaitForSingleObject( hTestEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ goto cleanup;
+ }
+
+
+ /* test was successful */
+ ret = PASS;
+
+
+cleanup:
+ /* close the new event handle */
+ if( hTestEvent != NULL )
+ {
+ if( ! CloseHandle( hTestEvent ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ }
+
+ /* close the original event handle */
+ if( ! CloseHandle( hEvent ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+
+ /* failure message */
+ if( ret != PASS )
+ {
+ Fail( "Test failed\n" );
+ }
+
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success or failure */
+ return ret;
+}
diff --git a/src/pal/tests/palsuite/threading/OpenEventW/test5/testinfo.dat b/src/pal/tests/palsuite/threading/OpenEventW/test5/testinfo.dat
new file mode 100644
index 0000000000..f5af943a7c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test5/testinfo.dat
@@ -0,0 +1,19 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = OpenEventW
+Name = Positive test for OpenEventW
+TYPE = DEFAULT
+EXE1 = test5
+Description
+= Test to ensure proper operation of the OpenEventW()
+= API by creating a new named event with CreateEventA()
+= and verifying that it can be opened with OpenEventW().
+= It should be possible to use the event handles
+= interchangeably, we test by setting the event with the
+= original handle and waiting on it with the new one,
+= then resetting it with the new one and waiting
+= on it with the original one.
diff --git a/src/pal/tests/palsuite/threading/OpenProcess/CMakeLists.txt b/src/pal/tests/palsuite/threading/OpenProcess/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenProcess/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/OpenProcess/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/OpenProcess/test1/CMakeLists.txt
new file mode 100644
index 0000000000..26d30547a9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenProcess/test1/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ test1.c
+)
+
+add_executable(paltest_openprocess_test1
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_openprocess_test1 coreclrpal)
+
+target_link_libraries(paltest_openprocess_test1
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ childProcess.c
+)
+
+add_executable(paltest_openprocess_test1_child
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_openprocess_test1_child coreclrpal)
+
+target_link_libraries(paltest_openprocess_test1_child
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.c b/src/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.c
new file mode 100644
index 0000000000..9ef07433fd
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.c
@@ -0,0 +1,75 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: childprocess.c
+**
+** Purpose: Test to ensure OpenProcess works properly.
+** All this program does is return a predefined value.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateMutexW
+** WaitForSingleObject
+** CloseHandle
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+#include "myexitcode.h"
+
+
+int __cdecl main( int argc, char **argv )
+{
+ HANDLE hMutex;
+ WCHAR wszMutexName[] = { 'T','E','S','T','1','\0' };
+ DWORD dwRet;
+ int i;
+
+ /* initialize the PAL */
+ if( PAL_Initialize(argc, argv) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* open a mutex to synchronize with the parent process */
+ hMutex = CreateMutexW( NULL, FALSE, wszMutexName );
+ if( hMutex == NULL )
+ {
+ Fail( "ERROR:%lu:CreateMutex() call failed\r\n", GetLastError() );
+ }
+
+ /* acquire the mutex lock */
+ dwRet = WaitForSingleObject( hMutex, 10000 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0",
+ dwRet );
+ if( ! CloseHandle( hMutex ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ Fail( "test failed\n" );
+ }
+
+
+ /* simulate some activity */
+ for( i=0; i<50000; i++ )
+ ;
+
+ /* close our mutex handle */
+ if( ! CloseHandle( hMutex ) )
+ {
+ Fail( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* return the predefined exit code */
+ return TEST_EXIT_CODE;
+}
diff --git a/src/pal/tests/palsuite/threading/OpenProcess/test1/myexitcode.h b/src/pal/tests/palsuite/threading/OpenProcess/test1/myexitcode.h
new file mode 100644
index 0000000000..66b8f43a97
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenProcess/test1/myexitcode.h
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================================
+**
+** Source: myexitcode.h
+**
+** Purpose: Define an exit code.
+**
+**
+**==========================================================================*/
+
+#define TEST_EXIT_CODE 317
diff --git a/src/pal/tests/palsuite/threading/OpenProcess/test1/test1.c b/src/pal/tests/palsuite/threading/OpenProcess/test1/test1.c
new file mode 100644
index 0000000000..d0f9019646
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenProcess/test1/test1.c
@@ -0,0 +1,282 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test1.c
+**
+** Purpose: Test to ensure OpenProcess works properly.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** Fail
+** ZeroMemory
+** GetCurrentDirectoryW
+** CreateProcessW
+** WaitForSingleObject
+** CreateMutexW
+** ReleaseMutex
+** CloseHandle
+** GetLastError
+** strlen
+** strncpy
+**
+**
+**===========================================================================*/
+#include <palsuite.h>
+#include "myexitcode.h"
+
+
+static const char* rgchPathDelim = "\\";
+
+
+int
+mkAbsoluteFilename( LPSTR dirName,
+ DWORD dwDirLength,
+ LPCSTR fileName,
+ DWORD dwFileLength,
+ LPSTR absPathName )
+{
+ DWORD sizeDN, sizeFN, sizeAPN;
+
+ sizeDN = strlen( dirName );
+ sizeFN = strlen( fileName );
+ sizeAPN = (sizeDN + 1 + sizeFN + 1);
+
+ /* ensure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */
+ if( sizeAPN > _MAX_PATH )
+ {
+ return ( 0 );
+ }
+
+ strncpy( absPathName, dirName, dwDirLength +1 );
+ strncpy( absPathName, rgchPathDelim, 2 );
+ strncpy( absPathName, fileName, dwFileLength +1 );
+
+ return (sizeAPN);
+
+}
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ const char* rgchChildFile = "childprocess";
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ DWORD dwError;
+ DWORD dwExitCode;
+ DWORD dwFileLength;
+ DWORD dwDirLength;
+ DWORD dwSize;
+ DWORD dwRet;
+
+ HANDLE hMutex;
+ HANDLE hChildProcess;
+
+ char rgchDirName[_MAX_DIR];
+ char absPathBuf[_MAX_PATH];
+ char* rgchAbsPathName;
+
+ BOOL ret = FAIL;
+ BOOL bChildDone = FALSE;
+ WCHAR wszMutexName[] = { 'T','E','S','T','1','\0' };
+
+ /* initialize the PAL */
+ if( PAL_Initialize(argc, argv) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* create a mutex to synchronize with the child process */
+ hMutex = CreateMutexW( NULL, TRUE, wszMutexName );
+ if( hMutex == NULL )
+ {
+ Fail( "ERROR:%lu:CreateMutex() call failed\r\n", GetLastError() );
+ }
+
+ /* zero our process and startup info structures */
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof( si );
+ ZeroMemory( &pi, sizeof(pi) );
+
+ /* build the absolute path to the child process */
+ rgchAbsPathName = &absPathBuf[0];
+ dwFileLength = strlen( rgchChildFile );
+
+ dwDirLength = GetCurrentDirectory( _MAX_PATH, rgchDirName );
+ if( dwDirLength == 0 )
+ {
+ dwError = GetLastError();
+ if( ReleaseMutex( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
+ }
+ if( CloseHandle( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ Fail( "GetCurrentDirectory call failed with error code %d\n",
+ dwError );
+ }
+
+ dwSize = mkAbsoluteFilename( rgchDirName,
+ dwDirLength,
+ rgchChildFile,
+ dwFileLength,
+ rgchAbsPathName );
+ if( dwSize == 0 )
+ {
+ if( ReleaseMutex( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
+ }
+ if( CloseHandle( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ Fail( "Palsuite Code: mkAbsoluteFilename() call failed. Could ",
+ "not build absolute path name to file\n. Exiting.\n" );
+ }
+
+ /* launch the child process */
+ if( !CreateProcess( NULL, /* module name to execute */
+ rgchAbsPathName, /* command line */
+ NULL, /* process handle not */
+ /* inheritable */
+ NULL, /* thread handle not */
+ /*inheritable */
+ FALSE, /* handle inheritance */
+ CREATE_NEW_CONSOLE, /* dwCreationFlags */
+ NULL, /* use parent's environment */
+ NULL, /* use parent's starting */
+ /* directory */
+ &si, /* startup info struct */
+ &pi ) /* process info struct */
+ )
+ {
+ dwError = GetLastError();
+ if( ReleaseMutex( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
+ }
+ if( CloseHandle( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ Fail( "CreateProcess call failed with error code %d\n",
+ dwError );
+ }
+
+ /* open another handle to the child process */
+ hChildProcess = OpenProcess( PROCESS_ALL_ACCESS, /* access */
+ FALSE, /* inheritable */
+ pi.dwProcessId /* process id */
+ );
+ if( hChildProcess == NULL )
+ {
+ dwError = GetLastError();
+ if( ReleaseMutex( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
+ }
+ Trace( "ERROR:%lu:OpenProcess call failed\n", dwError );
+ goto cleanup2;
+ }
+
+ /* release the mutex so the child can proceed */
+ if( ReleaseMutex( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* wait for the child process to complete, using the new handle */
+ dwRet = WaitForSingleObject( hChildProcess, 10000 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject call returned %lu, "
+ "expected WAIT_OBJECT_0",
+ dwRet );
+ goto cleanup;
+ }
+
+ /* remember that we waited until the child was finished */
+ bChildDone = TRUE;
+
+ /* check the exit code from the process -- this is a bit of an */
+ /* extra verification that we opened the correct process handle */
+ if( ! GetExitCodeProcess( hChildProcess, &dwExitCode ) )
+ {
+ Trace( "ERROR:%lu:GetExitCodeProcess call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* verification */
+ if( (dwExitCode & 0xFF) != (TEST_EXIT_CODE & 0xFF) )
+ {
+ Trace( "GetExitCodeProcess returned an incorrect exit code %d, "
+ "expected value is %d\n",
+ (dwExitCode & 0xFF),
+ (TEST_EXIT_CODE & 0xFF));
+ goto cleanup;
+ }
+
+ /* success if we get here */
+ ret = PASS;
+
+
+cleanup:
+ /* wait on the child process to complete if necessary */
+ if( ! bChildDone )
+ {
+ dwRet = WaitForSingleObject( hChildProcess, 10000 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject call returned %lu, "
+ "expected WAIT_OBJECT_0",
+ dwRet );
+ ret = FAIL;
+ }
+ }
+
+ /* close all our handles */
+ if( CloseHandle ( hChildProcess ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ ret = FAIL;
+ }
+
+cleanup2:
+ if( CloseHandle ( pi.hProcess ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ ret = FAIL;
+ }
+ if( CloseHandle ( pi.hThread ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ ret = FAIL;
+ }
+ if( CloseHandle( hMutex ) == 0 )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ ret = FAIL;
+ }
+
+ if( ret == FAIL )
+ {
+ Fail( "test failed\n" );
+ }
+
+
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/OpenProcess/test1/testinfo.dat b/src/pal/tests/palsuite/threading/OpenProcess/test1/testinfo.dat
new file mode 100644
index 0000000000..dd6b2c0ffe
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenProcess/test1/testinfo.dat
@@ -0,0 +1,19 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = OpenProcess
+Name = Test for OpenProcess
+TYPE = DEFAULT
+EXE1 = test1
+EXE2 = childprocess
+Description
+= Test to ensure proper operation of the OpenProcess API.
+= The test launches a trivial child process, then opens
+= a handle to it using OpenProcess. It uses that handle
+= to wait for the child process to terminate, and then
+= checks the exit code of the child process in order to
+= verify that it was in fact a handle to the correct
+= process.
diff --git a/src/pal/tests/palsuite/threading/QueryThreadCycleTime/CMakeLists.txt b/src/pal/tests/palsuite/threading/QueryThreadCycleTime/CMakeLists.txt
new file mode 100644
index 0000000000..5e1ef7f28b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueryThreadCycleTime/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
diff --git a/src/pal/tests/palsuite/threading/QueryThreadCycleTime/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/QueryThreadCycleTime/test1/CMakeLists.txt
new file mode 100644
index 0000000000..ad3eec1a45
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueryThreadCycleTime/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_querythreadcycletime_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_querythreadcycletime_test1 coreclrpal)
+
+target_link_libraries(paltest_querythreadcycletime_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/QueryThreadCycleTime/test1/test1.c b/src/pal/tests/palsuite/threading/QueryThreadCycleTime/test1/test1.c
new file mode 100644
index 0000000000..54f0116bd0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueryThreadCycleTime/test1/test1.c
@@ -0,0 +1,98 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source:
+**
+** Source : test1.c
+**
+** Purpose: Test for QueryThreadCycleTime() function
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+int __cdecl main(int argc, char *argv[]) {
+ int ret = FAIL;
+
+ //Test is failing unreliably, so for now we always return pass.
+ if (TRUE){
+ ret = PASS;
+ goto EXIT;
+ }
+
+ LONG64 Actual, Expected, Delta = 850000000;
+ Actual = 0;
+ Expected = 0;
+ const LONG64 MSEC_TO_NSEC = 1000000;
+
+ /*
+ * Initialize the PAL and return FAILURE if this fails
+ */
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ HANDLE cThread = GetCurrentThread();
+
+ int i;
+ /* Take 2000 tiny measurements */
+ for (i = 0; i < 2000; i++){
+ ULONG64 FirstCount, SecondCount;
+ LONG64 Init;
+
+ Sleep(1);
+
+ /* Grab a FirstCount, then loop for a bit to make the clock increase */
+ if (!QueryThreadCycleTime(cThread, (PULONG64)&FirstCount))
+ {
+ Fail("ERROR: QueryThreadCycleTime returned failure.\n");
+ }
+
+ LONG64 x;
+ /* Init is in milliseconds, so we will convert later */
+ Init = (LONG64)GetTickCount();
+ x = Init + 3;
+ volatile int counter;
+ do {
+ for (counter = 0; counter < 100000; counter++)
+ {
+ // spin to consume CPU time
+ }
+
+ } while (x > GetTickCount());
+ Expected += (GetTickCount() - Init) * MSEC_TO_NSEC;
+ /* Get a second count */
+ if (!QueryThreadCycleTime(cThread, (PULONG64)&SecondCount))
+ {
+ Fail("ERROR: QueryThreadCycleTime returned failure.\n");
+ }
+
+ LONG64 trial = (LONG64)SecondCount - (LONG64)FirstCount;
+ if (trial < 0){
+ printf("Negative value %llu measured", trial);
+ }
+ Actual += (trial);
+
+ }
+
+
+
+ if(labs(Expected - Actual) > Delta)
+ {
+ Fail("ERROR: The measured time (%llu millisecs) was not within Delta %llu "
+ "of the expected time (%llu millisecs).\n",
+ (Actual / MSEC_TO_NSEC), (Delta / MSEC_TO_NSEC), (Expected / MSEC_TO_NSEC));
+ }
+ //printf("%llu, %llu\n", Expected, Actual);
+ PAL_Terminate();
+ ret = PASS;
+
+EXIT:
+ return ret;
+}
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/CMakeLists.txt b/src/pal/tests/palsuite/threading/QueueUserAPC/CMakeLists.txt
new file mode 100644
index 0000000000..19ee487a6a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+add_subdirectory(test4)
+add_subdirectory(test5)
+add_subdirectory(test6)
+add_subdirectory(test7)
+
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/QueueUserAPC/test1/CMakeLists.txt
new file mode 100644
index 0000000000..54a4d03719
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_queueuserapc_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_queueuserapc_test1 coreclrpal)
+
+target_link_libraries(paltest_queueuserapc_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test1/test1.c b/src/pal/tests/palsuite/threading/QueueUserAPC/test1/test1.c
new file mode 100644
index 0000000000..3637897ba7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test1/test1.c
@@ -0,0 +1,313 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test1.c
+**
+** Purpose: Tests that APCs sent to a thread in an alertable state via
+** QueueUserAPC are executed in FIFO order. Also tests that the APC
+** function is executed within the context of the correct thread and
+** that the dwData parameter gets sent correctly.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+const int ChildThreadSleepTime = 2000;
+const int InterruptTime = 1000;
+
+VOID PALAPI APCFuncA(ULONG_PTR dwParam);
+VOID PALAPI APCFuncB(ULONG_PTR dwParam);
+VOID PALAPI APCFuncC(ULONG_PTR dwParam);
+VOID PALAPI APCFuncD(ULONG_PTR dwParam);
+DWORD PALAPI SleeperProc(LPVOID lpParameter);
+
+const char *ExpectedResults = "A0B0C0D0A1B1C1D1A2B2C2D2A3B3C3D3";
+char ResultBuffer[256];
+char *ResultPtr;
+DWORD ChildThread;
+
+/* synchronization events */
+static HANDLE hSyncEvent1 = NULL;
+static HANDLE hSyncEvent2 = NULL;
+
+/* thread result because we have no GetExitCodeThread() API */
+BOOL bThreadResult = FAIL;
+
+int __cdecl main (int argc, char **argv)
+{
+ HANDLE hThread = NULL;
+ int ret;
+ int i,j;
+ BOOL bResult = FAIL;
+
+ PAPCFUNC APCFuncs[] =
+ {
+ APCFuncA,
+ APCFuncB,
+ APCFuncC,
+ APCFuncD,
+ };
+
+ /* initialize the PAL */
+ if (0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ ResultPtr = ResultBuffer;
+
+ /* create a pair of synchronization events to coordinate our threads */
+ hSyncEvent1 = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if( hSyncEvent1 == NULL )
+ {
+ Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ hSyncEvent2 = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if( hSyncEvent2 == NULL )
+ {
+ Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* create a child thread which will call SleepEx */
+ hThread = CreateThread( NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)SleeperProc,
+ 0,
+ 0,
+ &ChildThread);
+
+ if( hThread == NULL )
+ {
+ Trace( "ERROR:%lu:CreateThread() call failed\n",
+ GetLastError());
+ goto cleanup;
+ }
+
+
+ /* wait on our synchronization event to ensure the thread is running */
+ ret = WaitForSingleObject( hSyncEvent1, 20000 );
+ if( ret != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ goto cleanup;
+ }
+
+
+ /* queue our user APC functions on the thread */
+ for (i=0; i<4; i++)
+ {
+ for (j=0; j<sizeof(APCFuncs)/sizeof(APCFuncs[0]); j++)
+ {
+ ret = QueueUserAPC(APCFuncs[j], hThread, '0' + i);
+ if (ret == 0)
+ {
+ Trace( "ERROR:%lu:QueueUserAPC() call failed\n",
+ GetLastError());
+ goto cleanup;
+ }
+ }
+ }
+
+ /* signal the child thread to continue */
+ if( ! SetEvent( hSyncEvent2 ) )
+ {
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+
+ /* wait on our synchronization event to ensure the other thread is done */
+ ret = WaitForSingleObject( hSyncEvent1, 20000 );
+ if( ret != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ goto cleanup;
+ }
+
+
+ /* check that the thread executed successfully */
+ if( bThreadResult == FAIL )
+ {
+ goto cleanup;
+ }
+
+
+ /* check the result buffer */
+ if (strcmp(ExpectedResults, ResultBuffer) != 0)
+ {
+ Trace( "FAIL:Expected the APC function calls to produce a result of "
+ " \"%s\", got \"%s\"\n",
+ ExpectedResults,
+ ResultBuffer );
+ goto cleanup;
+ }
+
+ /* success if we get here */
+ bResult = PASS;
+
+cleanup:
+ /* wait for the other thread to finish */
+ if( hThread != NULL )
+ {
+ ret = WaitForSingleObject( hThread, INFINITE );
+ if (ret == WAIT_FAILED)
+ {
+ Trace( "ERROR:%lu:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ bResult = FAIL;
+ }
+ }
+
+ /* close our synchronization handles */
+ if( hSyncEvent1 != NULL )
+ {
+ if( ! CloseHandle( hSyncEvent1 ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ bResult = FAIL;
+ }
+ }
+
+ if( hSyncEvent2 != NULL )
+ {
+ if( ! CloseHandle( hSyncEvent2 ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ bResult = FAIL;
+ }
+ }
+
+ if( bResult == FAIL )
+ {
+ Fail( "test failed\n" );
+ }
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
+
+VOID PALAPI APCFuncA(ULONG_PTR dwParam)
+{
+ char val = (int) dwParam;
+
+ if (GetCurrentThreadId() != ChildThread)
+ {
+ Fail("Executing APC in thread %d, should be in %d!\n",
+ GetCurrentThreadId(), ChildThread);
+ }
+
+ *ResultPtr++ = 'A';
+ *ResultPtr++ = val;
+ *ResultPtr = 0;
+}
+
+VOID PALAPI APCFuncB(ULONG_PTR dwParam)
+{
+ char val = (int) dwParam;
+
+ if (GetCurrentThreadId() != ChildThread)
+ {
+ Fail("Executing APC in thread %d, should be in %d!\n",
+ GetCurrentThreadId(), ChildThread);
+ }
+
+ *ResultPtr++ = 'B';
+ *ResultPtr++ = val;
+ *ResultPtr = 0;
+}
+
+VOID PALAPI APCFuncC(ULONG_PTR dwParam)
+{
+ char val = (int) dwParam;
+
+ if (GetCurrentThreadId() != ChildThread)
+ {
+ Fail("Executing APC in thread %d, should be in %d!\n",
+ GetCurrentThreadId(), ChildThread);
+ }
+
+ *ResultPtr++ = 'C';
+ *ResultPtr++ = val;
+ *ResultPtr = 0;
+}
+
+VOID PALAPI APCFuncD(ULONG_PTR dwParam)
+{
+ char val = (int) dwParam;
+
+ if (GetCurrentThreadId() != ChildThread)
+ {
+ Fail("Executing APC in thread %d, should be in %d!\n",
+ GetCurrentThreadId(), ChildThread);
+ }
+
+ *ResultPtr++ = 'D';
+ *ResultPtr++ = val;
+ *ResultPtr = 0;
+}
+
+/* Entry Point for child thread. All it does is call SleepEx. */
+DWORD PALAPI SleeperProc(LPVOID lpParameter)
+{
+ DWORD ret;
+
+ /* signal the main thread that we're ready to proceed */
+ if( ! SetEvent( hSyncEvent1 ) )
+ {
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ bThreadResult = FAIL;
+ goto done;
+ }
+
+ /* wait for notification from the main thread */
+ ret = WaitForSingleObject( hSyncEvent2, 20000 );
+ if( ret != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ bThreadResult = FAIL;
+ goto done;
+ }
+
+ /* call SleepEx to activate any queued APCs */
+ ret = SleepEx(ChildThreadSleepTime, TRUE);
+ if (ret != WAIT_IO_COMPLETION)
+ {
+ Trace( "ERROR:SleepEx() call returned %lu, "
+ "expected WAIT_IO_COMPLETION\n",
+ ret );
+ bThreadResult = FAIL;
+ goto done;
+ }
+
+ /* everything passed here */
+ bThreadResult = PASS;
+
+
+done:
+ /* signal the main thread that we're finished */
+ if( ! SetEvent( hSyncEvent1 ) )
+ {
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ bThreadResult = FAIL;
+ }
+
+ /* return success or failure */
+ return bThreadResult;
+}
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test1/testinfo.dat b/src/pal/tests/palsuite/threading/QueueUserAPC/test1/testinfo.dat
new file mode 100644
index 0000000000..fbe8343d81
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test1/testinfo.dat
@@ -0,0 +1,15 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = QueueUserAPC
+Name = Test #1 for QueueUserAPC
+TYPE = DEFAULT
+EXE1 = test1
+Description
+=Tests that APCs sent to a thread in an alertable state via
+=QueueUserAPC are executed in FIFO order. Also tests that the APC
+=function is executed within the context of the correct thread and
+=that the dwData parameter gets sent correctly.
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/QueueUserAPC/test2/CMakeLists.txt
new file mode 100644
index 0000000000..88cda5f7b1
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_queueuserapc_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_queueuserapc_test2 coreclrpal)
+
+target_link_libraries(paltest_queueuserapc_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test2/test2.c b/src/pal/tests/palsuite/threading/QueueUserAPC/test2/test2.c
new file mode 100644
index 0000000000..dc2bfdb173
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test2/test2.c
@@ -0,0 +1,224 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test2.c
+**
+** Purpose: Tests that APCs are not executed if a thread never enters an
+** alertable state after they are queued.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+const int ChildThreadSleepTime = 2000;
+const int InterruptTime = 1000;
+
+DWORD ChildThread;
+BOOL InAPC;
+
+/* synchronization events */
+static HANDLE hSyncEvent1 = NULL;
+static HANDLE hSyncEvent2 = NULL;
+
+/* thread result because we have no GetExitCodeThread() API */
+static BOOL bThreadResult = FAIL;
+
+
+VOID PALAPI APCFunc(ULONG_PTR dwParam)
+{
+ InAPC = TRUE;
+}
+
+DWORD PALAPI SleeperProc(LPVOID lpParameter)
+{
+ DWORD ret;
+
+ /* signal the main thread that we're ready to proceed */
+ if( ! SetEvent( hSyncEvent1 ) )
+ {
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ bThreadResult = FAIL;
+ goto done;
+ }
+
+ /* wait for notification from the main thread */
+ ret = WaitForSingleObject( hSyncEvent2, 20000 );
+ if( ret != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ bThreadResult = FAIL;
+ goto done;
+ }
+
+ /* call our sleep function */
+ Sleep( ChildThreadSleepTime );
+
+ /* success if we reach here */
+ bThreadResult = PASS;
+
+
+done:
+
+ /* signal the main thread that we're finished */
+ if( ! SetEvent( hSyncEvent1 ) )
+ {
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ bThreadResult = FAIL;
+ }
+
+ /* return success or failure */
+ return bThreadResult;
+}
+
+
+int __cdecl main (int argc, char **argv)
+{
+ /* local variables */
+ HANDLE hThread = 0;
+ int ret;
+ BOOL bResult = FAIL;
+
+ /* initialize the PAL */
+ if (0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ InAPC = FALSE;
+
+ /* create a pair of synchronization events to coordinate our threads */
+ hSyncEvent1 = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if( hSyncEvent1 == NULL )
+ {
+ Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ hSyncEvent2 = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if( hSyncEvent2 == NULL )
+ {
+ Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* create a child thread */
+ hThread = CreateThread( NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)SleeperProc,
+ 0,
+ 0,
+ &ChildThread);
+
+ if (hThread == NULL)
+ {
+ Trace( "ERROR:%lu:CreateThread() call failed\n",
+ GetLastError());
+ goto cleanup;
+ }
+
+
+ /* wait on our synchronization event to ensure the thread is running */
+ ret = WaitForSingleObject( hSyncEvent1, 20000 );
+ if( ret != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ goto cleanup;
+ }
+
+ /* queue a user APC on the child thread */
+ ret = QueueUserAPC(APCFunc, hThread, 0);
+ if (ret == 0)
+ {
+ Trace( "ERROR:%lu:QueueUserAPC() call failed\n",
+ GetLastError());
+ goto cleanup;
+ }
+
+ /* signal the child thread to continue */
+ if( ! SetEvent( hSyncEvent2 ) )
+ {
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* wait on our synchronization event to ensure the other thread is done */
+ ret = WaitForSingleObject( hSyncEvent1, 20000 );
+ if( ret != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ goto cleanup;
+ }
+
+ /* check that the thread executed successfully */
+ if( bThreadResult == FAIL )
+ {
+ goto cleanup;
+ }
+
+
+ /* check whether the APC function was executed */
+ if( InAPC )
+ {
+ Trace( "FAIL:APC function was executed but shouldn't have been\n" );
+ goto cleanup;
+ }
+
+ /* success if we reach here */
+ bResult = PASS;
+
+
+cleanup:
+ /* wait for the other thread to finish */
+ if( hThread != NULL )
+ {
+ ret = WaitForSingleObject( hThread, INFINITE );
+ if (ret == WAIT_FAILED)
+ {
+ Trace( "ERROR:%lu:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ bResult = FAIL;
+ }
+ }
+
+ /* close our synchronization handles */
+ if( hSyncEvent1 != NULL )
+ {
+ if( ! CloseHandle( hSyncEvent1 ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ bResult = FAIL;
+ }
+ }
+
+ if( hSyncEvent2 != NULL )
+ {
+ if( ! CloseHandle( hSyncEvent2 ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ bResult = FAIL;
+ }
+ }
+
+ if( bResult == FAIL )
+ {
+ Fail( "test failed\n" );
+ }
+
+
+ /* terminate the PAL */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test2/testinfo.dat b/src/pal/tests/palsuite/threading/QueueUserAPC/test2/testinfo.dat
new file mode 100644
index 0000000000..42d942df36
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test2/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = QueueUserAPC
+Name = Test #2 for QueueUserAPC
+TYPE = DEFAULT
+EXE1 = test2
+Description
+=Tests that APCs are not executed if a thread never enters an
+=alertable state after they are queued.
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/QueueUserAPC/test3/CMakeLists.txt
new file mode 100644
index 0000000000..8475dcb03e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test3.c
+)
+
+add_executable(paltest_queueuserapc_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_queueuserapc_test3 coreclrpal)
+
+target_link_libraries(paltest_queueuserapc_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test3/test3.c b/src/pal/tests/palsuite/threading/QueueUserAPC/test3/test3.c
new file mode 100644
index 0000000000..933f41a5b4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test3/test3.c
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test3.c
+**
+** Purpose: Tests how QueueUserAPC handles an invalid thread.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+int __cdecl main (int argc, char **argv)
+{
+ int ret;
+
+ if (0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ ret = QueueUserAPC(NULL, NULL, 0);
+ if (ret != 0)
+ {
+ Fail("QueueUserAPC passed with an invalid thread!\n");
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test3/testinfo.dat b/src/pal/tests/palsuite/threading/QueueUserAPC/test3/testinfo.dat
new file mode 100644
index 0000000000..0b96349f15
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test3/testinfo.dat
@@ -0,0 +1,12 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = QueueUserAPC
+Name = Test #3 for QueueUserAPC
+TYPE = DEFAULT
+EXE1 = test3
+Description
+=Tests how QueueUserAPC handles an invalid thread.
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test4/CMakeLists.txt b/src/pal/tests/palsuite/threading/QueueUserAPC/test4/CMakeLists.txt
new file mode 100644
index 0000000000..2552738b50
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test4/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test4.c
+)
+
+add_executable(paltest_queueuserapc_test4
+ ${SOURCES}
+)
+
+add_dependencies(paltest_queueuserapc_test4 coreclrpal)
+
+target_link_libraries(paltest_queueuserapc_test4
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test4/test4.c b/src/pal/tests/palsuite/threading/QueueUserAPC/test4/test4.c
new file mode 100644
index 0000000000..c28709db81
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test4/test4.c
@@ -0,0 +1,72 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test4.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** GetCurrentThread
+** SleepEx
+**
+** Purpose:
+**
+** Test to ensure proper operation of the QueueUserAPC()
+** API by trying to queue APC functions on the current
+** thread.
+**
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+static BOOL bAPCExecuted = FALSE;
+
+VOID PALAPI APCFunc( ULONG_PTR dwParam )
+{
+ bAPCExecuted = TRUE;
+}
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ HANDLE hThread = NULL;
+ DWORD ret;
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* get the current thread */
+ hThread = GetCurrentThread();
+ ret = QueueUserAPC( APCFunc, hThread, 0 );
+ if( ret == 0 )
+ {
+ Fail( "ERROR:%lu:QueueUserAPC call failed\n", GetLastError() );
+ }
+
+ /* call SleepEx() to put the thread in an alertable state */
+ ret = SleepEx( 2000, TRUE );
+ if( ret != WAIT_IO_COMPLETION )
+ {
+ Fail( "ERROR:Expected sleep to return WAIT_IO_COMPLETION, got %lu\n",
+ ret );
+ }
+
+ /* check that the APC function was executed */
+ if( bAPCExecuted == FALSE )
+ {
+ Fail( "ERROR:APC function was not executed\n" );
+ }
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test4/testinfo.dat b/src/pal/tests/palsuite/threading/QueueUserAPC/test4/testinfo.dat
new file mode 100644
index 0000000000..cd7b7c2f21
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test4/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = QueueUserAPC
+Name = Positive test for QueueUserAPC
+TYPE = DEFAULT
+EXE1 = test4
+Description
+= Test to ensure proper operation of the QueueUserAPC()
+= API by trying to queue APC functions on the current
+= thread.
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test5/CMakeLists.txt b/src/pal/tests/palsuite/threading/QueueUserAPC/test5/CMakeLists.txt
new file mode 100644
index 0000000000..c2fa10de12
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test5/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test5.c
+)
+
+add_executable(paltest_queueuserapc_test5
+ ${SOURCES}
+)
+
+add_dependencies(paltest_queueuserapc_test5 coreclrpal)
+
+target_link_libraries(paltest_queueuserapc_test5
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test5/test5.c b/src/pal/tests/palsuite/threading/QueueUserAPC/test5/test5.c
new file mode 100644
index 0000000000..3d26a55f59
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test5/test5.c
@@ -0,0 +1,200 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test5.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateEvent
+** SetEvent
+** CreateThread
+** ResumeThread
+** WaitForSingleObject
+** CloseHandle
+**
+** Purpose:
+**
+** Test to ensure proper operation of the QueueUserAPC()
+** API by trying to queue and activate APC functions on
+** a thread that was created suspended, prior to resuming
+** it. We're verifying the following behavior:
+**
+** "If an application queues an APC before the thread begins
+** running, the thread begins by calling the APC function.
+** After the thread calls an APC function, it calls the APC
+** functions for all APCs in its APC queue."
+**
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+static HANDLE hEvent = NULL;
+static BOOL bAPCExecuted = FALSE;
+
+VOID PALAPI APCFunc( ULONG_PTR dwParam )
+{
+ bAPCExecuted = TRUE;
+}
+
+/**
+ * ThreadFunc
+ *
+ * Dummy thread function for APC queuing.
+ */
+DWORD PALAPI ThreadFunc( LPVOID param )
+{
+ DWORD ret = 0;
+
+ /* alertable wait until the global event is signalled */
+ ret = WaitForSingleObject( hEvent, INFINITE );
+ if( ret != WAIT_OBJECT_0 )
+ {
+ Fail( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ }
+
+ return 0;
+}
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ HANDLE hThread = NULL;
+ DWORD IDThread;
+ DWORD ret;
+ BOOL bResult = FALSE;
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* create an event for the other thread to wait on */
+ hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ if( hEvent == NULL )
+ {
+ Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ }
+
+ /* run another dummy thread to cause notification of the library */
+ hThread = CreateThread( NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE) ThreadFunc, /* thread function */
+ (LPVOID) NULL, /* pass thread index as */
+ /* function argument */
+ CREATE_SUSPENDED, /* create suspended */
+ &IDThread ); /* returns thread id */
+
+ /* Check the return value for success. */
+ if( hThread == NULL )
+ {
+ /* error creating thread */
+ Trace( "ERROR:%lu:CreateThread call failed\n", GetLastError() );
+ if( ! CloseHandle( hEvent ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ Fail( "test failed\n" );
+ }
+
+ /* queue our APC on the suspended thread */
+ ret = QueueUserAPC( APCFunc, hThread, 0 );
+ if( ret == 0 )
+ {
+ Fail( "ERROR:%lu:QueueUserAPC call failed\n", GetLastError() );
+ }
+
+ /* wait on the suspended thread */
+ ret = WaitForSingleObject( hThread, 2000 );
+ if( ret != WAIT_TIMEOUT )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ ret );
+ goto cleanup;
+ }
+
+ /* verify that the APC function was not executed */
+ if( bAPCExecuted == TRUE )
+ {
+ Trace( "ERROR:APC function was executed for a suspended thread\n" );
+ goto cleanup;
+ }
+
+ /* Resume the suspended thread */
+ ResumeThread( hThread );
+
+ /* do another wait on the resumed thread */
+ ret = WaitForSingleObject( hThread, 2000 );
+
+ /* verify that we got a WAIT_TIMEOUT result */
+ if( ret != WAIT_TIMEOUT )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ ret );
+ goto cleanup;
+ }
+
+ /* check that the APC function was actually executed */
+ if( bAPCExecuted == FALSE )
+ {
+ Trace( "ERROR:APC function was not executed\n" );
+ goto cleanup;
+ }
+
+ /* set the success flag */
+ bResult = PASS;
+
+cleanup:
+ /* signal the event so the other thread will exit */
+ if( ! SetEvent( hEvent ) )
+ {
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ bResult = FAIL;
+ }
+
+ /* close the global event handle */
+ if( ! CloseHandle( hEvent ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ bResult = FAIL;
+ }
+
+ /* wait on the other thread to complete */
+ ret = WaitForSingleObject( hThread, 2000 );
+ if( ret != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ bResult = FAIL;
+ }
+
+ /* close the thread handle */
+ if( ! CloseHandle( hThread ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ bResult = FAIL;
+ }
+
+ /* output final failure result for failure case */
+ if( bResult == FAIL )
+ {
+ Fail( "test failed\n" );
+ }
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test5/testinfo.dat b/src/pal/tests/palsuite/threading/QueueUserAPC/test5/testinfo.dat
new file mode 100644
index 0000000000..f1775aabe8
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test5/testinfo.dat
@@ -0,0 +1,20 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = QueueUserAPC
+Name = Positive test for QueueUserAPC
+TYPE = DEFAULT
+EXE1 = test5
+Description
+= Test to ensure proper operation of the QueueUserAPC()
+= API by trying to queue and activate APC functions on
+= a thread that was created suspended, prior to resuming
+= it. We're verifying the following behavior:
+=
+= "If an application queues an APC before the thread begins
+= running, the thread begins by calling the APC function.
+= After the thread calls an APC function, it calls the APC
+= functions for all APCs in its APC queue."
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test6/CMakeLists.txt b/src/pal/tests/palsuite/threading/QueueUserAPC/test6/CMakeLists.txt
new file mode 100644
index 0000000000..3fd1b1a08e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test6/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test6.c
+)
+
+add_executable(paltest_queueuserapc_test6
+ ${SOURCES}
+)
+
+add_dependencies(paltest_queueuserapc_test6 coreclrpal)
+
+target_link_libraries(paltest_queueuserapc_test6
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test6/test6.c b/src/pal/tests/palsuite/threading/QueueUserAPC/test6/test6.c
new file mode 100644
index 0000000000..e2e2464726
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test6/test6.c
@@ -0,0 +1,129 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test6.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateEvent
+** SetEvent
+** CreateThread
+** ResumeThread
+** WaitForMultipleObjectsEx
+** CloseHandle
+**
+** Purpose:
+**
+** Test to ensure proper operation of the QueueUserAPC()
+** API by trying to queue APC functions on a thread that
+** has already terminated.
+**
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+static BOOL bAPCExecuted = FALSE;
+
+VOID PALAPI APCFunc( ULONG_PTR dwParam )
+{
+ bAPCExecuted = TRUE;
+}
+
+/**
+ * ThreadFunc
+ *
+ * Dummy thread function for APC queuing.
+ */
+DWORD PALAPI ThreadFunc( LPVOID param )
+{
+ int i;
+
+ /* simulate some activity */
+ for( i=0; i<250000; i++ )
+ ;
+
+ return 0;
+}
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ HANDLE hThread = NULL;
+ DWORD IDThread;
+ DWORD ret;
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* run another dummy thread to cause notification of the library */
+ hThread = CreateThread( NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE) ThreadFunc, /* thread function */
+ (LPVOID) NULL, /* pass thread index as */
+ /* function argument */
+ CREATE_SUSPENDED, /* create suspended */
+ &IDThread ); /* returns thread id */
+
+ /* Check the return value for success. */
+ if( hThread == NULL )
+ {
+ /* error creating thread */
+ Fail( "ERROR:%lu:CreateThread call failed\n", GetLastError() );
+ }
+
+ /* Resume the suspended thread */
+ ResumeThread( hThread );
+
+ /* wait on the other thread to complete */
+ ret = WaitForSingleObject( hThread, INFINITE );
+ if( ret != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ if( ! CloseHandle( hThread ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ Fail( "test failed\n" );
+ }
+
+ /* queue our APC on the finished thread */
+ ret = QueueUserAPC( APCFunc, hThread, 0 );
+ if( ret != 0 )
+ {
+ Trace( "ERROR:QueueUserAPC call succeeded on a terminated thread\n" );
+ if( ! CloseHandle( hThread ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ Fail( "test failed\n" );
+ }
+
+ if( ! CloseHandle( hThread ) )
+ {
+ Fail( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+
+ /* dummy check that the APC function wasn't actually executed */
+ if( bAPCExecuted != FALSE )
+ {
+ Fail( "ERROR:APC function was executed\n" );
+ }
+
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test6/testinfo.dat b/src/pal/tests/palsuite/threading/QueueUserAPC/test6/testinfo.dat
new file mode 100644
index 0000000000..4d806184ee
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test6/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = QueueUserAPC
+Name = Negative test for QueueUserAPC
+TYPE = DEFAULT
+EXE1 = test6
+Description
+= Test to ensure proper operation of the QueueUserAPC()
+= API by trying to queue APC functions on a thread that
+= has already terminated.
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test7/CMakeLists.txt b/src/pal/tests/palsuite/threading/QueueUserAPC/test7/CMakeLists.txt
new file mode 100644
index 0000000000..751f3894f4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test7/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test7.c
+)
+
+add_executable(paltest_queueuserapc_test7
+ ${SOURCES}
+)
+
+add_dependencies(paltest_queueuserapc_test7 coreclrpal)
+
+target_link_libraries(paltest_queueuserapc_test7
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test7/test7.c b/src/pal/tests/palsuite/threading/QueueUserAPC/test7/test7.c
new file mode 100644
index 0000000000..54a63982fe
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test7/test7.c
@@ -0,0 +1,253 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test7.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateEvent
+** SetEvent
+** CreateThread
+** ResumeThread
+** WaitForMultipleObjectsEx
+** CloseHandle
+**
+** Purpose:
+**
+** Test to ensure proper operation of the QueueUserAPC()
+** API by trying to queue an APC function on a thread and
+** activating it with WaitForMultipleObjectsEx.
+**
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+static HANDLE hSyncEvent = NULL;
+static HANDLE hTestEvent = NULL;
+static int nAPCExecuted = 0;
+static BOOL bThreadResult = FALSE;
+
+VOID PALAPI APCFunc( ULONG_PTR dwParam )
+{
+ ++nAPCExecuted;
+}
+
+/**
+ * ThreadFunc
+ *
+ * Dummy thread function for APC queuing.
+ */
+DWORD PALAPI ThreadFunc( LPVOID param )
+{
+ DWORD ret = 0;
+
+ /* pessimism */
+ bThreadResult = FALSE;
+
+ /* set the sync event to notify the main thread */
+ if( ! SetEvent( hSyncEvent ) )
+ {
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ goto done;
+ }
+
+ /* wait until the test event is signalled */
+ ret = WaitForSingleObject( hTestEvent, INFINITE );
+ if( ret != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ goto done;
+ }
+
+ /* now do an alertable wait on the same event, which is now
+ in an unsignalled state */
+ ret = WaitForMultipleObjectsEx( 1, &hTestEvent, TRUE, 2000, TRUE );
+
+ /* verify that we got a WAIT_IO_COMPLETION result */
+ if( ret != WAIT_IO_COMPLETION )
+ {
+ Trace( "ERROR:WaitForMultipleObjectsEx returned %lu, "
+ "expected WAIT_IO_COMPLETION\n",
+ ret );
+ goto done;
+ }
+
+ /* set the event again */
+ if( ! SetEvent( hTestEvent ) )
+ {
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ goto done;
+ }
+
+ /* do a non-alertable wait on the same event */
+ ret = WaitForMultipleObjectsEx( 1, &hTestEvent, TRUE, INFINITE, FALSE );
+
+ /* verify that we got a WAIT_OBJECT_0 result */
+ if( ret != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForMultipleObjectsEx returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ goto done;
+ }
+
+ /* success at this point */
+ bThreadResult = TRUE;
+
+
+done:
+ return bThreadResult;
+}
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ HANDLE hThread = NULL;
+ DWORD IDThread;
+ DWORD ret;
+ BOOL bResult = FALSE;
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* create an auto-reset event for the other thread to wait on */
+ hTestEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if( hTestEvent == NULL )
+ {
+ Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ }
+
+ /* create an auto-reset event for synchronization */
+ hSyncEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if( hSyncEvent == NULL )
+ {
+ Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ if( ! CloseHandle( hTestEvent ) )
+ {
+ Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ }
+ Fail( "test failed\n" );
+ }
+
+ /* run another dummy thread to cause notification of the library */
+ hThread = CreateThread( NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE) ThreadFunc, /* thread function */
+ (LPVOID) NULL, /* pass thread index as */
+ /* function argument */
+ CREATE_SUSPENDED, /* create suspended */
+ &IDThread ); /* returns thread id */
+
+ /* Check the return value for success. */
+ if( hThread == NULL )
+ {
+ /* error creating thread */
+ Trace( "ERROR:%lu:CreateThread call failed\n", GetLastError() );
+ if( ! CloseHandle( hTestEvent ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+ Fail( "test failed\n" );
+ }
+
+ /* Resume the suspended thread */
+ ResumeThread( hThread );
+
+ /* wait until the other thread is ready to proceed */
+ ret = WaitForSingleObject( hSyncEvent, 10000 );
+ if( ret != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ goto cleanup;
+ }
+
+
+ /* now queue our APC on the test thread */
+ ret = QueueUserAPC( APCFunc, hThread, 0 );
+ if( ret == 0 )
+ {
+ Trace( "ERROR:%lu:QueueUserAPC call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* signal the test event so the other thread will proceed */
+ if( ! SetEvent( hTestEvent ) )
+ {
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ goto cleanup;
+ }
+
+ /* wait on the other thread to complete */
+ ret = WaitForSingleObject( hThread, INFINITE );
+ if( ret != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForSingleObject() returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ ret );
+ goto cleanup;
+ }
+
+ /* check the result of the other thread */
+ if( bThreadResult == FALSE )
+ {
+ goto cleanup;
+ }
+
+ /* check that the APC function was actually executed exactly one time */
+ if( nAPCExecuted != 1 )
+ {
+ Trace( "ERROR:APC function was executed %d times, "
+ "expected once\n", nAPCExecuted );
+ goto cleanup;
+ }
+
+ /* set the success flag */
+ bResult = PASS;
+
+
+cleanup:
+ /* close the global event handles */
+ if( ! CloseHandle( hTestEvent ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ bResult = FAIL;
+ }
+
+ if( ! CloseHandle( hSyncEvent ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ bResult = FAIL;
+ }
+
+ /* close the thread handle */
+ if( ! CloseHandle( hThread ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ bResult = FAIL;
+ }
+
+ /* output final failure result for failure case */
+ if( bResult == FAIL )
+ {
+ Fail( "test failed\n" );
+ }
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/QueueUserAPC/test7/testinfo.dat b/src/pal/tests/palsuite/threading/QueueUserAPC/test7/testinfo.dat
new file mode 100644
index 0000000000..d92d9496d7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test7/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = QueueUserAPC
+Name = Positive test for QueueUserAPC
+TYPE = DEFAULT
+EXE1 = test7
+Description
+= Test to ensure proper operation of the QueueUserAPC()
+= API by trying to queue an APC function on a thread and
+= activating it with WaitForMultipleObjectsEx.
diff --git a/src/pal/tests/palsuite/threading/ReleaseMutex/CMakeLists.txt b/src/pal/tests/palsuite/threading/ReleaseMutex/CMakeLists.txt
new file mode 100644
index 0000000000..f9524196e0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ReleaseMutex/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test3)
+
diff --git a/src/pal/tests/palsuite/threading/ReleaseMutex/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/ReleaseMutex/test3/CMakeLists.txt
new file mode 100644
index 0000000000..8e64c28242
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ReleaseMutex/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ ReleaseMutex.c
+)
+
+add_executable(paltest_releasemutex_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_releasemutex_test3 coreclrpal)
+
+target_link_libraries(paltest_releasemutex_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/ReleaseMutex/test3/ReleaseMutex.c b/src/pal/tests/palsuite/threading/ReleaseMutex/test3/ReleaseMutex.c
new file mode 100644
index 0000000000..5f6adb0419
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ReleaseMutex/test3/ReleaseMutex.c
@@ -0,0 +1,103 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: ReleaseMutex/test3/ReleaseMutex.c
+**
+** Purpose: Test failure code for ReleaseMutex.
+**
+** Dependencies: CreateMutex
+** ReleaseMutex
+** CreateThread
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+DWORD dwTestResult; /* global for test result */
+
+DWORD dwThreadId; /* consumer thread identifier */
+
+HANDLE hMutex; /* handle to mutex */
+
+HANDLE hThread; /* handle to thread */
+
+/*
+ * Thread function.
+ */
+DWORD
+PALAPI
+ThreadFunction( LPVOID lpNoArg )
+{
+
+ dwTestResult = ReleaseMutex(hMutex);
+
+ return 0;
+}
+
+int __cdecl main (int argc, char **argv)
+{
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return (FAIL);
+ }
+
+ /*
+ * set dwTestResult so test fails even if ReleaseMutex is not called
+ */
+ dwTestResult = 1;
+
+ /*
+ * Create mutex
+ */
+ hMutex = CreateMutexW (
+ NULL,
+ TRUE,
+ NULL);
+
+ if ( NULL == hMutex )
+ {
+ Fail ( "hMutex = CreateMutex () - returned NULL\n"
+ "Failing Test.\nGetLastError returned %d\n", GetLastError());
+ }
+
+ /*
+ * Create ThreadFunction
+ */
+ hThread = CreateThread(
+ NULL,
+ 0,
+ ThreadFunction,
+ NULL,
+ 0,
+ &dwThreadId);
+
+ if ( NULL == hThread )
+ {
+
+ Fail ( "CreateThread() returned NULL. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ /*
+ * Wait for ThreadFunction to complete
+ */
+ WaitForSingleObject (hThread, INFINITE);
+
+ if (dwTestResult)
+ {
+ Fail ("ReleaseMutex() test was expected to return 0.\n"
+ "It returned %d. Failing test.\n", dwTestResult );
+ }
+
+ Trace ("ReleaseMutex() test returned 0.\nTest passed.\n");
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/ReleaseMutex/test3/testinfo.dat b/src/pal/tests/palsuite/threading/ReleaseMutex/test3/testinfo.dat
new file mode 100644
index 0000000000..3f06757eb6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ReleaseMutex/test3/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ReleaseMutex
+Name = Positive Test for ReleaseMutex
+TYPE = DEFAULT
+EXE1 = releasemutex
+Description
+= Calls ReleaseMutex from a thread which should not have ownership of the
+= mutex. If ReleaseMutex fails correctly this test will succeed.
diff --git a/src/pal/tests/palsuite/threading/ResetEvent/CMakeLists.txt b/src/pal/tests/palsuite/threading/ResetEvent/CMakeLists.txt
new file mode 100644
index 0000000000..a3847f8ca9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/CMakeLists.txt
@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+add_subdirectory(test4)
+
diff --git a/src/pal/tests/palsuite/threading/ResetEvent/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/ResetEvent/test1/CMakeLists.txt
new file mode 100644
index 0000000000..78e84da2b9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_resetevent_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_resetevent_test1 coreclrpal)
+
+target_link_libraries(paltest_resetevent_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/ResetEvent/test1/test1.c b/src/pal/tests/palsuite/threading/ResetEvent/test1/test1.c
new file mode 100644
index 0000000000..20a0d5dffa
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test1/test1.c
@@ -0,0 +1,99 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test1.c
+**
+** Purpose: Test for ResetEvent. Create an event with an intial
+** state signaled. Then reset that signal, and check to see that
+** the event is now not signaled.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+BOOL ResetEventTest()
+{
+ BOOL bRet = FALSE;
+ DWORD dwRet = 0;
+
+ LPSECURITY_ATTRIBUTES lpEventAttributes = 0;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = TRUE;
+
+ /* Create an Event, ensure it is valid */
+ HANDLE hEvent = CreateEvent( lpEventAttributes,
+ bManualReset, bInitialState, NULL);
+
+ if (hEvent != INVALID_HANDLE_VALUE)
+ {
+ /* Check that WaitFor returns WAIT_OBJECT_0, indicating that
+ the event is signaled.
+ */
+
+ dwRet = WaitForSingleObject(hEvent,0);
+
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Fail("ResetEventTest:WaitForSingleObject failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ /* Call ResetEvent, which will reset the signal */
+ bRet = ResetEvent(hEvent);
+
+ if (!bRet)
+ {
+ Fail("ResetEventTest:ResetEvent failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ /* Call WaitFor again, and since it has been reset,
+ the return value should now be WAIT_TIMEOUT
+ */
+ dwRet = WaitForSingleObject(hEvent,0);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Fail("ResetEventTest:WaitForSingleObject %s failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ bRet = CloseHandle(hEvent);
+
+ if (!bRet)
+ {
+ Fail("ResetEventTest:CloseHandle failed (%x)\n", GetLastError());
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Fail("ResetEventTest:CreateEvent failed (%x)\n", GetLastError());
+ }
+
+ return bRet;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!ResetEventTest())
+ {
+ Fail ("Test failed\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/ResetEvent/test1/testinfo.dat b/src/pal/tests/palsuite/threading/ResetEvent/test1/testinfo.dat
new file mode 100644
index 0000000000..ed27f13dba
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test1/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ResetEvent
+Name = Positive Test for ResetEvent
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Test for ResetEvent. Create an event with an intial
+= state signaled. Then reset that signal, and check to see that
+= the event is now not signaled.
diff --git a/src/pal/tests/palsuite/threading/ResetEvent/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/ResetEvent/test2/CMakeLists.txt
new file mode 100644
index 0000000000..ad0fe9b4a1
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_resetevent_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_resetevent_test2 coreclrpal)
+
+target_link_libraries(paltest_resetevent_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/ResetEvent/test2/test2.c b/src/pal/tests/palsuite/threading/ResetEvent/test2/test2.c
new file mode 100644
index 0000000000..8117f44353
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test2/test2.c
@@ -0,0 +1,89 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test2.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateEvent
+** CloseHandle
+** WaitForSingleObject
+**
+** Purpose: Test to ensure proper operation of the ResetEvent()
+** API by calling it on an event handle that's already
+** unsignalled.
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ DWORD dwRet = 0;
+ HANDLE hEvent = NULL;
+ LPSECURITY_ATTRIBUTES lpEventAttributes = NULL;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = FALSE;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* create an unsignalled event which we can use with ResetEvent */
+ hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ NULL );
+
+ if( hEvent == INVALID_HANDLE_VALUE )
+ {
+ /* ERROR */
+ Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ }
+
+ /* verify that the event isn't signalled yet */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ CloseHandle( hEvent );
+ Fail( "Test failed\n" );
+ }
+
+ /* try to reset the event */
+ if( ! ResetEvent( hEvent ) )
+ {
+ /* ERROR */
+ Trace( "FAIL:%lu:ResetEvent() call failed\n", GetLastError() );
+ CloseHandle( hEvent );
+ Fail( "Test failed\n" );
+ }
+
+ /* close the event handle */
+ if( ! CloseHandle( hEvent ) )
+ {
+ Fail( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/ResetEvent/test2/testinfo.dat b/src/pal/tests/palsuite/threading/ResetEvent/test2/testinfo.dat
new file mode 100644
index 0000000000..4af1769cd4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test2/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ResetEvent
+Name = Positive test for ResetEvent
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Test to ensure proper operation of the ResetEvent()
+= API by calling it on an event handle that's already
+= unsignalled.
diff --git a/src/pal/tests/palsuite/threading/ResetEvent/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/ResetEvent/test3/CMakeLists.txt
new file mode 100644
index 0000000000..3c4ea1f249
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test3.c
+)
+
+add_executable(paltest_resetevent_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_resetevent_test3 coreclrpal)
+
+target_link_libraries(paltest_resetevent_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/ResetEvent/test3/test3.c b/src/pal/tests/palsuite/threading/ResetEvent/test3/test3.c
new file mode 100644
index 0000000000..9bc068ea72
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test3/test3.c
@@ -0,0 +1,86 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test3.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateEvent
+** CloseHandle
+**
+** Purpose:
+**
+** Test to ensure proper operation of the ResetEvent()
+** API by calling it on an event handle that's been
+** closed. We expect it to return an appropriate error
+** result.
+**
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ HANDLE hEvent = NULL;
+ LPSECURITY_ATTRIBUTES lpEventAttributes = NULL;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = FALSE;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* create an event which we can use with ResetEvent */
+ hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ NULL );
+
+ if( hEvent == INVALID_HANDLE_VALUE )
+ {
+ /* ERROR */
+ Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ }
+
+ /* close the event handle */
+ if( ! CloseHandle( hEvent ) )
+ {
+ Fail( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+
+ /* try to reset the event */
+ if( ResetEvent( hEvent ) )
+ {
+ /* ERROR */
+ Fail( "FAIL:ResetEvent() call succeeded on a closed event handle\n" );
+ }
+
+ /* verify the result of GetLastError() */
+ if( GetLastError() != ERROR_INVALID_HANDLE )
+ {
+ /* ERROR */
+ Fail( "FAIL:ResetEvent() call failed on a closed event handle "
+ "but returned an unexpected error result %lu\n" );
+ }
+
+
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/ResetEvent/test3/testinfo.dat b/src/pal/tests/palsuite/threading/ResetEvent/test3/testinfo.dat
new file mode 100644
index 0000000000..4abeeded00
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test3/testinfo.dat
@@ -0,0 +1,15 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ResetEvent
+Name = Negative test for ResetEvent
+TYPE = DEFAULT
+EXE1 = test3
+Description
+= Test to ensure proper operation of the ResetEvent()
+= API by calling it on an event handle that's been
+= closed. We expect it to return an appropriate error
+= result.
diff --git a/src/pal/tests/palsuite/threading/ResetEvent/test4/CMakeLists.txt b/src/pal/tests/palsuite/threading/ResetEvent/test4/CMakeLists.txt
new file mode 100644
index 0000000000..68ee20bc95
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test4/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test4.c
+)
+
+add_executable(paltest_resetevent_test4
+ ${SOURCES}
+)
+
+add_dependencies(paltest_resetevent_test4 coreclrpal)
+
+target_link_libraries(paltest_resetevent_test4
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/ResetEvent/test4/test4.c b/src/pal/tests/palsuite/threading/ResetEvent/test4/test4.c
new file mode 100644
index 0000000000..0cc68fd9aa
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test4/test4.c
@@ -0,0 +1,161 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test4.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateEvent
+** CloseHandle
+** WaitForSingleObject
+** DuplicateHandle
+** GetCurrentProcess
+**
+** Purpose:
+**
+** Test to ensure proper operation of the ResetEvent()
+** API by calling it on an event handle that's the
+** result of a DuplicateHandle() call on another event
+** handle.
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ DWORD dwRet = 0;
+ HANDLE hEvent = NULL;
+ HANDLE hDupEvent = NULL;
+ LPSECURITY_ATTRIBUTES lpEventAttributes = NULL;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = TRUE;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* create an event which we can use with ResetEvent */
+ hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ NULL );
+
+ if( hEvent == INVALID_HANDLE_VALUE )
+ {
+ /* ERROR */
+ Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ }
+
+ /* verify that the event is signalled already */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ CloseHandle( hEvent );
+ Fail( "Test failed\n" );
+ }
+
+
+ /* duplicate the event handle */
+ if( ! (DuplicateHandle(
+ GetCurrentProcess(),
+ hEvent,
+ GetCurrentProcess(),
+ &hDupEvent,
+ GENERIC_READ|GENERIC_WRITE, /* ignored in PAL */
+ FALSE,
+ DUPLICATE_SAME_ACCESS ) ) )
+ {
+ Trace("ERROR:%u:DuplicateHandle() call failed\n",
+ GetLastError() );
+ CloseHandle( hEvent );
+ Fail("Test failed\n");
+ }
+
+ /* verify that the event is signalled with the duplicate handle */
+ dwRet = WaitForSingleObject( hDupEvent, 0 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ CloseHandle( hEvent );
+ CloseHandle( hDupEvent );
+ Fail( "Test failed\n" );
+ }
+
+
+ /* reset the event using the duplicate handle */
+ if( ! ResetEvent( hDupEvent ) )
+ {
+ /* ERROR */
+ Trace( "ERROR:%lu:ResetEvent() call failed\n", GetLastError() );
+ CloseHandle( hEvent );
+ CloseHandle( hDupEvent );
+ Fail( "Test failed\n" );
+ }
+
+ /* verify that the event isn't signalled using the duplicate handle*/
+ dwRet = WaitForSingleObject( hDupEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ CloseHandle( hEvent );
+ Fail( "Test failed\n" );
+ }
+
+ /* check that the event isn't signalled using the original event handle */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ CloseHandle( hEvent );
+ Fail( "Test failed\n" );
+ }
+
+
+ /* close the duplicate event handle */
+ if( ! CloseHandle( hDupEvent ) )
+ {
+ Fail( "ERROR:%lu:CloseHandle() call failed for duplicate handle\n",
+ GetLastError() );
+ }
+
+
+ /* close the event handle */
+ if( ! CloseHandle( hEvent ) )
+ {
+ Fail( "ERROR:%lu:CloseHandle() call failed for original handle\n",
+ GetLastError() );
+ }
+
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/ResetEvent/test4/testinfo.dat b/src/pal/tests/palsuite/threading/ResetEvent/test4/testinfo.dat
new file mode 100644
index 0000000000..0223246c6f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test4/testinfo.dat
@@ -0,0 +1,15 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ResetEvent
+Name = Positive test for ResetEvent
+TYPE = DEFAULT
+EXE1 = test4
+Description
+= Test to ensure proper operation of the ResetEvent()
+= API by calling it on an event handle that's the
+= result of a DuplicateHandle() call on another event
+= handle.
diff --git a/src/pal/tests/palsuite/threading/ResumeThread/CMakeLists.txt b/src/pal/tests/palsuite/threading/ResumeThread/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResumeThread/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/ResumeThread/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/ResumeThread/test1/CMakeLists.txt
new file mode 100644
index 0000000000..2c83dfd3d5
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResumeThread/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_resumethread_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_resumethread_test1 coreclrpal)
+
+target_link_libraries(paltest_resumethread_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/ResumeThread/test1/test1.c b/src/pal/tests/palsuite/threading/ResumeThread/test1/test1.c
new file mode 100644
index 0000000000..037f79f7a3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResumeThread/test1/test1.c
@@ -0,0 +1,141 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test1.c
+**
+** Purpose: Test for ResumeThread. Create a suspended Thread.
+** First, ensure that it is indeed suspended. Then call resumethread
+** and check to ensure that the function has now run.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+DWORD dwResumeThreadTestParameter = 0;
+
+DWORD PALAPI ResumeThreadTestThread( LPVOID lpParameter)
+{
+ DWORD dwRet = 0;
+
+ /* Save parameter so we can check and ensure this function ran
+ properly.
+ */
+
+ dwResumeThreadTestParameter = (DWORD)lpParameter;
+
+ return dwRet;
+}
+
+BOOL ResumeThreadTest()
+{
+ BOOL bRet = FALSE;
+ DWORD dwRet = 0;
+
+ LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL;
+ DWORD dwStackSize = 0;
+ LPTHREAD_START_ROUTINE lpStartAddress = &ResumeThreadTestThread;
+ LPVOID lpParameter = lpStartAddress;
+ DWORD dwCreationFlags = CREATE_SUSPENDED;
+ DWORD dwThreadId = 0;
+
+ HANDLE hThread = 0;
+
+ dwResumeThreadTestParameter = 0;
+
+ /* Create a thread, with CREATE_SUSPENDED, so we can resume it! */
+
+ hThread = CreateThread( lpThreadAttributes,
+ dwStackSize, lpStartAddress, lpParameter,
+ dwCreationFlags, &dwThreadId );
+
+ if (hThread != INVALID_HANDLE_VALUE)
+ {
+ /* Wait for one second. This should return WAIT_TIMEOUT */
+ dwRet = WaitForSingleObject(hThread,1000);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("ResumeThreadTest:WaitForSingleObject "
+ "failed (%x)\n",GetLastError());
+ }
+ else
+ {
+ /* Check to ensure the parameter hasn't changed. The
+ function shouldn't have occurred yet.
+ */
+ if (dwResumeThreadTestParameter != 0)
+ {
+ Trace("ResumeThreadTest:parameter error\n");
+ }
+ else
+ {
+ /* Call ResumeThread and ensure the return value is
+ correct.
+ */
+
+ dwRet = ResumeThread(hThread);
+
+ if (dwRet != 1)
+ {
+ Trace("ResumeThreadTest:ResumeThread "
+ "failed (%x)\n",GetLastError());
+ }
+ else
+ {
+ /* Wait again, now that the thread has been
+ resumed, and the return should be WAIT_OBJECT_0
+ */
+ dwRet = WaitForSingleObject(hThread,INFINITE);
+
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Trace("ResumeThreadTest:WaitForSingleObject "
+ "failed (%x)\n",GetLastError());
+ }
+ else
+ {
+ /* Check the param now and it should have been
+ set.
+ */
+ if (dwResumeThreadTestParameter != (DWORD)lpParameter)
+ {
+ Trace("ResumeThreadTest:parameter error\n");
+ }
+ else
+ {
+ bRet = TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Trace("ResumeThreadTest:CreateThread failed (%x)\n",GetLastError());
+ }
+
+ return bRet;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!ResumeThreadTest())
+ {
+ Fail("Test Failed\n");
+ }
+
+ PAL_Terminate();
+ return (PASS);
+
+}
diff --git a/src/pal/tests/palsuite/threading/ResumeThread/test1/testinfo.dat b/src/pal/tests/palsuite/threading/ResumeThread/test1/testinfo.dat
new file mode 100644
index 0000000000..8472165d5d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResumeThread/test1/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ResumeThread
+Name = Positive Test for ResumeThread
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Test for ResumeThread. Create a suspended Thread.
+= First, ensure that it is indeed suspended. Then call resumethread
+= and check to ensure that the function has now run.
diff --git a/src/pal/tests/palsuite/threading/SetErrorMode/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetErrorMode/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetErrorMode/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/SetErrorMode/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetErrorMode/test1/CMakeLists.txt
new file mode 100644
index 0000000000..cfdeb31950
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetErrorMode/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_seterrormode_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_seterrormode_test1 coreclrpal)
+
+target_link_libraries(paltest_seterrormode_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/SetErrorMode/test1/test1.c b/src/pal/tests/palsuite/threading/SetErrorMode/test1/test1.c
new file mode 100644
index 0000000000..238cec4421
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetErrorMode/test1/test1.c
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test1.c (SetErrorMode)
+**
+** Purpose: Tests the PAL implementation of the SetErrorMode function.
+** This test will set the error mode and then read the error
+** set with GetLastError().
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+int __cdecl main(int argc, char **argv)
+{
+ DWORD dErrorReturn;
+ UINT dErrorModes[] = {SEM_NOOPENFILEERRORBOX, SEM_FAILCRITICALERRORS, 0};
+ int i;
+
+ /*
+ * Initialize the Pal
+ */
+ if ((PAL_Initialize(argc,argv)) != 0)
+ {
+ return (FAIL);
+ }
+
+ /*
+ * Loop through the supported Error Modes and verify
+ * that GetLastError() returns the correct Error Mode
+ */
+ for (i=0; i < (sizeof(dErrorModes) / sizeof(UINT)); i++)
+ {
+ SetLastError(dErrorModes[i]);
+ if ((dErrorReturn = GetLastError()) != dErrorModes[i])
+ {
+ Fail("ERROR: SetLastError was set to 0x%4.4x but,"
+ " GetLastError returned 0x%4.4x\n",
+ dErrorModes[i],
+ dErrorReturn);
+ }
+ }
+
+ PAL_Terminate();
+ return (PASS);
+}
diff --git a/src/pal/tests/palsuite/threading/SetErrorMode/test1/testinfo.dat b/src/pal/tests/palsuite/threading/SetErrorMode/test1/testinfo.dat
new file mode 100644
index 0000000000..875ac2a223
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetErrorMode/test1/testinfo.dat
@@ -0,0 +1,16 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = Threading
+Function = SetErrorMode
+Name = Positive Test for SetErrorMode
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Tests the PAL implementation of the SetErrorMode function.
+= This test will set the error mode and then read the error
+= set with GetLastError().
+
+
diff --git a/src/pal/tests/palsuite/threading/SetEvent/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetEvent/CMakeLists.txt
new file mode 100644
index 0000000000..a3847f8ca9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/CMakeLists.txt
@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+add_subdirectory(test4)
+
diff --git a/src/pal/tests/palsuite/threading/SetEvent/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetEvent/test1/CMakeLists.txt
new file mode 100644
index 0000000000..9be8d5a421
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_setevent_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_setevent_test1 coreclrpal)
+
+target_link_libraries(paltest_setevent_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/SetEvent/test1/test1.c b/src/pal/tests/palsuite/threading/SetEvent/test1/test1.c
new file mode 100644
index 0000000000..d5a29ce3f3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test1/test1.c
@@ -0,0 +1,94 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test1.c
+**
+** Purpose: Test for SetEvent. Create an Event and then set
+** this event, checking the return value. Ensure that it returns
+** positive.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+BOOL SetEventTest()
+{
+ int bRet = 0;
+ DWORD dwRet = 0;
+
+ LPSECURITY_ATTRIBUTES lpEventAttributes = 0;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = FALSE;
+
+ /* Create an event which we can use with SetEvent */
+ HANDLE hEvent = CreateEvent( lpEventAttributes,
+ bManualReset, bInitialState, NULL);
+
+ if (hEvent != INVALID_HANDLE_VALUE)
+ {
+ dwRet = WaitForSingleObject(hEvent,0);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("SetEventTest:WaitForSingleObject failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ /* Set the event to the previously created event and check
+ the return value.
+ */
+ bRet = SetEvent(hEvent);
+
+ if (!bRet)
+ {
+ Trace("SetEventTest:SetEvent failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ dwRet = WaitForSingleObject(hEvent,0);
+
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Trace("SetEventTest:WaitForSingleObject failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ dwRet = CloseHandle(hEvent);
+
+ if (!dwRet)
+ {
+ Trace("SetEventTest:CloseHandle failed (%x)\n", GetLastError());
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Trace("SetEventTest:CreateEvent failed (%x)\n", GetLastError());
+ }
+
+ return bRet;
+}
+
+
+int __cdecl main(int argc, char **argv)
+{
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(SetEventTest() == 0)
+ {
+ Fail ("Test failed\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/SetEvent/test1/testinfo.dat b/src/pal/tests/palsuite/threading/SetEvent/test1/testinfo.dat
new file mode 100644
index 0000000000..9bfd80829c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test1/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = SetEvent
+Name = Positive Test for SetEvent
+TYPE = DEFAULT
+EXE1 = test1
+Description
+=Test for SetEvent. Create an Event and then set
+=this event, checking the return value. Ensure that it returns
+=positive.
diff --git a/src/pal/tests/palsuite/threading/SetEvent/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetEvent/test2/CMakeLists.txt
new file mode 100644
index 0000000000..87522b3ffc
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_setevent_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_setevent_test2 coreclrpal)
+
+target_link_libraries(paltest_setevent_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/SetEvent/test2/test2.c b/src/pal/tests/palsuite/threading/SetEvent/test2/test2.c
new file mode 100644
index 0000000000..5fd2833957
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test2/test2.c
@@ -0,0 +1,125 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test2.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateEvent
+** CloseHandle
+** WaitForSingleObject
+**
+** Purpose:
+**
+** Test to ensure proper operation of the SetEvent()
+** API by calling it on an event handle that's already set.
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ DWORD dwRet = 0;
+ HANDLE hEvent = NULL;
+ LPSECURITY_ATTRIBUTES lpEventAttributes = NULL;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = FALSE;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* create an event which we can use with SetEvent */
+ hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ NULL );
+
+ if( hEvent == INVALID_HANDLE_VALUE )
+ {
+ /* ERROR */
+ Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ }
+
+ /* verify that the event isn't signalled yet */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ CloseHandle( hEvent );
+ Fail( "Test failed\n" );
+ }
+
+ /* set the event */
+ if( ! SetEvent( hEvent ) )
+ {
+ /* ERROR */
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ CloseHandle( hEvent );
+ Fail( "Test failed\n" );
+ }
+
+ /* verify that the event is signalled */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ CloseHandle( hEvent );
+ Fail( "Test failed\n" );
+ }
+
+ /* try to set the event again */
+ if( ! SetEvent( hEvent ) )
+ {
+ /* ERROR */
+ Trace( "FAIL:%lu:SetEvent() call failed on signalled event\n",
+ GetLastError() );
+ CloseHandle( hEvent );
+ Fail( "Test failed\n" );
+ }
+
+ /* verify that the event is still signalled */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ CloseHandle( hEvent );
+ Fail( "Test failed\n" );
+ }
+
+
+ /* close the event handle */
+ if( ! CloseHandle( hEvent ) )
+ {
+ Fail( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/SetEvent/test2/testinfo.dat b/src/pal/tests/palsuite/threading/SetEvent/test2/testinfo.dat
new file mode 100644
index 0000000000..f2153052bb
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test2/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = SetEvent
+Name = Positive test for SetEvent
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Test to ensure proper operation of the SetEvent()
+= API by calling it on an event handle that's already set.
diff --git a/src/pal/tests/palsuite/threading/SetEvent/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetEvent/test3/CMakeLists.txt
new file mode 100644
index 0000000000..d09239b3e4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test3.c
+)
+
+add_executable(paltest_setevent_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_setevent_test3 coreclrpal)
+
+target_link_libraries(paltest_setevent_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/SetEvent/test3/test3.c b/src/pal/tests/palsuite/threading/SetEvent/test3/test3.c
new file mode 100644
index 0000000000..21601f00b8
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test3/test3.c
@@ -0,0 +1,85 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test3.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateEvent
+** CloseHandle
+**
+** Purpose:
+**
+** Test to ensure proper operation of the SetEvent()
+** API by calling it on an event handle that's been
+** closed. We expect it to return an appropriate error
+** result.
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ HANDLE hEvent = NULL;
+ LPSECURITY_ATTRIBUTES lpEventAttributes = NULL;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = FALSE;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* create an event which we can use with SetEvent */
+ hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ NULL );
+
+ if( hEvent == INVALID_HANDLE_VALUE )
+ {
+ /* ERROR */
+ Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ }
+
+ /* close the event handle */
+ if( ! CloseHandle( hEvent ) )
+ {
+ Fail( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
+ }
+
+ /* try to set the event */
+ if( SetEvent( hEvent ) )
+ {
+ /* ERROR */
+ Fail( "FAIL:SetEvent() call succeeded on a closed event handle\n" );
+ }
+
+ /* verify the result of GetLastError() */
+ if( GetLastError() != ERROR_INVALID_HANDLE )
+ {
+ /* ERROR */
+ Fail( "FAIL:SetEvent() call failed on a closed event handle"
+ "but returned an unexpected error result %lu\n" );
+ }
+
+
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/SetEvent/test3/testinfo.dat b/src/pal/tests/palsuite/threading/SetEvent/test3/testinfo.dat
new file mode 100644
index 0000000000..7b8f43013a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test3/testinfo.dat
@@ -0,0 +1,15 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = SetEvent
+Name = Negative test for SetEvent
+TYPE = DEFAULT
+EXE1 = test3
+Description
+= Test to ensure proper operation of the SetEvent()
+= API by calling it on an event handle that's been
+= closed. We expect it to return an appropriate error
+= result.
diff --git a/src/pal/tests/palsuite/threading/SetEvent/test4/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetEvent/test4/CMakeLists.txt
new file mode 100644
index 0000000000..3f1d344292
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test4/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test4.c
+)
+
+add_executable(paltest_setevent_test4
+ ${SOURCES}
+)
+
+add_dependencies(paltest_setevent_test4 coreclrpal)
+
+target_link_libraries(paltest_setevent_test4
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/SetEvent/test4/test4.c b/src/pal/tests/palsuite/threading/SetEvent/test4/test4.c
new file mode 100644
index 0000000000..7a79a9d708
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test4/test4.c
@@ -0,0 +1,161 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test4.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** CreateEvent
+** CloseHandle
+** WaitForSingleObject
+** DuplicateHandle
+** GetCurrentProcess
+**
+** Purpose:
+**
+** Test to ensure proper operation of the SetEvent()
+** API by calling it on an event handle that's the
+** result of a DuplicateHandle() call on another event
+** handle.
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ DWORD dwRet = 0;
+ HANDLE hEvent = NULL;
+ HANDLE hDupEvent = NULL;
+ LPSECURITY_ATTRIBUTES lpEventAttributes = NULL;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = FALSE;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* create an event which we can use with SetEvent */
+ hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ NULL );
+
+ if( hEvent == INVALID_HANDLE_VALUE )
+ {
+ /* ERROR */
+ Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ }
+
+ /* verify that the event isn't signalled yet */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ CloseHandle( hEvent );
+ Fail( "Test failed\n" );
+ }
+
+
+ /* duplicate the event handle */
+ if( ! (DuplicateHandle(
+ GetCurrentProcess(),
+ hEvent,
+ GetCurrentProcess(),
+ &hDupEvent,
+ GENERIC_READ|GENERIC_WRITE, /* ignored in PAL */
+ FALSE,
+ DUPLICATE_SAME_ACCESS ) ) )
+ {
+ Trace("ERROR:%u:DuplicateHandle() call failed\n",
+ GetLastError() );
+ CloseHandle( hEvent );
+ Fail("Test failed\n");
+ }
+
+ /* verify that the event isn't signalled yet with the duplicate handle */
+ dwRet = WaitForSingleObject( hDupEvent, 0 );
+ if( dwRet != WAIT_TIMEOUT )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_TIMEOUT\n",
+ dwRet );
+ CloseHandle( hEvent );
+ CloseHandle( hDupEvent );
+ Fail( "Test failed\n" );
+ }
+
+
+ /* set the event using the duplicate handle */
+ if( ! SetEvent( hDupEvent ) )
+ {
+ /* ERROR */
+ Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
+ CloseHandle( hEvent );
+ CloseHandle( hDupEvent );
+ Fail( "Test failed\n" );
+ }
+
+ /* verify that the event is signalled using the duplicate handle*/
+ dwRet = WaitForSingleObject( hDupEvent, 0 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ CloseHandle( hEvent );
+ Fail( "Test failed\n" );
+ }
+
+ /* verify that the event is signalled using the original event handle */
+ dwRet = WaitForSingleObject( hEvent, 0 );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ /* ERROR */
+ Trace( "ERROR:WaitForSingleObject() call returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ CloseHandle( hEvent );
+ Fail( "Test failed\n" );
+ }
+
+
+ /* close the duplicate event handle */
+ if( ! CloseHandle( hDupEvent ) )
+ {
+ Fail( "ERROR:%lu:CloseHandle() call failed for duplicate handle\n",
+ GetLastError() );
+ }
+
+
+ /* close the event handle */
+ if( ! CloseHandle( hEvent ) )
+ {
+ Fail( "ERROR:%lu:CloseHandle() call failed for original handle\n",
+ GetLastError() );
+ }
+
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/SetEvent/test4/testinfo.dat b/src/pal/tests/palsuite/threading/SetEvent/test4/testinfo.dat
new file mode 100644
index 0000000000..9a7f7ddb3b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test4/testinfo.dat
@@ -0,0 +1,15 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = SetEvent
+Name = Positive test for SetEvent
+TYPE = DEFAULT
+EXE1 = test4
+Description
+= Test to ensure proper operation of the SetEvent()
+= API by calling it on an event handle that's the
+= result of a DuplicateHandle() call on another event
+= handle.
diff --git a/src/pal/tests/palsuite/threading/Sleep/CMakeLists.txt b/src/pal/tests/palsuite/threading/Sleep/CMakeLists.txt
new file mode 100644
index 0000000000..ef14ea5352
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/Sleep/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+
diff --git a/src/pal/tests/palsuite/threading/Sleep/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/Sleep/test1/CMakeLists.txt
new file mode 100644
index 0000000000..b0d8db28a2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/Sleep/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ Sleep.c
+)
+
+add_executable(paltest_sleep_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_sleep_test1 coreclrpal)
+
+target_link_libraries(paltest_sleep_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/Sleep/test1/Sleep.c b/src/pal/tests/palsuite/threading/Sleep/test1/Sleep.c
new file mode 100644
index 0000000000..f7f7c91730
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/Sleep/test1/Sleep.c
@@ -0,0 +1,80 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: Sleep.c
+**
+** Purpose: Test to establish whether the Sleep function stops the thread from
+** executing for the specified times.
+**
+** Dependencies: GetSystemTime
+** Fail
+** Trace
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+DWORD SleepTimes[] =
+{
+ 0,
+ 50,
+ 100,
+ 500,
+ 2000
+};
+
+/* Milliseconds of error which are acceptable Function execution time, etc. */
+DWORD AcceptableTimeError = 150;
+
+int __cdecl main( int argc, char **argv )
+{
+ UINT64 OldTimeStamp;
+ UINT64 NewTimeStamp;
+ DWORD MaxDelta;
+ DWORD TimeDelta;
+ DWORD i;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ LARGE_INTEGER performanceFrequency;
+ if (!QueryPerformanceFrequency(&performanceFrequency))
+ {
+ return FAIL;
+ }
+
+ for( i = 0; i < sizeof(SleepTimes) / sizeof(DWORD); i++)
+ {
+ OldTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+ Sleep(SleepTimes[i]);
+ NewTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+ TimeDelta = NewTimeStamp - OldTimeStamp;
+
+ /* For longer intervals use a 10 percent tolerance */
+ if ((SleepTimes[i] * 0.1) > AcceptableTimeError)
+ {
+ MaxDelta = SleepTimes[i] + (DWORD)(SleepTimes[i] * 0.1);
+ }
+ else
+ {
+ MaxDelta = SleepTimes[i] + AcceptableTimeError;
+ }
+
+ if ( TimeDelta<SleepTimes[i] || TimeDelta>MaxDelta )
+ {
+ Fail("The sleep function slept for %d ms when it should have "
+ "slept for %d ms\n", TimeDelta, SleepTimes[i]);
+ }
+ }
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/Sleep/test1/testinfo.dat b/src/pal/tests/palsuite/threading/Sleep/test1/testinfo.dat
new file mode 100644
index 0000000000..433a061f2c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/Sleep/test1/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = Sleep
+Name = Positive Test for Sleep
+TYPE = DEFAULT
+EXE1 = sleep
+Description
+= Test to see if the Sleep function stops the thread from executing for the
+= specified amount of time.
diff --git a/src/pal/tests/palsuite/threading/Sleep/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/Sleep/test2/CMakeLists.txt
new file mode 100644
index 0000000000..832e8ab42d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/Sleep/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ sleep.c
+)
+
+add_executable(paltest_sleep_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_sleep_test2 coreclrpal)
+
+target_link_libraries(paltest_sleep_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/Sleep/test2/sleep.c b/src/pal/tests/palsuite/threading/Sleep/test2/sleep.c
new file mode 100644
index 0000000000..eb30e34f2f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/Sleep/test2/sleep.c
@@ -0,0 +1,73 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: Sleep.c
+**
+** Purpose: Test to establish whether the Sleep function stops the thread from
+** executing for the specified times.
+**
+** Dependencies: GetTickCount
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+/*
+ * times in 10^(-3) seconds
+ */
+
+DWORD SleepTimes[] =
+{
+ 60000,
+ 300000,
+ 1800000,
+ 3200000
+};
+
+/* Milliseconds of error which are acceptable Function execution time, etc. */
+DWORD AcceptableTimeError = 150;
+
+int __cdecl main( int argc, char **argv )
+{
+ UINT64 OldTimeStamp;
+ UINT64 NewTimeStamp;
+ DWORD MaxDelta;
+ DWORD TimeDelta;
+ DWORD i;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ LARGE_INTEGER performanceFrequency;
+ if (!QueryPerformanceFrequency(&performanceFrequency))
+ {
+ return FAIL;
+ }
+
+ for( i = 0; i < sizeof(SleepTimes) / sizeof(DWORD); i++)
+ {
+ OldTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+ Sleep(SleepTimes[i]);
+ NewTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+ TimeDelta = NewTimeStamp - OldTimeStamp;
+
+ MaxDelta = SleepTimes[i] + AcceptableTimeError;
+
+ if ( TimeDelta<SleepTimes[i] || TimeDelta>MaxDelta )
+ {
+ Fail("The sleep function slept for %u ms when it should have "
+ "slept for %u ms\n", TimeDelta, SleepTimes[i]);
+ }
+ }
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/Sleep/test2/testinfo.dat b/src/pal/tests/palsuite/threading/Sleep/test2/testinfo.dat
new file mode 100644
index 0000000000..433a061f2c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/Sleep/test2/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = Sleep
+Name = Positive Test for Sleep
+TYPE = DEFAULT
+EXE1 = sleep
+Description
+= Test to see if the Sleep function stops the thread from executing for the
+= specified amount of time.
diff --git a/src/pal/tests/palsuite/threading/SleepEx/CMakeLists.txt b/src/pal/tests/palsuite/threading/SleepEx/CMakeLists.txt
new file mode 100644
index 0000000000..ef14ea5352
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+
diff --git a/src/pal/tests/palsuite/threading/SleepEx/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/SleepEx/test1/CMakeLists.txt
new file mode 100644
index 0000000000..de562755fc
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_sleepex_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_sleepex_test1 coreclrpal)
+
+target_link_libraries(paltest_sleepex_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/SleepEx/test1/test1.c b/src/pal/tests/palsuite/threading/SleepEx/test1/test1.c
new file mode 100644
index 0000000000..7ccfe0ce87
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/test1/test1.c
@@ -0,0 +1,89 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test1.c
+**
+** Purpose: Tests that SleepEx correctly sleeps for a given amount of time,
+** regardless of the alertable flag.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+typedef struct
+{
+ DWORD SleepTime;
+ BOOL Alertable;
+} testCase;
+
+testCase testCases[] =
+{
+ {0, FALSE},
+ {50, FALSE},
+ {100, FALSE},
+ {500, FALSE},
+ {2000, FALSE},
+
+ {0, TRUE},
+ {50, TRUE},
+ {100, TRUE},
+ {500, TRUE},
+ {2000, TRUE},
+};
+
+/* Milliseconds of error which are acceptable Function execution time, etc. */
+DWORD AcceptableTimeError = 150;
+
+int __cdecl main( int argc, char **argv )
+{
+ UINT64 OldTimeStamp;
+ UINT64 NewTimeStamp;
+ DWORD MaxDelta;
+ DWORD TimeDelta;
+ DWORD i;
+
+ if (0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ LARGE_INTEGER performanceFrequency;
+ if (!QueryPerformanceFrequency(&performanceFrequency))
+ {
+ return FAIL;
+ }
+
+ for (i = 0; i<sizeof(testCases) / sizeof(testCases[0]); i++)
+ {
+ OldTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+ SleepEx(testCases[i].SleepTime, testCases[i].Alertable);
+
+ NewTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+ TimeDelta = NewTimeStamp - OldTimeStamp;
+
+ /* For longer intervals use a 10 percent tolerance */
+ if ((testCases[i].SleepTime * 0.1) > AcceptableTimeError)
+ {
+ MaxDelta = testCases[i].SleepTime + (DWORD)(testCases[i].SleepTime * 0.1);
+ }
+ else
+ {
+ MaxDelta = testCases[i].SleepTime + AcceptableTimeError;
+ }
+
+ if (TimeDelta < testCases[i].SleepTime || TimeDelta > MaxDelta)
+ {
+ Fail("The sleep function slept for %d ms when it should have "
+ "slept for %d ms\n", TimeDelta, testCases[i].SleepTime);
+ }
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/SleepEx/test1/testinfo.dat b/src/pal/tests/palsuite/threading/SleepEx/test1/testinfo.dat
new file mode 100644
index 0000000000..1242768743
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/test1/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = SleepEx
+Name = Test #1 for SleepEx
+TYPE = DEFAULT
+EXE1 = test1
+Description
+=Tests that SleepEx correctly sleeps for a given amount of time,
+=regardless of the alertable flag.
diff --git a/src/pal/tests/palsuite/threading/SleepEx/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/SleepEx/test2/CMakeLists.txt
new file mode 100644
index 0000000000..0ea4a44449
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_sleepex_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_sleepex_test2 coreclrpal)
+
+target_link_libraries(paltest_sleepex_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/SleepEx/test2/test2.c b/src/pal/tests/palsuite/threading/SleepEx/test2/test2.c
new file mode 100644
index 0000000000..c2ba4e704d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/test2/test2.c
@@ -0,0 +1,183 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test2.c
+**
+** Purpose: Tests that a child thread in the middle of a SleepEx call will be
+** interrupted by QueueUserAPC if the alert flag was set.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+const int ChildThreadSleepTime = 2000;
+const int InterruptTime = 1000;
+/* We need to keep in mind that BSD has a timer resolution of 10ms, so
+ we need to adjust our delta to keep that in mind. Besides we need some
+ tolerance to account for different scheduling strategies, heavy load
+ scenarios, etc.
+
+ Real-world data also tells us we can expect a big difference between
+ values when run on real iron vs run in a hypervisor.
+
+ Thread-interruption times when run on bare metal will typically yield
+ around 0ms on Linux and between 0 and 16ms on FreeBSD. However, when run
+ in a hypervisor (like VMWare ESXi) we may get values around an order of
+ magnitude higher, up to 110 ms for some tests.
+*/
+const DWORD AcceptableDelta = 150;
+
+const int Iterations = 5;
+
+void RunTest(BOOL AlertThread);
+VOID PALAPI APCFunc(ULONG_PTR dwParam);
+DWORD PALAPI SleeperProc(LPVOID lpParameter);
+
+DWORD ThreadSleepDelta;
+
+int __cdecl main( int argc, char **argv )
+{
+ int i;
+ DWORD dwAvgDelta;
+
+ if (0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /*
+ On some platforms (e.g. FreeBSD 4.9) the first call to some synch objects
+ (such as conditions) involves some pthread internal initialization that
+ can make the first wait slighty longer, potentially going above the
+ acceptable delta for this test. Let's add a dummy wait to preinitialize
+ internal structures
+ */
+ Sleep(100);
+
+ /*
+ * Check that Queueing an APC in the middle of a sleep does interrupt
+ * it, if it's in an alertable state.
+ */
+ dwAvgDelta = 0;
+ for (i=0;i<Iterations;i++)
+ {
+ RunTest(TRUE);
+ dwAvgDelta += ThreadSleepDelta - InterruptTime;
+ }
+ dwAvgDelta /= Iterations;
+
+ if (dwAvgDelta > AcceptableDelta)
+ {
+ Fail("Expected thread to sleep for %d ms (and get interrupted).\n"
+ "Average delta: %u ms, acceptable delta: %u\n",
+ InterruptTime, dwAvgDelta, AcceptableDelta);
+ }
+
+ /*
+ * Check that Queueing an APC in the middle of a sleep does NOT interrupt
+ * it, if it is not in an alertable state.
+ */
+ dwAvgDelta = 0;
+ for (i=0;i<Iterations;i++)
+ {
+ RunTest(FALSE);
+ dwAvgDelta += ThreadSleepDelta - ChildThreadSleepTime;
+ }
+ dwAvgDelta /= Iterations;
+
+ if (dwAvgDelta > AcceptableDelta)
+ {
+ Fail("Expected thread to sleep for %d ms (and not be interrupted).\n"
+ "Average delta: %u ms, acceptable delta: %u\n",
+ ChildThreadSleepTime, dwAvgDelta, AcceptableDelta);
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
+void RunTest(BOOL AlertThread)
+{
+ HANDLE hThread = 0;
+ DWORD dwThreadId = 0;
+ int ret;
+
+ hThread = CreateThread( NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)SleeperProc,
+ (LPVOID) AlertThread,
+ 0,
+ &dwThreadId);
+
+ if (hThread == NULL)
+ {
+ Fail("ERROR: Was not able to create the thread to test SleepEx!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ if (SleepEx(InterruptTime, FALSE) != 0)
+ {
+ Fail("The creating thread did not sleep!\n");
+ }
+
+ ret = QueueUserAPC(APCFunc, hThread, 0);
+ if (ret == 0)
+ {
+ Fail("QueueUserAPC failed! GetLastError returned %d\n", GetLastError());
+ }
+
+ ret = WaitForSingleObject(hThread, INFINITE);
+ if (ret == WAIT_FAILED)
+ {
+ Fail("Unable to wait on child thread!\nGetLastError returned %d.",
+ GetLastError());
+ }
+}
+
+/* Function doesn't do anything, just needed to interrupt SleepEx */
+VOID PALAPI APCFunc(ULONG_PTR dwParam)
+{
+
+}
+
+/* Entry Point for child thread. */
+DWORD PALAPI SleeperProc(LPVOID lpParameter)
+{
+ UINT64 OldTimeStamp;
+ UINT64 NewTimeStamp;
+ BOOL Alertable;
+ DWORD ret;
+
+ Alertable = (BOOL) lpParameter;
+
+ LARGE_INTEGER performanceFrequency;
+ if (!QueryPerformanceFrequency(&performanceFrequency))
+ {
+ return FAIL;
+ }
+
+ OldTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+ ret = SleepEx(ChildThreadSleepTime, Alertable);
+
+ NewTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+ if (Alertable && ret != WAIT_IO_COMPLETION)
+ {
+ Fail("Expected the interrupted sleep to return WAIT_IO_COMPLETION.\n"
+ "Got %d\n", ret);
+ }
+ else if (!Alertable && ret != 0)
+ {
+ Fail("Sleep did not timeout. Expected return of 0, got %d.\n", ret);
+ }
+
+
+ ThreadSleepDelta = NewTimeStamp - OldTimeStamp;
+
+ return 0;
+}
diff --git a/src/pal/tests/palsuite/threading/SleepEx/test2/testinfo.dat b/src/pal/tests/palsuite/threading/SleepEx/test2/testinfo.dat
new file mode 100644
index 0000000000..52f3ce0af6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/test2/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = SleepEx
+Name = Test #2 for SleepEx
+TYPE = DEFAULT
+EXE1 = test2
+Description
+=Tests that a child thread in the middle of a SleepEx call will be
+=interrupted by QueueUserAPC if the alert flag was set.
+
diff --git a/src/pal/tests/palsuite/threading/SwitchToThread/CMakeLists.txt b/src/pal/tests/palsuite/threading/SwitchToThread/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SwitchToThread/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/SwitchToThread/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/SwitchToThread/test1/CMakeLists.txt
new file mode 100644
index 0000000000..40ecc910b0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SwitchToThread/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_switchtothread_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_switchtothread_test1 coreclrpal)
+
+target_link_libraries(paltest_switchtothread_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/SwitchToThread/test1/test1.c b/src/pal/tests/palsuite/threading/SwitchToThread/test1/test1.c
new file mode 100644
index 0000000000..76ecdd3572
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SwitchToThread/test1/test1.c
@@ -0,0 +1,97 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test1.c
+**
+** Purpose: Test to ensure SwitchToThread works, without
+** causing test to hang
+**
+** Dependencies: PAL_Initialize
+** Fail
+** SwitchToThread
+** WaitForMultipleObject
+** CreateThread
+** GetLastError
+**
+
+**
+**===========================================================================*/
+
+
+#include <palsuite.h>
+#define THREAD_COUNT 10
+#define REPEAT_COUNT 1000
+#define TIMEOUT 60000
+void PALAPI Run_Thread(LPVOID lpParam);
+
+/**
+ * main
+ *
+ * executable entry point
+ */
+INT __cdecl main( INT argc, CHAR **argv )
+{
+ DWORD dwParam;
+ HANDLE hThread[THREAD_COUNT];
+ DWORD threadId[THREAD_COUNT];
+
+ int i = 0;
+ int returnCode = 0;
+
+ /*PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return FAIL;
+ }
+
+
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ dwParam = (int) i;
+ //Create thread
+ hThread[i] = CreateThread(
+ NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE)Run_Thread,/* thread function */
+ (LPVOID)dwParam, /* argument to thread function */
+ 0, /* use default creation flags */
+ &threadId[i] /* returns the thread identifier*/
+ );
+
+ if(hThread[i] == NULL)
+ {
+ Fail("Create Thread failed for iteration %d GetLastError value is %d\n", i, GetLastError());
+ }
+
+ }
+
+
+ returnCode = WaitForMultipleObjects(THREAD_COUNT, hThread, TRUE, TIMEOUT);
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) returned %d, expected value is %d, and GetLastError value is %d\n", returnCode, WAIT_OBJECT_0, GetLastError());
+ }
+
+ PAL_Terminate();
+ return PASS;
+
+}
+
+void PALAPI Run_Thread (LPVOID lpParam)
+{
+ int i = 0;
+ int Id=(int)lpParam;
+
+ for(i=0; i < REPEAT_COUNT; i++ )
+ {
+ // No Last Error is set..
+ if(!SwitchToThread())
+ {
+ Trace( "The operating system did not switch execution to another thread,"
+ "for thread id[%d], iteration [%d]\n", Id, i );
+ }
+ }
+}
diff --git a/src/pal/tests/palsuite/threading/SwitchToThread/test1/testinfo.dat b/src/pal/tests/palsuite/threading/SwitchToThread/test1/testinfo.dat
new file mode 100644
index 0000000000..15ee8d4d4e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SwitchToThread/test1/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = SwitchToThread
+Name = Test for SwitchToThread
+TYPE = DEFAULT
+EXE1 = test
+Description
+= Purpose: Test to ensure SwitchToThread is
+= working properly on supported platforms
diff --git a/src/pal/tests/palsuite/threading/TLS/CMakeLists.txt b/src/pal/tests/palsuite/threading/TLS/CMakeLists.txt
new file mode 100644
index 0000000000..bffdf7f714
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/CMakeLists.txt
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+add_subdirectory(test4)
+add_subdirectory(test5)
+
+
diff --git a/src/pal/tests/palsuite/threading/TLS/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/TLS/test1/CMakeLists.txt
new file mode 100644
index 0000000000..ff1a866eb9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ TLS.c
+)
+
+add_executable(paltest_tls_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_tls_test1 coreclrpal)
+
+target_link_libraries(paltest_tls_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/TLS/test1/TLS.c b/src/pal/tests/palsuite/threading/TLS/test1/TLS.c
new file mode 100644
index 0000000000..4300c3f98b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test1/TLS.c
@@ -0,0 +1,182 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: tls.c
+**
+** Purpose: Test to ensure TlsAlloc, TlsGetValue, TlsSetValue
+** and TlsFree are working properly together.
+**
+** Dependencies: PAL_Initialize
+** Fail
+** Sleep
+** LocalAlloc
+** LocalFree
+** WaitForSingleObject
+** CreateThread
+** GetLastError
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+#define NUM_OF_THREADS 10
+
+DWORD dwTlsIndex; /* TLS index */
+
+/**
+ * CommonFunction
+ *
+ * Helper function that calls TlsGetValue
+ */
+VOID CommonFunction(VOID)
+{
+ LPVOID lpvData;
+ DWORD dwError;
+
+ /* Retrieve a data pointer for the current thread. */
+ lpvData = TlsGetValue(dwTlsIndex);
+
+ if ( (lpvData == 0) &&
+ ((dwError = GetLastError()) != NO_ERROR) )
+ {/*ERROR */
+ Fail("TlsGetValue(%d) returned 0 with error %d\n",
+ dwTlsIndex,
+ dwError);
+ }
+
+ Sleep(5000);
+}
+
+/**
+ * ThreadFunc
+ *
+ * Thread function that stores a value in the thread's tls slot
+ * for the predefined tls index
+ */
+DWORD PALAPI ThreadFunc(LPVOID lpThreadParameter)
+{
+ LPVOID lpvData;
+ DWORD dwError;
+
+ /* Initialize the TLS index for this thread.*/
+ lpvData = (LPVOID) LocalAlloc(0, 256);
+
+ if( lpvData == NULL )
+ {/*ERROR */
+ dwError = GetLastError();
+ Fail("Unexpected LocalAlloc(0, 256) failure with error %d\n",
+ dwError);
+ }
+
+
+ if ( TlsSetValue(dwTlsIndex, lpvData) == 0 )
+ {/*ERROR */
+ dwError = GetLastError();
+ Fail("TlsSetValue(%d, %x) returned 0 with error %d\n",
+ dwTlsIndex,
+ lpvData,
+ dwError);
+ }
+
+ CommonFunction();
+
+ /* Release the dynamic memory. */
+ lpvData = TlsGetValue(dwTlsIndex);
+
+ if ( (lpvData == 0) &&
+ ((dwError = GetLastError()) != NO_ERROR) )
+ {/*ERROR */
+ Fail("TlsGetValue(%d) returned 0 with error %d\n",
+ dwTlsIndex,
+ dwError);
+ }
+ else
+ {
+ if( LocalFree((HLOCAL) lpvData) != NULL )
+ {
+ dwError = GetLastError();
+ Fail("Unexpected LocalFree(%x) failure with error %d\n",
+ lpvData,
+ dwError);
+ }
+ }
+
+ return PASS;
+}
+
+/**
+ * main
+ *
+ * executable entry point
+ */
+INT __cdecl main( INT argc, CHAR **argv )
+{
+ DWORD IDThread;
+ HANDLE hThread[NUM_OF_THREADS];
+ int i;
+
+ /*PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return FAIL;
+ }
+
+ /*Allocate a TLS index. */
+ if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
+ {/*RROR*/
+ DWORD dwError = GetLastError();
+ Fail("TlsAlloc() returned error %d\n",
+ dwError);
+ }
+
+ /*Create multiple threads.*/
+
+ for (i = 0; i < NUM_OF_THREADS; i++)
+ {
+ hThread[i] = CreateThread(NULL, /* no security attributes*/
+ 0, /* use default stack size */
+ ThreadFunc, /* thread function */
+ NULL, /* no thread function argument */
+ 0, /* use default creation flags */
+ &IDThread); /* returns thread identifier */
+
+ /* Check the return value for success. */
+ if (hThread[i] == NULL)
+ {/* ERROR */
+ DWORD dwError = GetLastError();
+ Fail("Unexpected CreateThread error %d\n",
+ dwError);
+ }
+ }
+
+ /* Wait for all threads to finish */
+ for (i = 0; i < NUM_OF_THREADS; i++)
+ {
+ DWORD dwRet;
+
+ dwRet = WaitForSingleObject(hThread[i], INFINITE);
+
+ if( dwRet == WAIT_FAILED )
+ {/* ERROR */
+ DWORD dwError = GetLastError();
+ Fail("Unexpected WaitForSingleObject error %d\n",
+ dwError);
+ }
+ }
+
+ /* Release the TLS index */
+ if( TlsFree( dwTlsIndex ) == 0 )
+ {/* ERROR */
+ DWORD dwError = GetLastError();
+ Fail("TlsFree() returned 0 with error %d\n",
+ dwError);
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
diff --git a/src/pal/tests/palsuite/threading/TLS/test1/testinfo.dat b/src/pal/tests/palsuite/threading/TLS/test1/testinfo.dat
new file mode 100644
index 0000000000..544e391266
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test1/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = TLS
+Name = Test for TlsAlloc, TlsGetValue, TlsSetValue and TlsFree
+TYPE = DEFAULT
+EXE1 = tls
+Description
+= Test to ensure TlsAlloc, TlsGetValue, TlsSetValue
+= and TlsFree are working properly together.
diff --git a/src/pal/tests/palsuite/threading/TLS/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/TLS/test2/CMakeLists.txt
new file mode 100644
index 0000000000..5afe82b4a6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ TLS.c
+)
+
+add_executable(paltest_tls_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_tls_test2 coreclrpal)
+
+target_link_libraries(paltest_tls_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/TLS/test2/TLS.c b/src/pal/tests/palsuite/threading/TLS/test2/TLS.c
new file mode 100644
index 0000000000..96a6011f96
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test2/TLS.c
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: tls.c
+**
+** Purpose: Test to ensure TlsAlloc and TlsFree are working when we try
+** to allocate the guaranted minimum number of indicies.
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+#define NUM_OF_INDEX 64
+/* Minimum guaranteed is at least 64 for all systems.*/
+
+/**
+ * main
+ *
+ * executable entry point
+ */
+INT __cdecl main( INT argc, CHAR **argv )
+{
+ DWORD dwIndexes[NUM_OF_INDEX];
+ int i,j;
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return FAIL;
+ }
+
+ /* Allocate a bunch of TLS indexes. */
+ for( i = 0; i < NUM_OF_INDEX; i++ )
+ {
+ if( (dwIndexes[i] = TlsAlloc()) == TLS_OUT_OF_INDEXES )
+ {/*ERROR */
+ DWORD dwError = GetLastError();
+ Fail("TlsAlloc() returned -1 with error %d"
+ "when trying to allocate %d index\n",
+ dwError,
+ i);
+ }
+ }
+
+ /* Free the TLS indexes.*/
+ for( j = 0; j < NUM_OF_INDEX; j++ )
+ {
+ if( TlsFree(dwIndexes[j]) == 0 )
+ {/* ERROR */
+ DWORD dwError = GetLastError();
+ Fail("TlsFree() returned 0 with error %d"
+ "when trying to free %d index\n",
+ dwError,
+ i);
+ }
+ }
+
+ PAL_Terminate();
+
+ return PASS;
+}
+
diff --git a/src/pal/tests/palsuite/threading/TLS/test2/testinfo.dat b/src/pal/tests/palsuite/threading/TLS/test2/testinfo.dat
new file mode 100644
index 0000000000..3a672a5f38
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test2/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = TLS
+Name = Test for TlsAlloc and TlsFree
+TYPE = DEFAULT
+EXE1 = tls
+Description
+= Test to ensure TlsAlloc and TlsFree are working when we try
+= to allocate the guaranted minimum number of index.
diff --git a/src/pal/tests/palsuite/threading/TLS/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/TLS/test3/CMakeLists.txt
new file mode 100644
index 0000000000..0964d33d2c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ TLS.c
+)
+
+add_executable(paltest_tls_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_tls_test3 coreclrpal)
+
+target_link_libraries(paltest_tls_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/TLS/test3/TLS.c b/src/pal/tests/palsuite/threading/TLS/test3/TLS.c
new file mode 100644
index 0000000000..4acaef5020
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test3/TLS.c
@@ -0,0 +1,90 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: tls.c
+**
+** Purpose: Test to ensure TlsGetValue, TlsSetValue and TlsFree
+** are not working with an invalid index
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** LocalAlloc
+** LocalFree
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+DWORD dwTlsIndex; /* TLS index */
+
+/**
+ * main
+ *
+ * executable entry point
+ */
+INT __cdecl main( INT argc, CHAR **argv )
+{
+ CHAR lpstrData[256] = "";
+ LPVOID lpvData = NULL;
+ BOOL bRet;
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return FAIL;
+ }
+
+ /* Invalid TLS index */
+ dwTlsIndex = -1;
+
+ /*
+ * Set some data in the invalid TLS index
+ *Should return 0 and an error
+ */
+ bRet = TlsSetValue(dwTlsIndex, (LPVOID)lpstrData);
+
+ if ( bRet != 0)
+ {/*ERROR */
+ Fail("TlsSetValue(%d, %x) returned %d "
+ "when it should have returned 0 and an error\n",
+ dwTlsIndex,
+ lpvData,
+ bRet);
+ }
+
+ /*
+ * Get the data at the invalid index
+ * Should return 0 and an error
+ */
+ lpvData = TlsGetValue(dwTlsIndex);
+
+ if ( lpvData != 0 )
+ {/* ERROR */
+ Fail("TlsGetValue(%d) returned %d "
+ "when it should have returned 0 and an error\n",
+ dwTlsIndex,
+ lpvData);
+ }
+
+ /*
+ * Release the invalid TLS index
+ * Should return 0 and an error
+ */
+ bRet = TlsFree( dwTlsIndex );
+
+ if( bRet != 0 )
+ {/* ERROR */
+ Fail("TlsFree() returned %d "
+ "when it should have returned 0 and an error\n",
+ bRet);
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
+
diff --git a/src/pal/tests/palsuite/threading/TLS/test3/testinfo.dat b/src/pal/tests/palsuite/threading/TLS/test3/testinfo.dat
new file mode 100644
index 0000000000..63ce59f351
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test3/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = TLS
+Name = TlsGetValue, TlsSetValue and TlsFree
+TYPE = DEFAULT
+EXE1 = tls
+Description
+= Test to ensure TlsGetValue, TlsSetValue and TlsFree
+= are not working with an invalid index
diff --git a/src/pal/tests/palsuite/threading/TLS/test4/CMakeLists.txt b/src/pal/tests/palsuite/threading/TLS/test4/CMakeLists.txt
new file mode 100644
index 0000000000..7e7b47786a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test4/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test4.c
+)
+
+add_executable(paltest_tls_test4
+ ${SOURCES}
+)
+
+add_dependencies(paltest_tls_test4 coreclrpal)
+
+target_link_libraries(paltest_tls_test4
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/TLS/test4/test4.c b/src/pal/tests/palsuite/threading/TLS/test4/test4.c
new file mode 100644
index 0000000000..8c3603cdb0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test4/test4.c
@@ -0,0 +1,137 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test4.c (threading/tls)
+**
+** Purpose: Test to ensure that upon key creation, the value NULL
+** is associated with the new key in all active threads.
+** Upon thread creation, the value NULL is associated
+** with all defined keys in the new thread.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** LocalAlloc
+** LocalFree
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+#define NUM_OF_THREADS 10
+
+DWORD dwTlsIndex; /* TLS index */
+
+/**
+ * ThreadFunc
+ *
+ * Thread function that checks that NULL is associated with the tls index
+ */
+DWORD PALAPI ThreadFunc(VOID)
+{
+ LPVOID lpvData;
+ DWORD dwError;
+
+ /* Retrieve a data pointer for the current thread.
+ The return value should be NULL since no data has been
+ set in the index */
+ lpvData = TlsGetValue(dwTlsIndex);
+
+ if ( (lpvData != NULL) &&
+ ((dwError = GetLastError()) == NO_ERROR) )
+ {/*ERROR */
+ Fail("TlsGetValue(%d) returned data "
+ "even if no data was associated with the index\n",
+ dwTlsIndex);
+ }
+
+ return PASS;
+}
+
+/**
+ * main
+ *
+ * executable entry point
+ */
+INT __cdecl main( INT argc, CHAR **argv )
+{
+ DWORD IDThread;
+ LPVOID lpvData;
+ DWORD dwError;
+ HANDLE hThread[NUM_OF_THREADS];
+ int i;
+
+ /*PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return FAIL;
+ }
+
+ /*Allocate a TLS index. */
+ if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
+ {/*ERROR*/
+ DWORD dwError = GetLastError();
+ Fail("TlsAlloc() returned error %d\n",
+ dwError);
+ }
+
+ /*Check that the value associated with the tls index is NULL*/
+ lpvData = TlsGetValue(dwTlsIndex);
+
+ if ( (lpvData != NULL) &&
+ ((dwError = GetLastError()) == NO_ERROR) )
+ {/*ERROR */
+ Fail("TlsGetValue(%d) returned non-null data "
+ "even if no data was associated with the index\n",
+ dwTlsIndex);
+ }
+
+ /*Create multiple threads.*/
+ for (i = 0; i < NUM_OF_THREADS; i++)
+ {
+ hThread[i] = CreateThread(NULL, /* no security attributes*/
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE) ThreadFunc, /* thread function */
+ NULL, /* no thread function argument */
+ 0, /* use default creation flags */
+ &IDThread); /* returns thread identifier */
+
+ /* Check the return value for success. */
+ if (hThread[i] == NULL)
+ {/* ERROR */
+ DWORD dwError = GetLastError();
+ Fail("Unexpected CreateThread error %d\n",
+ dwError);
+ }
+ }
+
+ /* Wait for all threads to finish */
+ for (i = 0; i < NUM_OF_THREADS; i++)
+ {
+ DWORD dwRet;
+
+ dwRet = WaitForSingleObject(hThread[i], INFINITE);
+
+ if( dwRet == WAIT_FAILED )
+ {/* ERROR */
+ DWORD dwError = GetLastError();
+ Fail("Unexpected WaitForSingleObject error %d\n",
+ dwError);
+ }
+ }
+
+ /* Release the TLS index */
+ if( TlsFree( dwTlsIndex ) == 0 )
+ {/* ERROR */
+ DWORD dwError = GetLastError();
+ Fail("TlsFree() returned 0 with error %d\n",
+ dwError);
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
diff --git a/src/pal/tests/palsuite/threading/TLS/test4/testinfo.dat b/src/pal/tests/palsuite/threading/TLS/test4/testinfo.dat
new file mode 100644
index 0000000000..6001770642
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test4/testinfo.dat
@@ -0,0 +1,15 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = TLS
+Name = TlsAlloc, TlsGetValue
+TYPE = DEFAULT
+EXE1 = test4
+Description
+=Test to ensure that upon key creation, the value NULL
+=is associated with the new key in all active threads.
+=Upon thread creation, the value NULL is associated
+=with all defined keys in the new thread.
diff --git a/src/pal/tests/palsuite/threading/TLS/test5/CMakeLists.txt b/src/pal/tests/palsuite/threading/TLS/test5/CMakeLists.txt
new file mode 100644
index 0000000000..5fb5c9ddfd
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test5/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test5.c
+)
+
+add_executable(paltest_tls_test5
+ ${SOURCES}
+)
+
+add_dependencies(paltest_tls_test5 coreclrpal)
+
+target_link_libraries(paltest_tls_test5
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/TLS/test5/test5.c b/src/pal/tests/palsuite/threading/TLS/test5/test5.c
new file mode 100644
index 0000000000..c1cd132937
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test5/test5.c
@@ -0,0 +1,108 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test5.c (threading/tls)
+**
+** Purpose: Test that creates a key, sets its value, deletes the key,
+** creates a new key, and gets its value to make sure its NULL.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** LocalAlloc
+** LocalFree
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+DWORD dwTlsIndex; /* TLS index */
+
+/**
+ * main
+ *
+ * executable entry point
+ */
+INT __cdecl main( INT argc, CHAR **argv )
+{
+ LPVOID lpvData;
+ DWORD dwError;
+
+ /*PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return FAIL;
+ }
+
+ /**
+ * create a key, set its value, delete the key
+ */
+
+ /*Allocate a TLS index. */
+ if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
+ {/*ERROR*/
+ DWORD dwError = GetLastError();
+ Fail("TlsAlloc() returned error %d\n",
+ dwError);
+ }
+
+ /* Initialize the TLS index for this thread.*/
+ lpvData = (LPVOID) LocalAlloc(0, 256);
+
+ if( lpvData == NULL )
+ {/*ERROR */
+ dwError = GetLastError();
+ Fail("Unexpected LocalAlloc(0, 256) failure with error %d\n",
+ dwError);
+ }
+
+ if ( TlsSetValue(dwTlsIndex, lpvData) == 0 )
+ {/*ERROR */
+ dwError = GetLastError();
+ Fail("TlsSetValue(%d, %x) returned 0 with error %d\n",
+ dwTlsIndex,
+ lpvData,
+ dwError);
+ }
+
+ /* Release the TLS index */
+ if( TlsFree( dwTlsIndex ) == 0 )
+ {/* ERROR */
+ DWORD dwError = GetLastError();
+ Fail("TlsFree() returned 0 with error %d\n",
+ dwError);
+ }
+
+
+ /**
+ * create a new key, and get its value
+ */
+
+ /*Allocate a TLS index. */
+ if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
+ {/*ERROR*/
+ DWORD dwError = GetLastError();
+ Fail("TlsAlloc() returned error %d\n",
+ dwError);
+ }
+
+ /* Retrieve a data pointer for the current thread.
+ The return value should be NULL since no data has been
+ set in the index */
+ lpvData = TlsGetValue(dwTlsIndex);
+
+ if ( (lpvData != NULL) &&
+ ((dwError = GetLastError()) == NO_ERROR) )
+ {/*ERROR */
+ Fail("TlsGetValue(%d) returned data "
+ "even if no data was associated with the index\n",
+ dwTlsIndex);
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
diff --git a/src/pal/tests/palsuite/threading/TLS/test5/testinfo.dat b/src/pal/tests/palsuite/threading/TLS/test5/testinfo.dat
new file mode 100644
index 0000000000..4b3e2a64c5
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test5/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = TLS
+Name = TlsAlloc and TlsGetValue
+TYPE = DEFAULT
+EXE1 = test5
+Description
+= Test that creates a key, sets its value, deletes the key,
+= creates a new key, and gets its value to make sure its NULL.
diff --git a/src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/CMakeLists.txt b/src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/CMakeLists.txt
new file mode 100644
index 0000000000..8b3a4ed64a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test.c
+)
+
+add_executable(paltest_tls_test6_optimizedtls
+ ${SOURCES}
+)
+
+add_dependencies(paltest_tls_test6_optimizedtls coreclrpal)
+
+target_link_libraries(paltest_tls_test6_optimizedtls
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/test.c b/src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/test.c
new file mode 100644
index 0000000000..02419dc90c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/test.c
@@ -0,0 +1,190 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test.c
+**
+** Purpose: Test to ensure TlsAlloc, PAL_MakeOptimizedTlsGetter,
+** PAL_FreeOptimizedTlsGetter and TlsFree are working properly
+** on supported platforms
+**
+** Dependencies: PAL_Initialize
+** Fail
+** Sleep
+** LocalAlloc
+** LocalFree
+** WaitForSingleObject
+** CreateThread
+** GetLastError
+**
+
+**
+**===========================================================================*/
+
+
+#include <palsuite.h>
+#define THREAD_COUNT 5
+DWORD dwTlsIndex; /* TLS index */
+
+void PALAPI Run_Thread(LPVOID lpParam);
+
+/**
+ * main
+ *
+ * executable entry point
+ */
+INT __cdecl main( INT argc, CHAR **argv )
+{
+ DWORD dwParam;
+ DWORD dwError;
+ HANDLE hThread[THREAD_COUNT];
+ DWORD threadId[THREAD_COUNT];
+
+ int i = 0;
+ int returnCode = 0;
+
+ /*PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return FAIL;
+ }
+
+ /*Allocate a TLS index. */
+ if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
+ {
+ /*ERROR*/
+ dwError = GetLastError();
+ Fail("TlsAlloc() returned error %d\n",
+ dwError);
+ }
+
+
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ dwParam = (int) i;
+ //Create thread
+ hThread[i] = CreateThread(
+ NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE)Run_Thread,/* thread function */
+ (LPVOID)dwParam, /* argument to thread function */
+ 0, /* use default creation flags */
+ &threadId[i] /* returns the thread identifier*/
+ );
+
+ if(hThread[i] == NULL)
+ {
+ Fail("Create Thread failed for iteration %d GetLastError value is %d\n", i, GetLastError());
+ }
+
+ }
+
+
+ returnCode = WaitForMultipleObjects(THREAD_COUNT, hThread, TRUE, INFINITE);
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) returned %d, expected value is %d, and GetLastError value is %d\n", returnCode, WAIT_OBJECT_0, GetLastError());
+ }
+
+ /* Release the TLS index */
+ if( TlsFree( dwTlsIndex ) == 0 )
+ {
+ /* ERROR */
+ dwError = GetLastError();
+ Fail("TlsFree() returned 0 with error %d\n",
+ dwError);
+ }
+
+ PAL_Terminate();
+ return PASS;
+
+}
+
+void PALAPI Run_Thread (LPVOID lpParam)
+{
+ unsigned int i = 0;
+
+ LPVOID lpvData;
+ DWORD dwError;
+ PAL_POPTIMIZEDTLSGETTER ptrOptimizedTlsGetter;
+
+ int Id=(int)lpParam;
+
+
+ lpvData = TlsGetValue(dwTlsIndex);
+ if ( (lpvData != NULL) &&
+ ((dwError = GetLastError()) == NO_ERROR) )
+ {
+ /*ERROR */
+ Fail("Error:%d:TlsGetValue(%d) returned data "
+ "even if data was not associated with the index, for thread id [%d]\n",
+ dwError, dwTlsIndex, Id);
+ }
+
+
+ /* Initialize the TLS index for this thread.*/
+ lpvData = (LPVOID) LocalAlloc(0, 256);
+
+ if( lpvData == NULL )
+ {
+ /*ERROR */
+ dwError = GetLastError();
+ Fail("Unexpected LocalAlloc(0, 256) failure with error %d\n",
+ dwError);
+ }
+
+ if ( TlsSetValue(dwTlsIndex, lpvData) == 0 )
+ {
+ /*ERROR */
+ dwError = GetLastError();
+ Fail("TlsSetValue(%d, %x) returned 0 with error %d\n",
+ dwTlsIndex,
+ lpvData,
+ dwError);
+ }
+
+ ptrOptimizedTlsGetter = PAL_MakeOptimizedTlsGetter(dwTlsIndex);
+ if( ptrOptimizedTlsGetter == NULL )
+ {
+ /* Retrieve a data pointer for the current thread.
+ The return value should be NULL since no data has been
+ set in the index */
+ lpvData = TlsGetValue(dwTlsIndex);
+ Trace("Not Inside the optimizer loop for thread [%d]\n", Id);
+
+ if ( (lpvData == NULL) &&
+ ((dwError = GetLastError()) == NO_ERROR) )
+ {
+ /*ERROR */
+ Fail("Error:%d:TlsGetValue(%d) returned data "
+ "as NULL even if data was associated with the index, for thread id [%d]\n",
+ dwError, dwTlsIndex, Id);
+ }
+ }
+ else
+ {
+ /* Retrieve a data pointer for the current thread.
+ The return value should be NULL since no data has been
+ set in the index */
+ lpvData = ptrOptimizedTlsGetter();
+
+ if ( (lpvData == NULL) &&
+ ((dwError = GetLastError()) == NO_ERROR) )
+ {
+ /*ERROR */
+ Fail(" Error:%d: MakeOptimizedTlsGetter for dwTlsIndex (%d) returned data "
+ "as NULL even if no data was associated with the index, for thread id [%d]\n",
+ dwError, dwTlsIndex, Id);
+ }
+
+ Trace("Inside the optimizer loop for thread [%d]\n", Id);
+ PAL_FreeOptimizedTlsGetter(ptrOptimizedTlsGetter);
+ }
+
+
+
+
+}
+
diff --git a/src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/testinfo.dat b/src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/testinfo.dat
new file mode 100644
index 0000000000..2193edcab3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = PAL_MakeOptimizedTlsGetter and PAL_FreeOptimizedTlsGetter
+Name = Test for PAL_MakeOptimizedTlsGetterandPAL_FreeOptimizedTlsGetter
+TYPE = DEFAULT
+EXE1 = test
+Description
+= Purpose: Test to ensure TlsAlloc, PAL_MakeOptimizedTlsGetter,
+= PAL_FreeOptimizedTlsGetter and TlsFree are working properly
+= on supported platforms
diff --git a/src/pal/tests/palsuite/threading/TerminateProcess/CMakeLists.txt b/src/pal/tests/palsuite/threading/TerminateProcess/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TerminateProcess/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/TerminateProcess/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/TerminateProcess/test1/CMakeLists.txt
new file mode 100644
index 0000000000..37d575952f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TerminateProcess/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ TerminateProcess.c
+)
+
+add_executable(paltest_terminateprocess_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_terminateprocess_test1 coreclrpal)
+
+target_link_libraries(paltest_terminateprocess_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/TerminateProcess/test1/TerminateProcess.c b/src/pal/tests/palsuite/threading/TerminateProcess/test1/TerminateProcess.c
new file mode 100644
index 0000000000..6feedfce76
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TerminateProcess/test1/TerminateProcess.c
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: terminateprocess/test1/terminateprocess.c
+**
+** Purpose: Test to see if TerminateProcess will
+** terminate the current process.
+**
+** Dependencies: GetCurrentProcess
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+INT __cdecl main( int argc, char **argv )
+{
+
+ HANDLE hProcess;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return (FAIL);
+ }
+
+ hProcess = GetCurrentProcess();
+
+ Trace ("Testing TerminateProcess function.\n");
+
+ if ( 0 == ( TerminateProcess ( hProcess, PASS ) ) )
+ {
+ Fail ("TerminateProcess failed.\n");
+ }
+
+ PAL_TerminateEx(FAIL);
+ return (FAIL);
+
+}
diff --git a/src/pal/tests/palsuite/threading/TerminateProcess/test1/testinfo.dat b/src/pal/tests/palsuite/threading/TerminateProcess/test1/testinfo.dat
new file mode 100644
index 0000000000..7ee69bfa29
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TerminateProcess/test1/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = TerminateProcess
+Name = Positive Test for TerminateProcess
+TYPE = DEFAULT
+EXE1 = terminateprocess
+Description
+= Test to see if the function TerminateProcess terminates the currently
+= running process and passes the appropriate value through uExitCode.
+
diff --git a/src/pal/tests/palsuite/threading/ThreadPriority/CMakeLists.txt b/src/pal/tests/palsuite/threading/ThreadPriority/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ThreadPriority/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/ThreadPriority/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/ThreadPriority/test1/CMakeLists.txt
new file mode 100644
index 0000000000..e341ca7435
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ThreadPriority/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ ThreadPriority.c
+)
+
+add_executable(paltest_threadpriority_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_threadpriority_test1 coreclrpal)
+
+target_link_libraries(paltest_threadpriority_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/ThreadPriority/test1/ThreadPriority.c b/src/pal/tests/palsuite/threading/ThreadPriority/test1/ThreadPriority.c
new file mode 100644
index 0000000000..95bcdac52a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ThreadPriority/test1/ThreadPriority.c
@@ -0,0 +1,83 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: threadpriority.c
+**
+** Purpose: Test to ensure GetThreadPriority works properly.
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** Fail
+** CreateThread
+** WaitForSingleObject
+** GetLastError
+** time()
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+/**
+ * CheckThreadPriority
+ *
+ * Helper function that checks the current thread priority
+ * against an expected value.
+ */
+static VOID CheckThreadPriority( HANDLE hThread, int expectedPriority )
+{
+ int priority;
+ DWORD dwError = 0;
+
+ /* get the current thread priority */
+ priority = GetThreadPriority( hThread );
+ if( priority == THREAD_PRIORITY_ERROR_RETURN )
+ {
+ /* GetThreadPriority call failed */
+ dwError = GetLastError();
+ Fail( "Unexpected GetThreadPriority() failure "
+ "with error %d\n", dwError );
+ }
+ else if( priority != expectedPriority )
+ {
+ /* unexpected thread priority detected */
+ Fail( "Unexpected initial thread priority value %d reported\n",
+ priority );
+ }
+}
+
+
+/**
+ * main
+ *
+ * executable entry point
+ */
+INT __cdecl main( INT argc, CHAR **argv )
+{
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+ /* set the thread priority of the main to the highest possible value
+ this will give the chance to the main thread to create all the
+ other threads */
+ if(!SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL))
+ {
+ DWORD dwError;
+
+ dwError = GetLastError();
+ Fail( "Unexpected SetThreadPriority() failure with error %d\n",
+ dwError );
+ }
+
+ CheckThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL );
+ //Add verification of timing out here..
+ PAL_Terminate();
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/ThreadPriority/test1/testinfo.dat b/src/pal/tests/palsuite/threading/ThreadPriority/test1/testinfo.dat
new file mode 100644
index 0000000000..0abd9c1e06
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ThreadPriority/test1/testinfo.dat
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ThreadPriority
+Name = Test for GetThreadPriority and SetThreadPriority
+TYPE = DEFAULT
+EXE1 = threadpriority
+Description
+= Test to ensure proper operation of the GetThreadPriority
+= and SetThreadPriority APIs. The test launches several threads
+= of varying priorities, and verifies that the correct priority
+= is reported for each. It also verifies that the processing
+= time for each test thread is consistent with the priority
+= that's set for it.
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjects/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/CMakeLists.txt
new file mode 100644
index 0000000000..2b1f946b26
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_waitformultipleobjects_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_waitformultipleobjects_test1 coreclrpal)
+
+target_link_libraries(paltest_waitformultipleobjects_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/test1.c b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/test1.c
new file mode 100644
index 0000000000..8249c38d9d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/test1.c
@@ -0,0 +1,224 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test1.c
+**
+** Purpose: Test for WaitForMultipleObjects. Call the function
+** on an array of 4 events, and ensure that it returns correct
+** results when we do so.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+/* Number of events in array */
+#define MAX_EVENTS 4
+
+BOOL WaitForMultipleObjectsTest()
+{
+ BOOL bRet = TRUE;
+ DWORD dwRet = 0;
+
+ DWORD i = 0, j = 0;
+
+ LPSECURITY_ATTRIBUTES lpEventAttributes = NULL;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = TRUE;
+
+ HANDLE hEvent[MAX_EVENTS];
+
+ /* Run through this for loop and create 4 events */
+ for (i = 0; i < MAX_EVENTS; i++)
+ {
+ hEvent[i] = CreateEvent( lpEventAttributes,
+ bManualReset, bInitialState, NULL);
+
+ if (hEvent[i] == INVALID_HANDLE_VALUE)
+ {
+ Trace("WaitForMultipleObjectsTest:CreateEvent %u failed (%x)\n", i, GetLastError());
+ bRet = FALSE;
+ break;
+ }
+
+ /* Set the current event */
+ bRet = SetEvent(hEvent[i]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsTest:SetEvent %u failed (%x)\n", i, GetLastError());
+ bRet = FALSE;
+ break;
+ }
+
+ /* Ensure that this returns the correct value */
+ dwRet = WaitForSingleObject(hEvent[i],0);
+
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Trace("WaitForMultipleObjectsTest:WaitForSingleObject %u failed (%x)\n", i, GetLastError());
+ bRet = FALSE;
+ break;
+ }
+
+ /* Reset the event, and again ensure that the return value of
+ WaitForSingle is correct.
+ */
+ bRet = ResetEvent(hEvent[i]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsTest:ResetEvent %u failed (%x)\n", i, GetLastError());
+ bRet = FALSE;
+ break;
+ }
+
+ dwRet = WaitForSingleObject(hEvent[i],0);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("WaitForMultipleObjectsTest:WaitForSingleObject %u failed (%x)\n", i, GetLastError());
+ bRet = FALSE;
+ break;
+ }
+ }
+
+ /*
+ * If the first section of the test passed, move on to the
+ * second.
+ */
+
+ if (bRet)
+ {
+ BOOL bWaitAll = TRUE;
+ DWORD nCount = MAX_EVENTS;
+ CONST HANDLE *lpHandles = &hEvent[0];
+
+ /* Call WaitForMultipleOjbects on all the events, the return
+ should be WAIT_TIMEOUT
+ */
+ dwRet = WaitForMultipleObjects( nCount,
+ lpHandles,
+ bWaitAll,
+ 0);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("WaitForMultipleObjectsTest:WaitForMultipleObjects failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ /* Step through each event and one at a time, set the
+ currect test, while reseting all the other tests
+ */
+
+ for (i = 0; i < MAX_EVENTS; i++)
+ {
+ for (j = 0; j < MAX_EVENTS; j++)
+ {
+ if (j == i)
+ {
+
+ bRet = SetEvent(hEvent[j]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsTest:SetEvent %u failed (%x)\n", j, GetLastError());
+ break;
+ }
+ }
+ else
+ {
+ bRet = ResetEvent(hEvent[j]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsTest:ResetEvent %u failed (%x)\n", j, GetLastError());
+ }
+ }
+ }
+
+ bWaitAll = FALSE;
+
+ /* Check that WaitFor returns WAIT_OBJECT + i */
+ dwRet = WaitForMultipleObjects( nCount,
+ lpHandles, bWaitAll, 0);
+
+ if (dwRet != WAIT_OBJECT_0+i)
+ {
+ Trace("WaitForMultipleObjectsTest:WaitForMultipleObjects failed (%x)\n", GetLastError());
+ bRet = FALSE;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < MAX_EVENTS; i++)
+ {
+ bRet = CloseHandle(hEvent[i]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsTest:CloseHandle %u failed (%x)\n", i, GetLastError());
+ }
+ }
+ }
+
+ return bRet;
+}
+
+BOOL WaitMultipleDuplicateHandleTest()
+{
+ BOOL testResult = TRUE;
+ const HANDLE eventHandle = CreateEvent(NULL, TRUE, TRUE, NULL);
+ HANDLE eventHandles[] = {eventHandle, eventHandle};
+
+ // WaitAny - Wait for any of the events (no error expected)
+ DWORD result = WaitForMultipleObjects(sizeof(eventHandles) / sizeof(eventHandles[0]), eventHandles, FALSE, 0);
+ if (result != WAIT_OBJECT_0)
+ {
+ Trace("WaitMultipleDuplicateHandleTest:WaitAny failed (%x)\n", GetLastError());
+ testResult = FALSE;
+ }
+
+ // WaitAll - Wait for all of the events (error expected)
+ result = WaitForMultipleObjects(sizeof(eventHandles) / sizeof(eventHandles[0]), eventHandles, TRUE, 0);
+ if (result != WAIT_FAILED)
+ {
+ Trace("WaitMultipleDuplicateHandleTest:WaitAll failed: call unexpectedly succeeded\n");
+ testResult = FALSE;
+ }
+ else if (GetLastError() != ERROR_INVALID_PARAMETER)
+ {
+ Trace("WaitMultipleDuplicateHandleTest:WaitAll failed: unexpected last error (%x)\n");
+ testResult = FALSE;
+ }
+
+ return testResult;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!WaitForMultipleObjectsTest())
+ {
+ Fail ("Test failed\n");
+ }
+
+ if (!WaitMultipleDuplicateHandleTest())
+ {
+ Fail("Test failed\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/testinfo.dat b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/testinfo.dat
new file mode 100644
index 0000000000..38bd350d64
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = WaitForMultipleObjects
+Name = Positive Test for WaitForMultipleObjects
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Test for WaitForMultipleObjects. Call the function
+= on an array of 4 events, and ensure that it returns correct
+= results when we do so.
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/CMakeLists.txt
new file mode 100644
index 0000000000..7c20179353
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/CMakeLists.txt
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+add_subdirectory(test3)
+add_subdirectory(test4)
+add_subdirectory(test5)
+add_subdirectory(test6)
+
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/CMakeLists.txt
new file mode 100644
index 0000000000..d035021bb9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_waitformultipleobjectsex_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_waitformultipleobjectsex_test1 coreclrpal)
+
+target_link_libraries(paltest_waitformultipleobjectsex_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/test1.c b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/test1.c
new file mode 100644
index 0000000000..df3233fa50
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/test1.c
@@ -0,0 +1,226 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test1.c
+**
+** Purpose: Test for WaitForMultipleObjectsEx. Call the function
+** on an array of 4 events, and ensure that it returns correct
+** results when we do so.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+/* Originally written as WaitForMultipleObjects/test1 */
+
+
+/* Number of events in array */
+#define MAX_EVENTS 4
+
+BOOL WaitForMultipleObjectsExTest()
+{
+ BOOL bRet = TRUE;
+ DWORD dwRet = 0;
+ DWORD i = 0, j = 0;
+
+ LPSECURITY_ATTRIBUTES lpEventAttributes = NULL;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = TRUE;
+
+ HANDLE hEvent[MAX_EVENTS];
+
+ /* Run through this for loop and create 4 events */
+ for (i = 0; i < MAX_EVENTS; i++)
+ {
+ hEvent[i] = CreateEvent( lpEventAttributes,
+ bManualReset, bInitialState, NULL);
+
+ if (hEvent[i] == INVALID_HANDLE_VALUE)
+ {
+ Trace("WaitForMultipleObjectsExTest:CreateEvent %u failed (%x)\n", i, GetLastError());
+ bRet = FALSE;
+ break;
+ }
+
+ /* Set the current event */
+ bRet = SetEvent(hEvent[i]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsExTest:SetEvent %u failed (%x)\n", i, GetLastError());
+ bRet = FALSE;
+ break;
+ }
+
+ /* Ensure that this returns the correct value */
+ dwRet = WaitForSingleObject(hEvent[i],0);
+
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Trace("WaitForMultipleObjectsExTest:WaitForSingleObject %u failed (%x)\n", i, GetLastError());
+ bRet = FALSE;
+ break;
+ }
+
+ /* Reset the event, and again ensure that the return value of
+ WaitForSingle is correct.
+ */
+ bRet = ResetEvent(hEvent[i]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsExTest:ResetEvent %u failed (%x)\n", i, GetLastError());
+ bRet = FALSE;
+ break;
+ }
+
+ dwRet = WaitForSingleObject(hEvent[i],0);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("WaitForMultipleObjectsExTest:WaitForSingleObject %u failed (%x)\n", i, GetLastError());
+ bRet = FALSE;
+ break;
+ }
+ }
+
+ /*
+ * If the first section of the test passed, move on to the
+ * second.
+ */
+
+ if (bRet)
+ {
+ BOOL bWaitAll = TRUE;
+ DWORD nCount = MAX_EVENTS;
+ CONST HANDLE *lpHandles = &hEvent[0];
+
+ /* Call WaitForMultipleObjectsEx on all the events, the return
+ should be WAIT_TIMEOUT
+ */
+ dwRet = WaitForMultipleObjectsEx(nCount,
+ lpHandles,
+ bWaitAll,
+ 0,
+ FALSE);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("WaitForMultipleObjectsExTest: WaitForMultipleObjectsEx failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ /* Step through each event and one at a time, set the
+ currect test, while reseting all the other tests
+ */
+
+ for (i = 0; i < MAX_EVENTS; i++)
+ {
+ for (j = 0; j < MAX_EVENTS; j++)
+ {
+ if (j == i)
+ {
+
+ bRet = SetEvent(hEvent[j]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsExTest:SetEvent %j failed (%x)\n", j, GetLastError());
+ break;
+ }
+ }
+ else
+ {
+ bRet = ResetEvent(hEvent[j]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsExTest:ResetEvent %u failed (%x)\n", j, GetLastError());
+ }
+ }
+ }
+
+ bWaitAll = FALSE;
+
+ /* Check that WaitFor returns WAIT_OBJECT + i */
+ dwRet = WaitForMultipleObjectsEx( nCount,
+ lpHandles, bWaitAll, 0, FALSE);
+
+ if (dwRet != WAIT_OBJECT_0+i)
+ {
+ Trace("WaitForMultipleObjectsExTest: WaitForMultipleObjectsEx failed (%x)\n", GetLastError());
+ bRet = FALSE;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < MAX_EVENTS; i++)
+ {
+ bRet = CloseHandle(hEvent[i]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsExTest:CloseHandle %u failed (%x)\n", i, GetLastError());
+ }
+ }
+ }
+
+ return bRet;
+}
+
+BOOL WaitMultipleDuplicateHandleTest()
+{
+ BOOL testResult = TRUE;
+ const HANDLE eventHandle = CreateEvent(NULL, TRUE, TRUE, NULL);
+ HANDLE eventHandles[] = {eventHandle, eventHandle};
+
+ // WaitAny - Wait for any of the events (no error expected)
+ DWORD result = WaitForMultipleObjects(sizeof(eventHandles) / sizeof(eventHandles[0]), eventHandles, FALSE, 0);
+ if (result != WAIT_OBJECT_0)
+ {
+ Trace("WaitMultipleDuplicateHandleTest:WaitAny failed (%x)\n", GetLastError());
+ testResult = FALSE;
+ }
+
+ // WaitAll - Wait for all of the events (error expected)
+ result = WaitForMultipleObjects(sizeof(eventHandles) / sizeof(eventHandles[0]), eventHandles, TRUE, 0);
+ if (result != WAIT_FAILED)
+ {
+ Trace("WaitMultipleDuplicateHandleTest:WaitAll failed: call unexpectedly succeeded\n");
+ testResult = FALSE;
+ }
+ else if (GetLastError() != ERROR_INVALID_PARAMETER)
+ {
+ Trace("WaitMultipleDuplicateHandleTest:WaitAll failed: unexpected last error (%x)\n");
+ testResult = FALSE;
+ }
+
+ return testResult;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!WaitForMultipleObjectsExTest())
+ {
+ Fail ("Test failed\n");
+ }
+
+ if (!WaitMultipleDuplicateHandleTest())
+ {
+ Fail("Test failed\n");
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/testinfo.dat b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/testinfo.dat
new file mode 100644
index 0000000000..596c4bbf32
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = WaitForMultipleObjectsEx
+Name = Test #1 for WaitForMultipleObjectsEx
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Test for WaitForMultipleObjectsEx. Call the function
+= on an array of 4 events, and ensure that it returns correct
+= results when we do so.
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/CMakeLists.txt
new file mode 100644
index 0000000000..ab47c6f2e7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_waitformultipleobjectsex_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_waitformultipleobjectsex_test2 coreclrpal)
+
+target_link_libraries(paltest_waitformultipleobjectsex_test2
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/test2.c b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/test2.c
new file mode 100644
index 0000000000..b2bc301128
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/test2.c
@@ -0,0 +1,188 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test2.c
+**
+** Purpose: Tests that a child thread in the middle of a
+** WaitForMultipleObjectsEx call will be interrupted by QueueUserAPC
+** if the alert flag was set.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+/* Based on SleepEx/test2 */
+
+const unsigned int ChildThreadWaitTime = 1000;
+const unsigned int InterruptTime = 500;
+
+#define TOLERANCE 10
+
+void RunTest(BOOL AlertThread);
+VOID PALAPI APCFunc(ULONG_PTR dwParam);
+DWORD PALAPI WaiterProc(LPVOID lpParameter);
+
+DWORD ThreadWaitDelta;
+
+int __cdecl main( int argc, char **argv )
+{
+
+ DWORD delta = 0;
+
+ if (0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /*
+ On some platforms (e.g. FreeBSD 4.9) the first call to some synch objects
+ (such as conditions) involves some pthread internal initialization that
+ can make the first wait slighty longer, potentially going above the
+ acceptable delta for this test. Let's add a dummy wait to preinitialize
+ internal structures
+ */
+ Sleep(100);
+
+
+ /*
+ * Check that Queueing an APC in the middle of a wait does interrupt
+ * it, if it's in an alertable state.
+ */
+ RunTest(TRUE);
+ // Make sure that the wait returns in time greater than interrupt and less than
+ // wait timeout
+ if (
+ ((ThreadWaitDelta >= ChildThreadWaitTime) && (ThreadWaitDelta - ChildThreadWaitTime) > TOLERANCE)
+ || (( ThreadWaitDelta < InterruptTime) && (ThreadWaitDelta - InterruptTime) > TOLERANCE)
+ )
+ {
+ Fail("Expected thread to wait for %d ms (and get interrupted).\n"
+ "Interrupt Time: %d ms, ThreadWaitDelta %u\n",
+ ChildThreadWaitTime, InterruptTime, ThreadWaitDelta);
+ }
+
+ /*
+ * Check that Queueing an APC in the middle of a wait does NOT interrupt
+ * it, if it is not in an alertable state.
+ */
+ RunTest(FALSE);
+
+ // Make sure that time taken for thread to return from wait is more than interrupt
+ // and also not less than the complete child thread wait time
+
+ delta = ThreadWaitDelta - ChildThreadWaitTime;
+ if( (ThreadWaitDelta < ChildThreadWaitTime) && ( delta > TOLERANCE) )
+ {
+ Fail("Expected thread to wait for %d ms (and not get interrupted).\n"
+ "Interrupt Time: %d ms, ThreadWaitDelta %u\n",
+ ChildThreadWaitTime, InterruptTime, ThreadWaitDelta);
+ }
+
+
+ PAL_Terminate();
+ return PASS;
+}
+
+void RunTest(BOOL AlertThread)
+{
+ HANDLE hThread = 0;
+ DWORD dwThreadId = 0;
+ int ret;
+
+ hThread = CreateThread( NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)WaiterProc,
+ (LPVOID) AlertThread,
+ 0,
+ &dwThreadId);
+
+ if (hThread == NULL)
+ {
+ Fail("ERROR: Was not able to create the thread to test!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ Sleep(InterruptTime);
+
+ ret = QueueUserAPC(APCFunc, hThread, 0);
+ if (ret == 0)
+ {
+ Fail("QueueUserAPC failed! GetLastError returned %d\n",
+ GetLastError());
+ }
+
+ ret = WaitForSingleObject(hThread, INFINITE);
+ if (ret == WAIT_FAILED)
+ {
+ Fail("Unable to wait on child thread!\nGetLastError returned %d.\n",
+ GetLastError());
+ }
+}
+
+/* Function doesn't do anything, just needed to interrupt the wait*/
+VOID PALAPI APCFunc(ULONG_PTR dwParam)
+{
+}
+
+/* Entry Point for child thread. */
+DWORD PALAPI WaiterProc(LPVOID lpParameter)
+{
+ HANDLE Semaphore;
+ UINT64 OldTimeStamp;
+ UINT64 NewTimeStamp;
+ BOOL Alertable;
+ DWORD ret;
+
+ /* Create a semaphore that is not in the signalled state */
+ Semaphore = CreateSemaphoreW(NULL, 0, 1, NULL);
+
+ if (Semaphore == NULL)
+ {
+ Fail("Failed to create semaphore! GetLastError returned %d.\n",
+ GetLastError());
+ }
+
+ Alertable = (BOOL) lpParameter;
+
+ LARGE_INTEGER performanceFrequency;
+ if (!QueryPerformanceFrequency(&performanceFrequency))
+ {
+ Fail("Failed to query performance frequency!");
+ }
+
+ OldTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+ ret = WaitForMultipleObjectsEx(1, &Semaphore, FALSE, ChildThreadWaitTime,
+ Alertable);
+
+ NewTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+
+ if (Alertable && ret != WAIT_IO_COMPLETION)
+ {
+ Fail("Expected the interrupted wait to return WAIT_IO_COMPLETION.\n"
+ "Got %d\n", ret);
+ }
+ else if (!Alertable && ret != WAIT_TIMEOUT)
+ {
+ Fail("WaitForMultipleObjectsEx did not timeout.\n"
+ "Expected return of WAIT_TIMEOUT, got %d.\n", ret);
+ }
+
+ ThreadWaitDelta = NewTimeStamp - OldTimeStamp;
+
+ ret = CloseHandle(Semaphore);
+ if (!ret)
+ {
+ Fail("Unable to close handle to semaphore!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ return 0;
+}
+
+
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/testinfo.dat b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/testinfo.dat
new file mode 100644
index 0000000000..e8e781a7f2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = WaitForMultipleObjectsEx
+Name = Test #2 for WaitForMultipleObjectsEx
+TYPE = DEFAULT
+EXE1 = test2
+Description
+=Tests that a child thread in the middle of a
+=WaitForMultipleObjectsEx call will be interrupted by QueueUserAPC
+=if the alert flag was set.
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/CMakeLists.txt
new file mode 100644
index 0000000000..645b86719d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test3.c
+)
+
+add_executable(paltest_waitformultipleobjectsex_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_waitformultipleobjectsex_test3 coreclrpal)
+
+target_link_libraries(paltest_waitformultipleobjectsex_test3
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/test3.c b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/test3.c
new file mode 100644
index 0000000000..b78b0540dc
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/test3.c
@@ -0,0 +1,106 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test3.c
+**
+** Purpose: Tests that waiting on an open mutex will a return
+** WAIT_OBJECT_0. Does this by creating a child thread that
+** acquires the mutex, releases it, and exits.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+
+const int ChildThreadWaitTime = 1000;
+const int ParentDelayTime = 2000;
+
+DWORD PALAPI AcquiringProc(LPVOID lpParameter);
+
+int __cdecl main( int argc, char **argv)
+{
+ HANDLE Mutex;
+ HANDLE hThread = 0;
+ DWORD dwThreadId = 0;
+ int ret;
+
+ if (0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ Mutex = CreateMutexW(NULL, FALSE, NULL);
+ if (Mutex == NULL)
+ {
+ Fail("Unable to create the mutex. GetLastError returned %d\n",
+ GetLastError());
+ }
+
+ hThread = CreateThread( NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)AcquiringProc,
+ (LPVOID) Mutex,
+ 0,
+ &dwThreadId);
+
+ if (hThread == NULL)
+ {
+ Fail("ERROR: Was not able to create the thread to test!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ Sleep(ParentDelayTime);
+
+ ret = WaitForMultipleObjectsEx(1, &Mutex, FALSE, INFINITE, FALSE);
+ if (ret != WAIT_OBJECT_0)
+ {
+ Fail("Expected WaitForMultipleObjectsEx to return WAIT_OBJECT_0\n"
+ "Got %d\n", ret);
+ }
+
+ if (!CloseHandle(Mutex))
+ {
+ Fail("CloseHandle on the mutex failed!\n");
+ }
+
+ if (!CloseHandle(hThread))
+ {
+ Fail("CloseHandle on the thread failed!\n");
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
+/*
+ * Entry Point for child thread. Acquries a mutex, releases it, and exits.
+ */
+DWORD PALAPI AcquiringProc(LPVOID lpParameter)
+{
+ HANDLE Mutex;
+ DWORD ret;
+
+ Mutex = (HANDLE) lpParameter;
+
+ Sleep(ChildThreadWaitTime);
+
+ ret = WaitForSingleObject(Mutex, 0);
+ if (ret != WAIT_OBJECT_0)
+ {
+ Fail("Expected the WaitForSingleObject call on the mutex to succeed\n"
+ "Expected return of WAIT_OBJECT_0, got %d\n", ret);
+ }
+
+ ret = ReleaseMutex(Mutex);
+ if (!ret)
+ {
+ Fail("Unable to release mutex! GetLastError returned %d\n",
+ GetLastError());
+ }
+
+ return 0;
+}
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/testinfo.dat b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/testinfo.dat
new file mode 100644
index 0000000000..991b93489d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = WaitForMultipleObjectsEx
+Name = Test #3 for WaitForMultipleObjectsEx
+TYPE = DEFAULT
+EXE1 = test3
+Description
+=Tests that waiting on an open mutex will a return
+=WAIT_OBJECT_0. Does this by creating a child thread that
+=acquires the mutex, releases it, and exits.
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/CMakeLists.txt
new file mode 100644
index 0000000000..3cd7f72b98
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test4.c
+)
+
+add_executable(paltest_waitformultipleobjectsex_test4
+ ${SOURCES}
+)
+
+add_dependencies(paltest_waitformultipleobjectsex_test4 coreclrpal)
+
+target_link_libraries(paltest_waitformultipleobjectsex_test4
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/test4.c b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/test4.c
new file mode 100644
index 0000000000..15d0a386d1
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/test4.c
@@ -0,0 +1,101 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test4.c
+**
+** Purpose: Tests that waiting on an abandonded mutex will a return
+** WAIT_ABANDONED_0. Does this by creating a child thread that
+** acquires the mutex and exits.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+
+const int ChildThreadWaitTime = 1000;
+const int ParentDelayTime = 2000;
+
+DWORD PALAPI AbandoningProc(LPVOID lpParameter);
+
+int __cdecl main( int argc, char **argv )
+{
+ HANDLE Mutex;
+ HANDLE hThread = 0;
+ DWORD dwThreadId = 0;
+ int ret;
+
+ if (0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ Mutex = CreateMutexW(NULL, FALSE, NULL);
+ if (Mutex == NULL)
+ {
+ Fail("Unable to create the mutex. GetLastError returned %d\n",
+ GetLastError());
+ }
+
+ hThread = CreateThread( NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)AbandoningProc,
+ (LPVOID) Mutex,
+ 0,
+ &dwThreadId);
+
+ if (hThread == NULL)
+ {
+ Fail("ERROR: Was not able to create the thread to test!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ Sleep(ParentDelayTime);
+
+ ret = WaitForMultipleObjectsEx(1, &Mutex, FALSE, INFINITE, FALSE);
+ if (ret != WAIT_ABANDONED_0)
+ {
+ Fail("Expected WaitForMultipleObjectsEx to return WAIT_ABANDONED_0\n"
+ "Got %d\n", ret);
+ }
+
+ ReleaseMutex(Mutex);
+ if (!CloseHandle(Mutex))
+ {
+ Fail("CloseHandle on the mutex failed!\n");
+ }
+
+ if (!CloseHandle(hThread))
+ {
+ Fail("CloseHandle on the thread failed!\n");
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
+/*
+ * Entry Point for child thread. Acquries a mutex and exit's without
+ * releasing it.
+ */
+DWORD PALAPI AbandoningProc(LPVOID lpParameter)
+{
+ HANDLE Mutex;
+ DWORD ret;
+
+ Mutex = (HANDLE) lpParameter;
+
+ Sleep(ChildThreadWaitTime);
+
+ ret = WaitForSingleObject(Mutex, 0);
+ if (ret != WAIT_OBJECT_0)
+ {
+ Fail("Expected the WaitForSingleObject call on the mutex to succeed\n"
+ "Expected return of WAIT_OBJECT_0, got %d\n", ret);
+ }
+
+ return 0;
+}
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/testinfo.dat b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/testinfo.dat
new file mode 100644
index 0000000000..16f3468ac2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = WaitForMultipleObjectsEx
+Name = Test #4 for WaitForMultipleObjectsEx
+TYPE = DEFAULT
+EXE1 = test4
+Description
+=Tests that waiting on an abandonded mutex will a return
+=WAIT_ABANDONED_0. Does this by creating a child thread that
+=acquires the mutex and exits.
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/CMakeLists.txt
new file mode 100644
index 0000000000..50147e9bf3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ test5.c
+)
+
+add_executable(paltest_waitformultipleobjectsex_test5
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_waitformultipleobjectsex_test5 coreclrpal)
+
+target_link_libraries(paltest_waitformultipleobjectsex_test5
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ helper.c
+)
+
+add_executable(paltest_waitformultipleobjectsex_test5_helper
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_waitformultipleobjectsex_test5_helper coreclrpal)
+
+target_link_libraries(paltest_waitformultipleobjectsex_test5_helper
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/commonconsts.h b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/commonconsts.h
new file mode 100644
index 0000000000..b746616b58
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/commonconsts.h
@@ -0,0 +1,42 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================
+**
+** Source: commonconsts.h
+**
+**
+**============================================================*/
+
+#ifndef _COMMONCONSTS_H_
+#define _COMMONCONSTS_H_
+
+#include <pal.h>
+
+const int TIMEOUT = 60 * 5 * 1000;
+
+char *szcHelperProcessStartEvName = "start";
+char *szcHelperProcessReadyEvName = "ready";
+char *szcHelperProcessFinishEvName = "finish";
+
+/* PEDANTIC and PEDANTIC0 is a helper macro that just grumps about any
+ * zero return codes in a generic way. with little typing */
+#define PEDANTIC(function, parameters) \
+{ \
+ if (! (function parameters) ) \
+ { \
+ Trace("%s: NonFatal failure of %s%s for reasons %u and %u\n", \
+ __FILE__, #function, #parameters, GetLastError(), errno); \
+ } \
+}
+#define PEDANTIC1(function, parameters) \
+{ \
+ if ( (function parameters) ) \
+ { \
+ Trace("%s: NonFatal failure of %s%s for reasons %u and %u\n", \
+ __FILE__, #function, #parameters, GetLastError(), errno); \
+ } \
+}
+
+#endif
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/helper.c b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/helper.c
new file mode 100644
index 0000000000..274780a216
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/helper.c
@@ -0,0 +1,122 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================
+**
+** Source: helper.c
+**
+** Purpose: This helper process sets up signals to communicate
+** with the test thread in the parent process, and let the test
+** thread signal this process when to exit.
+**
+**
+**============================================================*/
+
+#include "commonconsts.h"
+
+#include <palsuite.h>
+
+HANDLE hProcessStartEvent;
+HANDLE hProcessReadyEvent;
+HANDLE hProcessFinishEvent;
+HANDLE hProcessCleanupEvent;
+
+
+int __cdecl main(int argc, char *argv[])
+{
+
+ BOOL success = TRUE; /* assume success */
+ DWORD dwRet;
+ DWORD dwProcessId;
+ char szEventName[MAX_LONGPATH];
+ PWCHAR uniString;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /* Open the event to let test thread tell us to get started. */
+ uniString = convert(szcHelperProcessStartEvName);
+ hProcessStartEvent = OpenEventW(EVENT_ALL_ACCESS, 0, uniString);
+ free(uniString);
+ if (!hProcessStartEvent)
+ {
+ Fail("helper.main: OpenEvent of '%S' failed (%u). "
+ "(the event should already exist!)\n",
+ szcHelperProcessStartEvName, GetLastError());
+ }
+
+ /* Wait for signal from test thread. */
+ dwRet = WaitForSingleObject(hProcessStartEvent, TIMEOUT);
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Fail("helper.main: WaitForSingleObject '%s' failed\n"
+ "LastError:(%u)\n", szcHelperProcessStartEvName, GetLastError());
+ }
+
+ dwProcessId = GetCurrentProcessId();
+
+ if ( 0 >= dwProcessId )
+ {
+ Fail ("helper.main: %s has invalid pid %d\n", argv[0], dwProcessId );
+ }
+
+ /* Open the event to tell test thread we are ready. */
+ if (_snprintf(szEventName, MAX_LONGPATH-1, "%s%d", szcHelperProcessReadyEvName, dwProcessId) < 0)
+ {
+ Fail ("helper.main: Insufficient event name string length for pid=%d\n", dwProcessId);
+ }
+
+ uniString = convert(szEventName);
+
+ hProcessReadyEvent = OpenEventW(EVENT_ALL_ACCESS, 0, uniString);
+ free(uniString);
+ if (!hProcessReadyEvent)
+ {
+ Fail("helper.main: OpenEvent of '%s' failed (%u). "
+ "(the event should already exist!)\n",
+ szEventName, GetLastError());
+ }
+
+ /* Open the event to let test thread tell us to exit. */
+ if (_snprintf(szEventName, MAX_LONGPATH-1, "%s%d", szcHelperProcessFinishEvName, dwProcessId) < 0)
+ {
+ Fail ("helper.main: Insufficient event name string length for pid=%d\n", dwProcessId);
+ }
+
+ uniString = convert(szEventName);
+
+ hProcessFinishEvent = OpenEventW(EVENT_ALL_ACCESS, 0, uniString);
+ free(uniString);
+ if (!hProcessFinishEvent)
+ {
+ Fail("helper.main: OpenEvent of '%s' failed LastError:(%u).\n",
+ szEventName, GetLastError());
+ }
+
+ /* Tell the test thread we are ready. */
+ if (!SetEvent(hProcessReadyEvent))
+ {
+ Fail("helper.main: SetEvent '%s' failed LastError:(%u)\n",
+ hProcessReadyEvent, GetLastError());
+ }
+
+ /* Wait for signal from test thread before exit. */
+ dwRet = WaitForSingleObject(hProcessFinishEvent, TIMEOUT);
+ if (WAIT_OBJECT_0 != dwRet)
+ {
+ Fail("helper.main: WaitForSingleObject '%s' failed pid=%d\n"
+ "LastError:(%u)\n",
+ szcHelperProcessFinishEvName, dwProcessId, GetLastError());
+ }
+
+ PEDANTIC(CloseHandle, (hProcessStartEvent));
+ PEDANTIC(CloseHandle, (hProcessReadyEvent));
+ PEDANTIC(CloseHandle, (hProcessFinishEvent));
+
+ PAL_Terminate();
+
+ return success ? PASS : FAIL;
+}
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.c b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.c
new file mode 100644
index 0000000000..d19978c103
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.c
@@ -0,0 +1,506 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================
+**
+** Source: test5.c
+**
+** Purpose: Test the functionality of simultaneously waiting
+** on multiple processes. Create the same number of helper
+** processes and helper threads.
+** Helper threads wait on helper processes to finish.
+** Helper processes wait on the event signal from test
+** thread before exit.
+** The test thread can wake up one helper
+** thread at a time by signaling the corresponding helper
+** process to finish.
+** The test thread can also wake up all helper threads at once
+** by signaling help process 0 to exit.
+**
+**
+**============================================================*/
+
+#define UNICODE
+
+#include "commonconsts.h"
+
+#include <palsuite.h>
+
+/* The maximum number of objects a thread can wait is MAXIMUM_WAIT_OBJECTS.
+ The last helper thread in this test case will wait on all helper processes
+ plus a thread finish event so the maximum number of helper processes
+ can be created in this test case is (MAXIMUM_WAIT_OBJECTS-1). */
+#define MAX_HELPER_PROCESS (MAXIMUM_WAIT_OBJECTS-1)
+
+int MaxNumHelperProcess = MAX_HELPER_PROCESS;
+
+/* indicate how the test thread wake up helper thread. */
+typedef enum _TestCaseType {
+ WakeUpOneThread, /* wake up one helper thread at a time. */
+ WakeUpAllThread /* wake up all helper threads at once */
+} TestCaseType;
+
+TestCaseType TestCase = WakeUpOneThread;
+
+/* When the test thread wakes up one thread at a time,
+ ThreadIndexOfThreadFinishEvent specifies the index of the thread that
+ should be waked up using hThreadFinishEvent instead of helper process. */
+DWORD ThreadIndexOfThreadFinishEvent = 0;
+
+struct helper_process_t
+{
+ PROCESS_INFORMATION pi;
+ HANDLE hProcessReadyEvent;
+ HANDLE hProcessFinishEvent;
+} helper_process[MAX_HELPER_PROCESS];
+
+HANDLE hProcessStartEvent;
+
+struct helper_thread_t
+{
+ HANDLE hThread;
+ DWORD dwThreadId;
+ HANDLE hThreadReadyEvent;
+ HANDLE hThreadFinishEvent;
+} helper_thread[MAX_HELPER_PROCESS];
+
+/*
+ * Entry Point for helper thread.
+ */
+DWORD PALAPI WaitForProcess(LPVOID lpParameter)
+{
+ DWORD index, i;
+ DWORD dwRet;
+ HANDLE handles[MAX_HELPER_PROCESS+1];
+
+ index = (DWORD) lpParameter;
+
+ /* The helper thread 0 will wait for helper process 0, helper thread 1 will
+ wait for helper process 0 and 1, helper thread 2 will wait for helper
+ process 0, 1, and 2, and so on ..., and the last helper thread will wait
+ on all helper processes.
+ Each helper thread also waits on hThreadFinishEvent so that
+ it can exit without waiting on any process to finish. */
+
+ for (i = 0; i <= index; i++)
+ {
+ handles[i] = helper_process[i].pi.hProcess;
+ }
+
+ handles[index+1] = helper_thread[index].hThreadFinishEvent;
+
+ if(!SetEvent(helper_thread[index].hThreadReadyEvent))
+ {
+ Fail("test5.WaitProcess: SetEvent of hThreadReadyEvent failed for thread %d. "
+ "GetLastError() returned %d.\n", index,
+ GetLastError());
+ }
+
+ dwRet = WaitForMultipleObjectsEx(index+2, &handles[0], FALSE, TIMEOUT, TRUE);
+ if (WakeUpAllThread == TestCase)
+ {
+ /* If the test thread signals helper process 0 to exit, all threads will be waked up,
+ and the return value must be (WAIT_OBJECT_0+0) because the handle of helper process 0
+ is in handle[0]. */
+ if (dwRet != (WAIT_OBJECT_0+0))
+ {
+ Fail("test5.WaitForProcess: invalid return value %d for WakupAllThread from WaitForMultipleObjectsEx for thread %d\n"
+ "LastError:(%u)\n",
+ dwRet, index,
+ GetLastError());
+ }
+ }
+ else if (WakeUpOneThread == TestCase)
+ {
+ /* If the test thread wakes up one helper thread at a time,
+ the return value must be either (WAIT_OBJECT_0+index) if the helper thread
+ wakes up because the corresponding help process exits,
+ or (index+1) if the helper thread wakes up because of hThreadReadyEvent. */
+ if ((index != ThreadIndexOfThreadFinishEvent && dwRet != (WAIT_OBJECT_0+index)) ||
+ (index == ThreadIndexOfThreadFinishEvent && dwRet != (index+1)))
+ {
+ Fail("test5.WaitForProcess: invalid return value %d for WakupOneThread from WaitForMultipleObjectsEx for thread %d\n"
+ "LastError:(%u)\n",
+ dwRet, index,
+ GetLastError());
+ }
+ }
+ else
+ {
+ Fail("Unknown TestCase %d\n", TestCase);
+ }
+ return 0;
+}
+
+/*
+ * Setup the helper processes and helper threads.
+ */
+void
+Setup()
+{
+
+ STARTUPINFO si;
+ DWORD dwRet;
+ int i;
+
+ char szEventName[MAX_PATH];
+ PWCHAR uniStringHelper;
+ PWCHAR uniString;
+
+ /* Create the event to start helper process after it was created. */
+ uniString = convert(szcHelperProcessStartEvName);
+ hProcessStartEvent = CreateEvent(NULL, TRUE, FALSE, uniString);
+ free(uniString);
+ if (!hProcessStartEvent)
+ {
+ Fail("test5.Setup: CreateEvent of '%s' failed. "
+ "GetLastError() returned %d.\n", szcHelperProcessStartEvName,
+ GetLastError());
+ }
+
+ /* Create the helper processes. */
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ uniStringHelper = convert("helper");
+ for (i = 0; i < MaxNumHelperProcess; i++)
+ {
+ ZeroMemory( &helper_process[i].pi, sizeof(PROCESS_INFORMATION));
+
+ if(!CreateProcess( NULL, uniStringHelper, NULL, NULL,
+ FALSE, 0, NULL, NULL, &si, &helper_process[i].pi))
+ {
+ Fail("test5.Setup: CreateProcess failed to load executable for helper process %d. "
+ "GetLastError() returned %u.\n",
+ i, GetLastError());
+ }
+
+ /* Create the event to let helper process tell us it is ready. */
+ if (_snprintf(szEventName, MAX_PATH-1, "%s%d",
+ szcHelperProcessReadyEvName, helper_process[i].pi.dwProcessId) < 0)
+ {
+ Fail ("test5.Setup: Insufficient event name string length for %s\n", szcHelperProcessReadyEvName);
+ }
+
+ uniString = convert(szEventName);
+
+ helper_process[i].hProcessReadyEvent = CreateEvent(NULL, FALSE, FALSE, uniString);
+ free(uniString);
+ if (!helper_process[i].hProcessReadyEvent)
+ {
+ Fail("test5.Setup: CreateEvent of '%s' failed. "
+ "GetLastError() returned %d.\n", szEventName,
+ GetLastError());
+ }
+
+ /* Create the event to tell helper process to exit. */
+ if (_snprintf(szEventName, MAX_PATH-1, "%s%d",
+ szcHelperProcessFinishEvName, helper_process[i].pi.dwProcessId) < 0)
+ {
+ Fail ("test5.Setup: Insufficient event name string length for %s\n", szcHelperProcessFinishEvName);
+ }
+
+ uniString = convert(szEventName);
+
+ helper_process[i].hProcessFinishEvent = CreateEvent(NULL, TRUE, FALSE, uniString);
+ free(uniString);
+ if (!helper_process[i].hProcessFinishEvent)
+ {
+ Fail("test5.Setup: CreateEvent of '%s' failed. "
+ "GetLastError() returned %d.\n", szEventName,
+ GetLastError());
+ }
+
+ }
+ free(uniStringHelper);
+
+ /* Signal all helper processes to start. */
+ if (!SetEvent(hProcessStartEvent))
+ {
+ Fail("test5.Setup: SetEvent '%s' failed\n",
+ "LastError:(%u)\n",
+ szcHelperProcessStartEvName, GetLastError());
+ }
+
+ /* Wait for ready signals from all helper processes. */
+ for (i = 0; i < MaxNumHelperProcess; i++)
+ {
+ dwRet = WaitForSingleObject(helper_process[i].hProcessReadyEvent, TIMEOUT);
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Fail("test5.Setup: WaitForSingleObject %s failed for helper process %d\n"
+ "LastError:(%u)\n",
+ szcHelperProcessReadyEvName, i, GetLastError());
+ }
+ }
+
+ /* Create the same number of helper threads as helper processes. */
+ for (i = 0; i < MaxNumHelperProcess; i++)
+ {
+ /* Create the event to let helper thread tell us it is ready. */
+ helper_thread[i].hThreadReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!helper_thread[i].hThreadReadyEvent)
+ {
+ Fail("test5.Setup: CreateEvent of hThreadReadyEvent failed for thread %d\n"
+ "LastError:(%u)\n", i, GetLastError());
+ }
+
+ /* Create the event to tell helper thread to exit without waiting for helper process. */
+ helper_thread[i].hThreadFinishEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!helper_thread[i].hThreadFinishEvent)
+ {
+ Fail("test5.Setup: CreateEvent of hThreadFinishEvent failed for thread %d\n"
+ "LastError:(%u)\n", i, GetLastError());
+ }
+
+ /* Create the helper thread. */
+ helper_thread[i].hThread = CreateThread( NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)WaitForProcess,
+ (LPVOID)i,
+ 0,
+ &helper_thread[i].dwThreadId);
+ if (NULL == helper_thread[i].hThread)
+ {
+ Fail("test5.Setup: Unable to create the helper thread %d\n"
+ "LastError:(%u)\n", i, GetLastError());
+ }
+ }
+
+ /* Wait for ready signals from all helper threads. */
+ for (i = 0; i < MaxNumHelperProcess; i++)
+ {
+ dwRet = WaitForSingleObject(helper_thread[i].hThreadReadyEvent, TIMEOUT);
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Fail("test5.Setup: WaitForSingleObject hThreadReadyEvent for thread %d\n"
+ "LastError:(%u)\n", i, GetLastError());
+ }
+ }
+}
+
+/*
+ * Cleanup the helper processes and helper threads.
+ */
+DWORD
+Cleanup()
+{
+ DWORD dwExitCode;
+ DWORD dwRet;
+ int i;
+
+ /* Wait for all helper process to finish and close their handles
+ and associated events. */
+ for (i = 0; i < MaxNumHelperProcess; i++)
+ {
+
+ /* wait for the child process to complete */
+ dwRet = WaitForSingleObject ( helper_process[i].pi.hProcess, TIMEOUT );
+ if (WAIT_OBJECT_0 != dwRet)
+ {
+ Fail("test5.Cleanup: WaitForSingleObject hThreadReadyEvent failed for thread %d\n"
+ "LastError:(%u)\n", i, GetLastError());
+ }
+
+ /* check the exit code from the process */
+ if (!GetExitCodeProcess(helper_process[i].pi.hProcess, &dwExitCode))
+ {
+ Trace( "test5.Cleanup: GetExitCodeProcess %d call failed LastError:(%u)\n",
+ i, GetLastError());
+ dwExitCode = FAIL;
+ }
+ PEDANTIC(CloseHandle, (helper_process[i].pi.hThread));
+ PEDANTIC(CloseHandle, (helper_process[i].pi.hProcess));
+ PEDANTIC(CloseHandle, (helper_process[i].hProcessReadyEvent));
+ PEDANTIC(CloseHandle, (helper_process[i].hProcessFinishEvent));
+ }
+
+ /* Close all helper threads' handles */
+ for (i = 0; i < MaxNumHelperProcess; i++)
+ {
+ PEDANTIC(CloseHandle, (helper_thread[i].hThread));
+ PEDANTIC(CloseHandle, (helper_thread[i].hThreadReadyEvent));
+ PEDANTIC(CloseHandle, (helper_thread[i].hThreadFinishEvent));
+ }
+
+ /* Close all process start event. */
+ PEDANTIC(CloseHandle, (hProcessStartEvent));
+
+ return dwExitCode;
+}
+
+/*
+ * In this test case, the test thread will signal one helper
+ * process to exit at a time starting from the last helper
+ * process and then wait for the corresponding helper thread to exit.
+ * The ThreadIndexOfThreadFinishEvent specifies the index of the thread that
+ * should be waked up using hThreadFinishEvent instead of helper process.
+ */
+void
+TestWakeupOneThread()
+{
+ DWORD dwRet;
+ int i;
+
+ TestCase = WakeUpOneThread;
+
+ if (((LONG)ThreadIndexOfThreadFinishEvent) < 0 ||
+ ThreadIndexOfThreadFinishEvent >= MAX_HELPER_PROCESS)
+ Fail("test5.TestWaitOnOneThread: Invalid ThreadIndexOfThreadFinishEvent %d\n", ThreadIndexOfThreadFinishEvent);
+
+ /* Since helper thread 0 waits on helper process 0,
+ thread 1 waits on on process 0, and 1,
+ thread 2 waits on process 0, 1, and 2, and so on ...,
+ and the last helper thread will wait on all helper processes,
+ the helper thread can be waked up one at a time by
+ waking up the help process one at a time starting from the
+ last helper process. */
+ for (i = MaxNumHelperProcess-1; i >= 0; i--)
+ {
+ /* make sure the helper thread has not exited yet. */
+ dwRet = WaitForSingleObject(helper_thread[i].hThread, 0);
+ if (WAIT_TIMEOUT != dwRet)
+ {
+ Fail("test5.TestWaitOnOneThread: helper thread %d already exited %d\n", i);
+ }
+
+ /* Decide how to wakeup the helper thread:
+ using event or using helper process. */
+ if (i == ThreadIndexOfThreadFinishEvent)
+ {
+ if (!SetEvent(helper_thread[i].hThreadFinishEvent))
+ {
+ Fail("test5.TestWaitOnOneThread: SetEvent hThreadFinishEvent failed for thread %d\n",
+ "LastError:(%u)\n", i, GetLastError());
+ }
+ }
+ else
+ {
+ if (!SetEvent(helper_process[i].hProcessFinishEvent))
+ {
+ Fail("test5.TestWaitOnOneThread: SetEvent %s%d failed for helper process %d\n",
+ "LastError:(%u)\n",
+ szcHelperProcessFinishEvName, helper_process[i].pi.dwProcessId, i,
+ GetLastError());
+ }
+ }
+
+ dwRet = WaitForSingleObject(helper_thread[i].hThread, TIMEOUT);
+ if (WAIT_OBJECT_0 != dwRet)
+ {
+ Fail("test5.TestWaitOnOneThread: WaitForSingleObject helper thread %d"
+ "LastError:(%u)\n",
+ i, GetLastError());
+ }
+ }
+
+ /* Finally, need to wake up the helper process which the test thread
+ skips waking up in the last loop. */
+ if (!SetEvent(helper_process[ThreadIndexOfThreadFinishEvent].hProcessFinishEvent))
+ {
+ Fail("test5.TestWaitOnOneThread: SetEvent %s%d failed\n",
+ "LastError:(%u)\n",
+ szcHelperProcessFinishEvName, helper_process[ThreadIndexOfThreadFinishEvent].pi.dwProcessId,
+ GetLastError());
+ }
+}
+
+/*
+ * In this test case, the test thread will signal the helper
+ * process 0 to exit. Since all helper threads wait on process 0,
+ * all helper threads will wake up and exit, and the test thread
+ * will wait for all of them to exit.
+ */
+void
+TestWakeupAllThread()
+{
+ DWORD dwRet;
+ int i;
+
+ TestCase = WakeUpAllThread;
+
+ /* make sure none of the helper thread exits. */
+ for (i = 0; i < MaxNumHelperProcess; i++)
+ {
+ dwRet = WaitForSingleObject(helper_thread[i].hThread, 0);
+ if (WAIT_TIMEOUT != dwRet)
+ {
+ Fail("test5.TestWaitOnAllThread: helper thread %d already exited %d\n", i);
+ }
+ }
+
+ /* Signal helper process 0 to exit. */
+ if (!SetEvent(helper_process[0].hProcessFinishEvent))
+ {
+ Fail("test5.TestWaitOnAllThread: SetEvent %s%d failed\n",
+ "LastError:(%u)\n",
+ szcHelperProcessFinishEvName, helper_process[0].pi.dwProcessId,
+ GetLastError());
+ }
+
+ /* Wait for all helper threads to exit. */
+ for (i = 0; i < MaxNumHelperProcess; i++)
+ {
+
+ dwRet = WaitForSingleObject(helper_thread[i].hThread, TIMEOUT);
+ if (WAIT_OBJECT_0 != dwRet)
+ {
+ Fail("test5.TestWaitOnAllThread: WaitForSingleObject failed for helper thread %d\n"
+ "LastError:(%u)\n",
+ i, GetLastError());
+ }
+ }
+
+ /* Signal the rest of helper processes to exit. */
+ for (i = 1; i < MaxNumHelperProcess; i++)
+ {
+ if (!SetEvent(helper_process[i].hProcessFinishEvent))
+ {
+ Fail("test5.TestWaitOnAllThread: SetEvent %s%d failed\n",
+ "LastError:(%u)\n",
+ szcHelperProcessFinishEvName, helper_process[i].pi.dwProcessId,
+ GetLastError());
+ }
+ }
+}
+
+int __cdecl main(int argc, char *argv[])
+{
+ DWORD dwExitCode;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ switch (argc)
+ {
+ case 1:
+ MaxNumHelperProcess = MAX_HELPER_PROCESS;
+ break;
+ case 2:
+ MaxNumHelperProcess = atol(argv[1]);
+ break;
+ default:
+ Fail("Invalid number of arguments\n");
+ }
+
+ if (MaxNumHelperProcess < 1 ||
+ MaxNumHelperProcess > MAX_HELPER_PROCESS)
+ Fail("test5.main: Invalid MaxNumHelperProcess %d\n", MaxNumHelperProcess);
+
+ Setup();
+ ThreadIndexOfThreadFinishEvent = 3;
+ TestWakeupOneThread();
+ dwExitCode = Cleanup();
+
+ if (PASS == dwExitCode)
+ {
+ Setup();
+ TestWakeupAllThread();
+ dwExitCode = Cleanup();
+ }
+
+ PAL_TerminateEx(dwExitCode);
+ return dwExitCode;
+}
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/testinfo.dat b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/testinfo.dat
new file mode 100644
index 0000000000..5efc75f77e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/testinfo.dat
@@ -0,0 +1,18 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = WaitForMultipleObjectsEx
+Name = Check simultaneously waiting on multiple processes.
+TYPE = DEFAULT
+EXE1 = test5
+EXE2 = helper
+Description
+= Create a number of helper processes and helper threads.
+= Helper threads wait on helper processes to finish.
+= Helper processes wait on the event signal from test
+= thread before exit. The test thread then selectively
+= signals helper process to finish and then wait on the
+= selected helper thread to finish.
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/CMakeLists.txt
new file mode 100644
index 0000000000..ce75cadbff
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ test6.c
+)
+
+add_executable(paltest_waitformultipleobjectsex_test6
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_waitformultipleobjectsex_test6 coreclrpal)
+
+target_link_libraries(paltest_waitformultipleobjectsex_test6
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ child6.c
+)
+
+add_executable(paltest_waitformultipleobjectsex_test6_child
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_waitformultipleobjectsex_test6_child coreclrpal)
+
+target_link_libraries(paltest_waitformultipleobjectsex_test6_child
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/child6.c b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/child6.c
new file mode 100644
index 0000000000..1a95815ddf
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/child6.c
@@ -0,0 +1,211 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: child6.c
+**
+** Purpose: Test for WaitForMultipleObjectsEx in multiple
+** scenarios - child process
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+int __cdecl main(int argc, char **argv)
+{
+ int i, iRet;
+ BOOL bRet;
+ BOOL bNamedEvent = 0;
+ BOOL bMutex = 0;
+ BOOL bMutexAndNamedEvent = 0;
+ BOOL bSemaphore = 0;
+ DWORD dwRet;
+ HANDLE hNamedEvent;
+ HANDLE hMutex;
+ char szTestName[256];
+ WCHAR wszTestName[256] = { 0 };
+ char szEventName[128] = { 0 };
+ char szMutexName[128] = { 0 };
+ char szSemName[128] = { 0 };
+ WCHAR wszEventName[128];
+ WCHAR wszMutexName[128];
+ WCHAR wszSemName[128];
+ DWORD iExitCode = 0;
+ HANDLE hSemaphore;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ Trace("[child] Starting\n");
+
+ for (i=1; i<argc; i++)
+ {
+ if (0 == strcmp(argv[i],"-event"))
+ {
+ bNamedEvent = 1;
+ }
+ else if (0 == strcmp(argv[i],"-mutex"))
+ {
+ bMutex = 1;
+ }
+ else if (0 == strcmp(argv[i],"-mutex_and_named_event"))
+ {
+ bMutexAndNamedEvent = 1;
+ }
+ else if (0 == strcmp(argv[i],"-semaphore"))
+ {
+ bSemaphore = 1;
+ }
+ else if (0 == strcmp(argv[i],"-exitcode") && i < argc-1 )
+ {
+ i++;
+ iExitCode = atoi(argv[i]);
+ Trace("[child] My exit code is %d\n", iExitCode);
+ }
+
+ else if ('-' != *argv[i])
+ {
+ strncpy(szTestName, argv[i], 256);
+ szTestName[255] = 0;
+ iRet = MultiByteToWideChar(CP_ACP, 0, szTestName, strlen(szTestName)+1, wszTestName, 256);
+ if (0 == iRet)
+ {
+ Fail("Failed to convert test string\n");
+ }
+ }
+ }
+
+ _snprintf(szEventName, 128, "%s_Event", szTestName);
+ szEventName[127] = 0;
+ _snprintf(szMutexName, 128, "%s_Mutex", szTestName);
+ szMutexName[127] = 0;
+ _snprintf(szSemName, 128, "%s_Semaphore", szTestName);
+ szSemName[127] = 0;
+
+ iRet = MultiByteToWideChar(CP_ACP, 0, szEventName, strlen(szEventName)+1, wszEventName, 128);
+ iRet &= MultiByteToWideChar(CP_ACP, 0, szMutexName, strlen(szMutexName)+1, wszMutexName, 128);
+ iRet &= MultiByteToWideChar(CP_ACP, 0, szSemName, strlen(szSemName)+1, wszSemName, 128);
+ if (0 == iRet)
+ {
+ Fail("[child] Failed to convert strings\n");
+ }
+
+ Trace("[child] TestName=%s Event: %S, Mutex: %S, Semaphore = %S\n",
+ szTestName, wszEventName, wszMutexName, wszSemName);
+
+ hNamedEvent = OpenEventW(0, FALSE, wszEventName);
+ if (NULL == hNamedEvent)
+ {
+ Fail("[child] OpenEventW failed [szEventName=%s GetLastError()=%u]\n",
+ szEventName, GetLastError());
+ }
+ hMutex = OpenMutexW(0, FALSE, wszMutexName);
+ if (NULL == hMutex)
+ {
+ Fail("[child] OpenMutexW failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+ hSemaphore = CreateSemaphoreW(NULL, 0, 256, wszSemName);
+ if (NULL == hSemaphore)
+ {
+ Fail("[child] CreateSemaphore failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+
+ if (bMutex)
+ {
+ Trace("[child] Going to wait on mutex %s\n", szMutexName);
+ dwRet = WaitForSingleObject(hMutex, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("[child] WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ Trace("[child] Setting event %s\n", szEventName);
+ bRet = SetEvent(hNamedEvent);
+ if (FALSE == bRet)
+ {
+ Fail("[child] SetEvent failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ // mutex will be abandoned
+ }
+ else if (bMutexAndNamedEvent)
+ {
+ dwRet = WaitForSingleObject(hMutex, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("[child] WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ Sleep(2000);
+
+ bRet = ReleaseMutex(hMutex);
+ if (FALSE == bRet)
+ {
+ Fail("[child] ReleaseMutex failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ Sleep(1000);
+
+ bRet = SetEvent(hNamedEvent);
+ if (FALSE == bRet)
+ {
+ Fail("[child] SetEvent failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+ }
+ else if (bSemaphore)
+ {
+ LONG lPrevCount = 42;
+
+
+ Trace("[child] Going to wait on event %s\n", szEventName);
+ dwRet = WaitForSingleObject(hNamedEvent, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("[child] WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ Trace("[child] Releasing semaphore %s\n", szSemName);
+ bRet = ReleaseSemaphore(hSemaphore, 10, &lPrevCount);
+ if (FALSE == bRet)
+ {
+ Fail("ReleaseMutex failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+ if (0 != lPrevCount)
+ {
+ Fail("Previous count from semaphore=%d, expected 0\n", lPrevCount);
+ }
+ }
+ else if (bNamedEvent)
+ {
+ Sleep(1000);
+
+ bRet = SetEvent(hNamedEvent);
+ if (FALSE == bRet)
+ {
+ Fail("[child] SetEvent failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+ }
+
+ Sleep(1000);
+
+ Trace("[child] Done\n");
+
+ PAL_TerminateEx(iExitCode);
+ return iExitCode;
+}
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/test6.c b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/test6.c
new file mode 100644
index 0000000000..399d2c3bff
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/test6.c
@@ -0,0 +1,709 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test6.c
+**
+** Purpose: Test for WaitForMultipleObjectsEx in multiple
+** scenarios
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+#define MAX_COUNT 10000
+#define MAX_THREADS 256
+
+BOOL g_bMutex = 0;
+BOOL g_bEvent = 0;
+BOOL g_bNamedEvent = 0;
+BOOL g_bSemaphore = 0;
+BOOL g_bProcess = 0;
+BOOL g_bLocalWaitAll = 0;
+BOOL g_bRemoteWaitAll = 0;
+BOOL g_bRandom = 0;
+
+int iCount = 1;
+int iThreads = 1;
+HANDLE hThreads[MAX_THREADS];
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+
+DWORD PALAPI EventTestThread(PVOID pArg)
+{
+ BOOL bRet;
+ DWORD dwRet;
+ HANDLE hEvent[2];
+ HANDLE (*prgHandles)[] = (HANDLE (*)[])pArg;
+
+ Trace("[EventTestThread] Starting\n");
+
+ bRet = DuplicateHandle(GetCurrentProcess(), (*prgHandles)[0], GetCurrentProcess(),
+ &hEvent[0], 0, FALSE, DUPLICATE_SAME_ACCESS);
+ bRet &= DuplicateHandle(GetCurrentProcess(), (*prgHandles)[1], GetCurrentProcess(),
+ &hEvent[1], 0, FALSE, DUPLICATE_SAME_ACCESS);
+ if (FALSE == bRet)
+ {
+ Fail("[EventTestThread] Failed to duplicate handles\n");
+ }
+
+ Sleep(1000);
+ bRet = SetEvent(hEvent[1]);
+ if (FALSE == bRet)
+ {
+ Fail("SetEvent failed\n");
+ Fail("[EventTestThread] SetEvent failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ dwRet = WaitForSingleObject(hEvent[1], INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("[EventTestThread] WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ Sleep(1000);
+ bRet = SetEvent(hEvent[0]);
+ if (FALSE == bRet)
+ {
+ Fail("[EventTestThread] SetEvent failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ Sleep(1000);
+ bRet = SetEvent(hEvent[1]);
+ if (FALSE == bRet)
+ {
+ Fail("[EventTestThread] SetEvent failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ CloseHandle(hEvent[0]);
+ CloseHandle(hEvent[1]);
+
+ Trace("[EventTestThread] Done\n");
+ return 0;
+}
+
+DWORD PALAPI MutexTestThread(PVOID pArg)
+{
+ BOOL bRet;
+ DWORD dwRet;
+ HANDLE hMutex;
+
+ Trace("[MutexTestThread] Starting\n");
+
+ bRet = DuplicateHandle(GetCurrentProcess(), (HANDLE)pArg, GetCurrentProcess(), &hMutex,
+ 0, FALSE, DUPLICATE_SAME_ACCESS);
+ if (FALSE == bRet)
+ {
+ Fail("[EventTestThread] DuplicateHandle failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ dwRet = WaitForSingleObject(hMutex, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("[EventTestThread] WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ Sleep(1000);
+ CloseHandle(hMutex);
+
+ Trace("[MutexTestThread] Done\n");
+
+ return 0;
+}
+
+DWORD PALAPI TestThread(PVOID pArg)
+{
+ BOOL bRet;
+ DWORD dwRet;
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si;
+ HANDLE hNamedEvent;
+ HANDLE hEvent[2] = { 0, 0 };
+ HANDLE hMutex = 0;
+ HANDLE hSemaphore = 0;
+ HANDLE hObjs[2];
+ DWORD dwThreadNum;
+ DWORD dwSlaveThreadTid = 0;
+ HANDLE hThread;
+ int i, iCnt, iRet;
+ char szTestName[128];
+ char szCmd[128];
+ char szEventName[128] = { 0 };
+ char szMutexName[128] = { 0 };
+ char szSemName[128] = { 0 };
+ WCHAR wszEventName[128] = { 0 };
+ WCHAR wszMutexName[128] = { 0 };
+ WCHAR wszSemName[128] = { 0 };
+ BOOL bMutex = g_bMutex;
+ BOOL bEvent = g_bEvent;
+ BOOL bNamedEvent = g_bNamedEvent;
+ BOOL bSemaphore = g_bSemaphore;
+ BOOL bProcess = g_bProcess;
+ BOOL bLocalWaitAll = g_bLocalWaitAll;
+ BOOL bRemoteWaitAll = g_bRemoteWaitAll;
+ int iDesiredExitCode;
+
+ dwThreadNum = (DWORD)pArg;
+
+ _snprintf (szTestName, 128, "Test6_%u", dwThreadNum);
+ szTestName[127] = 0;
+
+ _snprintf(szEventName, 128, "%s_Event", szTestName);
+ szEventName[127] = 0;
+ _snprintf(szMutexName, 128, "%s_Mutex", szTestName);
+ szMutexName[127] = 0;
+ _snprintf(szSemName, 128, "%s_Semaphore", szTestName);
+ szSemName[127] = 0;
+
+ iRet = MultiByteToWideChar(CP_ACP, 0, szEventName, strlen(szEventName)+1, wszEventName, 128);
+ iRet &= MultiByteToWideChar(CP_ACP, 0, szMutexName, strlen(szMutexName)+1, wszMutexName, 128);
+ iRet &= MultiByteToWideChar(CP_ACP, 0, szSemName, strlen(szSemName)+1, wszSemName, 128);
+
+ if (0 == iRet)
+ {
+ Fail("[TestThread] Failed to convert strings\n");
+ }
+
+ Trace("[TestThread] TestName=%s Event: %S, Mutex: %S, Semaphore = %S\n",
+ szTestName, wszEventName, wszMutexName, wszSemName);
+
+ hEvent[0] = CreateEventA(NULL, FALSE, FALSE, NULL);
+ hEvent[1] = CreateEventA(NULL, FALSE, FALSE, NULL);
+
+ hNamedEvent = CreateEventW(NULL, FALSE, FALSE, wszEventName);
+ hMutex = CreateMutexW(NULL, FALSE, wszMutexName);
+ hSemaphore = CreateSemaphoreW(NULL, 0, 256, wszSemName);
+
+ if (NULL == hEvent[0] || NULL == hEvent[1] || NULL == hMutex ||
+ NULL == hNamedEvent || NULL == hSemaphore)
+ {
+ Fail("[TestThread] Failed to create objects "
+ "[hNamedEvent=%p hMutex=%p hSemaphore=%p]\n",
+ (VOID*)hNamedEvent, (VOID*)hMutex, (VOID*)hSemaphore);
+ }
+
+ for (iCnt=0; iCnt<iCount; iCnt++)
+ {
+ if (g_bRandom)
+ {
+ int iRnd;
+
+ bMutex = 0;
+ bEvent = 0;
+ bNamedEvent = 0;
+ bSemaphore = 0;
+ bProcess = 0;
+ bLocalWaitAll = 0;
+ bRemoteWaitAll = 0;
+
+ iRnd = rand() % 7;
+ switch(iRnd)
+ {
+ case 0:
+ bMutex = 1;
+ break;
+ case 1:
+ bEvent = 1;
+ break;
+ case 2:
+ bNamedEvent = 1;
+ break;
+ case 3:
+ bSemaphore = 1;
+ break;
+ case 4:
+ bProcess = 1;
+ break;
+ case 5:
+ bLocalWaitAll = 1;
+ break;
+ case 6:
+ bRemoteWaitAll = 1;
+ break;
+ }
+ }
+
+ if (bEvent)
+ {
+ Trace("======================================================================\n");
+ Trace("Local unnamed event test\n");
+ Trace("----------------------------------------\n");
+ hThread = CreateThread(NULL, 0, EventTestThread, (PVOID)hEvent, 0, &dwSlaveThreadTid);
+ if (NULL == hThread)
+ {
+ Fail("Failed to create thread\n");
+ }
+
+ hObjs[0] = hEvent[0];
+ dwRet = WaitForMultipleObjects(1, hObjs, FALSE, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed\n");
+ }
+
+ hObjs[0] = hThread;
+ dwRet = WaitForMultipleObjects(1, hObjs, FALSE, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed\n");
+ }
+
+ CloseHandle(hThread);
+ Trace("Local unnamed event test done \n");
+ Trace("======================================================================\n");
+ }
+
+ if (bMutex)
+ {
+ Trace("======================================================================\n");
+ Trace("Mutex with remote thread awakening test\n");
+ Trace("----------------------------------------\n");
+
+ hThread = CreateThread(NULL, 0, MutexTestThread, (PVOID)hMutex, 0, &dwSlaveThreadTid);
+ if (NULL == hThread)
+ {
+ Fail("Failed to create thread\n");
+ }
+
+ Sleep(1000);
+
+ hObjs[0] = hMutex;
+
+ for (i=0;i<10;i++)
+ {
+ dwRet = WaitForMultipleObjects(1, hObjs, FALSE, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed [dwRet=%x GetLastError()=%d\n",
+ dwRet, GetLastError());
+ }
+ }
+
+ hObjs[0] = hThread;
+ dwRet = WaitForMultipleObjects(1, hObjs, FALSE, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ for (i=0;i<10;i++)
+ {
+ bRet = ReleaseMutex(hMutex);
+ if (FALSE == bRet)
+ {
+ Fail("ReleaseMutex failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+ }
+
+ CloseHandle(hThread);
+ Trace("Mutex with remote thread awakening test done\n");
+ Trace("======================================================================\n");
+ }
+
+ if (bNamedEvent)
+ {
+ Trace("======================================================================\n");
+ Trace("Named event with remote thread awakening test\n");
+ Trace("----------------------------------------\n");
+
+ ZeroMemory ( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ ZeroMemory ( &pi, sizeof(pi) );
+
+ _snprintf (szCmd, 128, "child6 -event %s", szTestName);
+ szCmd[127] = 0;
+
+ bRet = CreateProcessA(NULL, szCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+ if (FALSE == bRet)
+ {
+ Fail("CreateProcess failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ hObjs[0] = pi.hProcess;
+ hObjs[1] = hNamedEvent;
+
+ dwRet = WaitForMultipleObjects(2, hObjs, FALSE, INFINITE);
+ if (1 != dwRet)
+ {
+ Fail("WaitForMultipleObjects failed [dwRet=%u GetLastError()=%u]\n",
+ dwRet, GetLastError());
+ }
+
+ dwRet = WaitForSingleObject(pi.hProcess, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+ Trace("Named event with remote thread awakening test done\n");
+ Trace("======================================================================\n");
+ }
+
+ if (bSemaphore)
+ {
+ Trace("======================================================================\n");
+ Trace("Semaphore with remote thread awakening test\n");
+ Trace("----------------------------------------\n");
+
+ ZeroMemory ( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ ZeroMemory ( &pi, sizeof(pi) );
+
+ _snprintf (szCmd, 128, "child6 -semaphore %s", szTestName);
+ szCmd[127] = 0;
+
+ bRet = CreateProcessA(NULL, szCmd, NULL, NULL, FALSE,
+ 0, NULL, NULL, &si, &pi);
+ if (FALSE == bRet)
+ {
+ Fail("CreateProcessA failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ Trace("Setting event %s\n", szEventName);
+ bRet = SetEvent(hNamedEvent);
+ if (FALSE == bRet)
+ {
+ Fail("[child] SetEvent failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ Trace("Going to wait on semaphore %s\n", szSemName);
+
+
+ hObjs[0] = pi.hProcess;
+ hObjs[0] = hEvent[0];
+ hObjs[1] = hSemaphore;
+ for (i=0;i<10;i++)
+ {
+ dwRet = WaitForMultipleObjects(2, hObjs, FALSE, INFINITE);
+ if (1 != dwRet)
+ {
+ Trace("WaitForMultipleObjects failed [tid=%u dwRet=%u GetLastError()=%u]\n",
+ GetCurrentThreadId(), dwRet, GetLastError());
+ DebugBreak();
+ }
+ }
+
+ dwRet = WaitForSingleObject(pi.hProcess, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+ Trace("Semaphore with remote thread awakening test done\n");
+ Trace("======================================================================\n");
+ }
+
+ if (bProcess)
+ {
+ DWORD dwExitCode;
+
+ Trace("======================================================================\n");
+ Trace("Process wait test\n");
+ Trace("----------------------------------------\n");
+
+ iDesiredExitCode = rand() % 0xFF;
+
+ ZeroMemory ( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ ZeroMemory ( &pi, sizeof(pi) );
+
+ _snprintf (szCmd, 128, "child6 -mutex %s -exitcode %d", szTestName, iDesiredExitCode);
+ szCmd[127] = 0;
+
+ bRet = CreateProcessA(NULL, szCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+ if (FALSE == bRet)
+ {
+ Fail("CreateProcess failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ Trace("Going to wait on event %s\n", szEventName);
+ dwRet = WaitForSingleObject(hNamedEvent, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ hObjs[0] = hEvent[0]; // dummy, this is a local event
+ hObjs[1] = hMutex;
+
+ dwRet = WaitForMultipleObjects(2, hObjs, FALSE, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+ if (1 == dwRet || (1 + WAIT_ABANDONED_0) == dwRet)
+ {
+ bRet = ReleaseMutex(hMutex);
+ if (FALSE == bRet)
+ {
+ Fail("ReleaseMutex failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+ }
+
+ dwRet = WaitForSingleObject(pi.hProcess, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ if (!GetExitCodeProcess(pi.hProcess, &dwExitCode))
+ {
+ Trace("GetExitCodeProcess call failed LastError:(%u)\n",
+ GetLastError());
+ dwExitCode = FAIL;
+ }
+
+ if (iDesiredExitCode != dwExitCode)
+ {
+ Fail("Wrong return code: %u [%d]\n", dwExitCode, iDesiredExitCode);
+ }
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ Trace("Process wait test done\n");
+ Trace("======================================================================\n");
+ }
+
+ if (bLocalWaitAll)
+ {
+ Trace("======================================================================\n");
+ Trace("WaitAll with local thread awakening test\n");
+ Trace("----------------------------------------\n");
+
+ hThread = CreateThread(NULL, 0, EventTestThread, (PVOID)hEvent, 0, &dwSlaveThreadTid);
+ if (NULL == hThread)
+ {
+ Fail("CreateThread failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ dwRet = WaitForMultipleObjects(2, hEvent, TRUE, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ hObjs[0] = hThread;
+ dwRet = WaitForMultipleObjects(1, hObjs, FALSE, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ CloseHandle(hThread);
+ Trace("WaitAll with local thread awakening test done\n");
+ Trace("======================================================================\n");
+ }
+
+ if (bRemoteWaitAll)
+ {
+ Trace("======================================================================\n");
+ Trace("WaitAll with remote thread awakening test\n");
+ Trace("----------------------------------------\n");
+
+ ZeroMemory ( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ ZeroMemory ( &pi, sizeof(pi) );
+
+ _snprintf (szCmd, 128, "child6 -mutex_and_named_event %s", szTestName);
+ szCmd[127] = 0;
+
+ bRet = CreateProcessA(NULL, szCmd, NULL, NULL, FALSE,
+ 0, NULL, NULL, &si, &pi);
+ if (FALSE == bRet)
+ {
+ Fail("CreateProcess failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ Sleep(1000);
+
+ hObjs[0] = hMutex;
+ hObjs[1] = hNamedEvent;
+
+ dwRet = WaitForMultipleObjects(2, hObjs, TRUE, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ bRet = ReleaseMutex(hMutex);
+ if (FALSE == bRet)
+ {
+ Fail("ReleaseMutex failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ dwRet = WaitForSingleObject(pi.hProcess, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
+ GetLastError());
+ }
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ Trace("WaitAll with remote thread awakening test done\n");
+ Trace("======================================================================\n");
+ }
+ }
+
+ return 0;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ DWORD dwRet;
+ DWORD dwSlaveThreadTid = 0;
+ int i, iCnt;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ srand(time(NULL) * GetCurrentProcessId());
+
+ if (argc == 1)
+ {
+ g_bMutex = 1;
+ g_bEvent = 1;
+ g_bNamedEvent = 1;
+ g_bSemaphore = 1;
+ g_bProcess = 1;
+ g_bLocalWaitAll = 1;
+ g_bRemoteWaitAll = 1;
+ }
+ else
+ {
+ for (i=1;i<argc;i++)
+ {
+ if (0 == strcmp(argv[i], "-mutex"))
+ {
+ g_bMutex = 1;
+ }
+ else if (0 == strcmp(argv[i], "-event"))
+ {
+ g_bEvent = 1;
+ }
+ else if (0 == strcmp(argv[i], "-namedevent"))
+ {
+ g_bNamedEvent = 1;
+ }
+ else if (0 == strcmp(argv[i], "-semaphore"))
+ {
+ g_bSemaphore = 1;
+ }
+ else if (0 == strcmp(argv[i], "-process"))
+ {
+ g_bProcess = 1;
+ }
+ else if (0 == strcmp(argv[i], "-localwaitall"))
+ {
+ g_bLocalWaitAll = 1;
+ }
+ else if (0 == strcmp(argv[i], "-remotewaitall"))
+ {
+ g_bRemoteWaitAll = 1;
+ }
+ else if (0 == strcmp(argv[i], "-all"))
+ {
+ g_bMutex = 1;
+ g_bEvent = 1;
+ g_bNamedEvent = 1;
+ g_bSemaphore = 1;
+ g_bProcess = 1;
+ g_bLocalWaitAll = 1;
+ g_bRemoteWaitAll = 1;
+ }
+ else if (0 == strcmp(argv[i], "-random"))
+ {
+ g_bRandom = 1;
+ }
+ else if ((0 == strcmp(argv[i], "-count")) && (argc > i+1))
+ {
+ i++;
+ iCnt = atoi(argv[i]);
+ if (iCnt > 0 && iCnt < MAX_COUNT)
+ {
+ iCount = iCnt;
+ }
+ }
+ else if ((0 == strcmp(argv[i], "-threads")) && (argc > i+1))
+ {
+ i++;
+ iCnt = atoi(argv[i]);
+ if (iCnt > 0 && iCnt <= MAX_THREADS)
+ {
+ iThreads = iCnt;
+ }
+ }
+ else
+ {
+ Trace("Unknown option %s ignored\n", argv[i]);
+ }
+ }
+ }
+
+
+ iCnt = 0;
+ for (i=0;i<iThreads;i++)
+ {
+ hThreads[iCnt] = CreateThread(NULL, 0, TestThread, (VOID*)iCnt, 0, &dwSlaveThreadTid);
+ if (NULL == hThreads[iCnt])
+ {
+ Trace("Failed to create thread\n");
+ }
+ else
+ {
+ iCnt++;
+ }
+ }
+
+ if (0 == iCnt)
+ {
+ Fail("Can't create any thread\n");
+ }
+
+ for (i=0; i<iCnt; i+=64)
+ {
+ dwRet = WaitForMultipleObjects(MIN(64, iCnt-i), &hThreads[i], TRUE, INFINITE);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed [dwRet=%u GetLastError()=%u iCnt=%d i=%d]\n",
+ dwRet, GetLastError(), iCnt, i);
+ }
+ }
+
+
+ for (i=0; i<iCnt; i++)
+ {
+ CloseHandle(hThreads[i]);
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/thistest.dat b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/thistest.dat
new file mode 100644
index 0000000000..8d36162752
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/thistest.dat
@@ -0,0 +1,2 @@
+
+PAL,threading,palsuite\threading\waitformultipleobjectsex\test6,Test6forWaitForMultipleObjectsEx=test6.c child6.c,<SUPPORTEXE>
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForSingleObject/CMakeLists.txt
new file mode 100644
index 0000000000..94bd51caa6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(WFSOExMutexTest)
+add_subdirectory(WFSOExSemaphoreTest)
+add_subdirectory(WFSOExThreadTest)
+add_subdirectory(WFSOMutexTest)
+add_subdirectory(WFSOProcessTest)
+add_subdirectory(WFSOSemaphoreTest)
+add_subdirectory(WFSOThreadTest)
+
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/CMakeLists.txt
new file mode 100644
index 0000000000..65fa0abe14
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ WFSOExMutexTest.c
+)
+
+add_executable(paltest_waitforsingleobject_wfsoexmutextest
+ ${SOURCES}
+)
+
+add_dependencies(paltest_waitforsingleobject_wfsoexmutextest coreclrpal)
+
+target_link_libraries(paltest_waitforsingleobject_wfsoexmutextest
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/WFSOExMutexTest.c b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/WFSOExMutexTest.c
new file mode 100644
index 0000000000..3737f9cc10
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/WFSOExMutexTest.c
@@ -0,0 +1,214 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: WFSOExMutex.c
+**
+** Purpose: Tests a child thread in the middle of a
+** WaitForSingleObjectEx call will be interrupted by QueueUserAPC
+** if the alert flag was set.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+/*Based on SleepEx/test2 */
+
+const int ChildThreadWaitTime = 4000;
+const int InterruptTime = 2000;
+const DWORD AcceptableDelta = 300;
+
+void RunTest(BOOL AlertThread);
+VOID PALAPI APCFunc(ULONG_PTR dwParam);
+DWORD PALAPI WaiterProc(LPVOID lpParameter);
+
+DWORD ThreadWaitDelta;
+HANDLE hMutex;
+
+
+
+int __cdecl main( int argc, char **argv )
+{
+ int ret=0;
+
+ if (0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /*
+ On some platforms (e.g. FreeBSD 4.9) the first call to some synch objects
+ (such as conditions) involves some pthread internal initialization that
+ can make the first wait slighty longer, potentially going above the
+ acceptable delta for this test. Let's add a dummy wait to preinitialize
+ internal structures
+ */
+ Sleep(100);
+
+ /*
+ The state of a mutex object is signaled when it is not owned by any thread.
+ The creating thread can use the bInitialOwner flag to request immediate ownership
+ of the mutex. Otherwise, a thread must use one of the wait functions to request
+ ownership. When the mutex's state is signaled, one waiting thread is granted
+ ownership, the mutex's state changes to nonsignaled, and the wait function returns.
+ Only one thread can own a mutex at any given time. The owning thread uses the
+ ReleaseMutex function to release its ownership.
+ */
+
+ /* Create a mutex that is not in the signalled state */
+ hMutex = CreateMutex(NULL, //No security attributes
+ TRUE, //Iniitally owned
+ NULL); //Name of mutex
+
+ if (hMutex == NULL)
+ {
+ Fail("Failed to create mutex! GetLastError returned %d.\n",
+ GetLastError());
+ }
+ /*
+ * Check that Queueing an APC in the middle of a wait does interrupt
+ * it, if it's in an alertable state.
+ */
+
+ RunTest(TRUE);
+ if ((ThreadWaitDelta - InterruptTime) > AcceptableDelta)
+ {
+ Fail("Expected thread to wait for %d ms (and get interrupted).\n"
+ "Thread waited for %d ms! (Acceptable delta: %d)\n",
+ InterruptTime, ThreadWaitDelta, AcceptableDelta);
+ }
+
+
+ /*
+ * Check that Queueing an APC in the middle of a wait does NOT interrupt
+ * it, if it is not in an alertable state.
+ */
+ RunTest(FALSE);
+ if ((ThreadWaitDelta - ChildThreadWaitTime) > AcceptableDelta)
+ {
+ Fail("Expected thread to wait for %d ms (and not be interrupted).\n"
+ "Thread waited for %d ms! (Acceptable delta: %d)\n",
+ ChildThreadWaitTime, ThreadWaitDelta, AcceptableDelta);
+ }
+
+
+
+ //Release Mutex
+ ret = ReleaseMutex(hMutex);
+ if (0==ret)
+ {
+ Fail("Unable to Release Mutex!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ //Close Mutex Handle
+ ret = CloseHandle(hMutex);
+ if (!ret)
+ {
+ Fail("Unable to close handle to Mutex!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
+void RunTest(BOOL AlertThread)
+{
+
+ HANDLE hThread = 0;
+ DWORD dwThreadId = 0;
+
+ int ret=0;
+
+ hThread = CreateThread( NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)WaiterProc,
+ (LPVOID) AlertThread,
+ 0,
+ &dwThreadId);
+
+ if (hThread == NULL)
+ {
+ Fail("ERROR: Was not able to create the thread to test!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+
+
+ Sleep(InterruptTime);
+
+ ret = QueueUserAPC(APCFunc, hThread, 0);
+
+ if (ret == 0)
+ {
+ Fail("QueueUserAPC failed! GetLastError returned %d\n",
+ GetLastError());
+ }
+
+ ret = WaitForSingleObject(hThread, INFINITE);
+
+ if (ret == WAIT_FAILED)
+ {
+ Fail("Unable to wait on child thread!\nGetLastError returned %d.\n",
+ GetLastError());
+ }
+
+
+ if (0==CloseHandle(hThread))
+ {
+ Trace("Could not close Thread handle\n");
+ Fail ( "GetLastError returned %d\n", GetLastError());
+ }
+}
+
+/* Function doesn't do anything, just needed to interrupt the wait*/
+VOID PALAPI APCFunc(ULONG_PTR dwParam)
+{
+}
+
+/* Entry Point for child thread. */
+DWORD PALAPI WaiterProc(LPVOID lpParameter)
+{
+ UINT64 OldTimeStamp;
+ UINT64 NewTimeStamp;
+ BOOL Alertable;
+ DWORD ret;
+
+ Alertable = (BOOL) lpParameter;
+
+ LARGE_INTEGER performanceFrequency;
+ if (!QueryPerformanceFrequency(&performanceFrequency))
+ {
+ Fail("Failed to query performance frequency!");
+ }
+
+ OldTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+ ret = WaitForSingleObjectEx( hMutex,
+ ChildThreadWaitTime,
+ Alertable);
+
+ NewTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+ if (Alertable && ret != WAIT_IO_COMPLETION)
+ {
+ Fail("Expected the interrupted wait to return WAIT_IO_COMPLETION.\n"
+ "Got %d\n", ret);
+ }
+ else if (!Alertable && ret != WAIT_TIMEOUT)
+ {
+ Fail("WaitForSingleObjectEx did not timeout.\n"
+ "Expected return of WAIT_TIMEOUT, got %d.\n", ret);
+ }
+
+ ThreadWaitDelta = NewTimeStamp - OldTimeStamp;
+
+ return 0;
+}
+
+
+
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/CMakeLists.txt
new file mode 100644
index 0000000000..12517e31e0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ WFSOExSemaphoreTest.c
+)
+
+add_executable(paltest_waitforsingleobject_wfsoexsemaphoretest
+ ${SOURCES}
+)
+
+add_dependencies(paltest_waitforsingleobject_wfsoexsemaphoretest coreclrpal)
+
+target_link_libraries(paltest_waitforsingleobject_wfsoexsemaphoretest
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/WFSOExSemaphoreTest.c b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/WFSOExSemaphoreTest.c
new file mode 100644
index 0000000000..793c50995c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/WFSOExSemaphoreTest.c
@@ -0,0 +1,184 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: WFSOExSemaphore.c
+**
+** Purpose: Tests a child thread in the middle of a
+** WaitForSingleObjectEx call will be interrupted by QueueUserAPC
+** if the alert flag was set.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+/*Based on SleepEx/test2 */
+
+const int ChildThreadWaitTime = 4000;
+const int InterruptTime = 2000;
+const DWORD AcceptableDelta = 300;
+
+void RunTest(BOOL AlertThread);
+VOID PALAPI APCFunc(ULONG_PTR dwParam);
+DWORD PALAPI WaiterProc(LPVOID lpParameter);
+
+DWORD ThreadWaitDelta;
+
+int __cdecl main( int argc, char **argv )
+{
+ if (0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /*
+ On some platforms (e.g. FreeBSD 4.9) the first call to some synch objects
+ (such as conditions) involves some pthread internal initialization that
+ can make the first wait slighty longer, potentially going above the
+ acceptable delta for this test. Let's add a dummy wait to preinitialize
+ internal structures
+ */
+ Sleep(100);
+
+ /*
+ * Check that Queueing an APC in the middle of a wait does interrupt
+ * it, if it's in an alertable state.
+ */
+
+ RunTest(TRUE);
+ if ((ThreadWaitDelta - InterruptTime) > AcceptableDelta)
+ {
+ Fail("Expected thread to wait for %d ms (and get interrupted).\n"
+ "Thread waited for %d ms! (Acceptable delta: %d)\n",
+ InterruptTime, ThreadWaitDelta, AcceptableDelta);
+ }
+
+
+ /*
+ * Check that Queueing an APC in the middle of a wait does NOT interrupt
+ * it, if it is not in an alertable state.
+ */
+ RunTest(FALSE);
+ if ((ThreadWaitDelta - ChildThreadWaitTime) > AcceptableDelta)
+ {
+ Fail("Expected thread to wait for %d ms (and not be interrupted).\n"
+ "Thread waited for %d ms! (Acceptable delta: %d)\n",
+ ChildThreadWaitTime, ThreadWaitDelta, AcceptableDelta);
+ }
+
+
+ PAL_Terminate();
+ return PASS;
+}
+
+void RunTest(BOOL AlertThread)
+{
+ HANDLE hThread = 0;
+ DWORD dwThreadId = 0;
+ int ret;
+
+ hThread = CreateThread( NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)WaiterProc,
+ (LPVOID) AlertThread,
+ 0,
+ &dwThreadId);
+
+ if (hThread == NULL)
+ {
+ Fail("ERROR: Was not able to create the thread to test!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ Sleep(InterruptTime);
+
+ ret = QueueUserAPC(APCFunc, hThread, 0);
+ if (ret == 0)
+ {
+ Fail("QueueUserAPC failed! GetLastError returned %d\n",
+ GetLastError());
+ }
+
+ ret = WaitForSingleObject(hThread, INFINITE);
+ if (ret == WAIT_FAILED)
+ {
+ Fail("Unable to wait on child thread!\nGetLastError returned %d.\n",
+ GetLastError());
+ }
+
+ if (0==CloseHandle(hThread))
+ {
+ Trace("Could not close Thread handle\n");
+ Fail ( "GetLastError returned %d\n", GetLastError());
+ }
+}
+
+/* Function doesn't do anything, just needed to interrupt the wait*/
+VOID PALAPI APCFunc(ULONG_PTR dwParam)
+{
+}
+
+/* Entry Point for child thread. */
+DWORD PALAPI WaiterProc(LPVOID lpParameter)
+{
+ HANDLE hSemaphore;
+ UINT64 OldTimeStamp;
+ UINT64 NewTimeStamp;
+ BOOL Alertable;
+ DWORD ret;
+
+ LARGE_INTEGER performanceFrequency;
+ if (!QueryPerformanceFrequency(&performanceFrequency))
+ {
+ Fail("Failed to query performance frequency!");
+ }
+
+ /* Create a semaphore that is not in the signalled state */
+ hSemaphore = CreateSemaphoreW(NULL, 0, 1, NULL);
+
+ if (hSemaphore == NULL)
+ {
+ Fail("Failed to create semaphore! GetLastError returned %d.\n",
+ GetLastError());
+ }
+
+ Alertable = (BOOL) lpParameter;
+
+ OldTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+ ret = WaitForSingleObjectEx( hSemaphore,
+ ChildThreadWaitTime,
+ Alertable);
+
+ NewTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+
+ if (Alertable && ret != WAIT_IO_COMPLETION)
+ {
+ Fail("Expected the interrupted wait to return WAIT_IO_COMPLETION.\n"
+ "Got %d\n", ret);
+ }
+ else if (!Alertable && ret != WAIT_TIMEOUT)
+ {
+ Fail("WaitForSingleObjectEx did not timeout.\n"
+ "Expected return of WAIT_TIMEOUT, got %d.\n", ret);
+ }
+
+
+ ThreadWaitDelta = NewTimeStamp - OldTimeStamp;
+
+ ret = CloseHandle(hSemaphore);
+ if (!ret)
+ {
+ Fail("Unable to close handle to semaphore!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ return 0;
+}
+
+
+
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/CMakeLists.txt
new file mode 100644
index 0000000000..f3d868f082
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ WFSOExThreadTest.c
+)
+
+add_executable(paltest_waitforsingleobject_wfsoexthreadtest
+ ${SOURCES}
+)
+
+add_dependencies(paltest_waitforsingleobject_wfsoexthreadtest coreclrpal)
+
+target_link_libraries(paltest_waitforsingleobject_wfsoexthreadtest
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/WFSOExThreadTest.c b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/WFSOExThreadTest.c
new file mode 100644
index 0000000000..894d2804aa
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/WFSOExThreadTest.c
@@ -0,0 +1,204 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: WFSOExThreadTest.c
+**
+** Purpose: Tests a child thread in the middle of a
+** WaitForSingleObjectEx call will be interrupted by QueueUserAPC
+** if the alert flag was set.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+/*Based on SleepEx/test2 */
+
+const int ChildThreadWaitTime = 4000;
+const int InterruptTime = 2000;
+const DWORD AcceptableDelta = 300;
+
+void RunTest(BOOL AlertThread);
+VOID PALAPI APCFunc(ULONG_PTR dwParam);
+DWORD PALAPI WaiterProc(LPVOID lpParameter);
+void WorkerThread(void);
+
+int ThreadWaitDelta;
+
+int __cdecl main( int argc, char **argv )
+{
+ if (0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /*
+ On some platforms (e.g. FreeBSD 4.9) the first call to some synch objects
+ (such as conditions) involves some pthread internal initialization that
+ can make the first wait slighty longer, potentially going above the
+ acceptable delta for this test. Let's add a dummy wait to preinitialize
+ internal structures
+ */
+ Sleep(100);
+
+ /*
+ * Check that Queueing an APC in the middle of a wait does interrupt
+ * it, if it's in an alertable state.
+ */
+
+ RunTest(TRUE);
+ if (abs(ThreadWaitDelta - InterruptTime) > AcceptableDelta)
+ {
+ Fail("Expected thread to wait for %d ms (and get interrupted).\n"
+ "Thread waited for %d ms! (Acceptable delta: %d)\n",
+ InterruptTime, ThreadWaitDelta, AcceptableDelta);
+ }
+
+
+ /*
+ * Check that Queueing an APC in the middle of a wait does NOT interrupt
+ * it, if it is not in an alertable state.
+ */
+ RunTest(FALSE);
+ if (abs(ThreadWaitDelta - ChildThreadWaitTime) > AcceptableDelta)
+ {
+ Fail("Expected thread to wait for %d ms (and not be interrupted).\n"
+ "Thread waited for %d ms! (Acceptable delta: %d)\n",
+ ChildThreadWaitTime, ThreadWaitDelta, AcceptableDelta);
+ }
+
+
+ PAL_Terminate();
+ return PASS;
+}
+
+void RunTest(BOOL AlertThread)
+{
+ HANDLE hThread = 0;
+ DWORD dwThreadId = 0;
+ int ret;
+
+ //Create thread
+ hThread = CreateThread( NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)WaiterProc,
+ (LPVOID) AlertThread,
+ 0,
+ &dwThreadId);
+
+ if (hThread == NULL)
+ {
+ Fail("ERROR: Was not able to create the thread to test!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ Sleep(InterruptTime);
+
+ ret = QueueUserAPC(APCFunc, hThread, 0);
+ if (ret == 0)
+ {
+ Fail("QueueUserAPC failed! GetLastError returned %d\n",
+ GetLastError());
+ }
+
+
+ ret = WaitForSingleObject(hThread, INFINITE);
+ if (ret == WAIT_FAILED)
+ {
+ Fail("Unable to wait on child thread!\nGetLastError returned %d.\n",
+ GetLastError());
+ }
+
+ if (0==CloseHandle(hThread))
+ {
+ Trace("Could not close Thread handle\n");
+ Fail ( "GetLastError returned %d\n", GetLastError());
+ }
+}
+
+/* Function doesn't do anything, just needed to interrupt the wait*/
+VOID PALAPI APCFunc(ULONG_PTR dwParam)
+{
+}
+
+/* Entry Point for child thread. */
+DWORD PALAPI WaiterProc(LPVOID lpParameter)
+{
+ HANDLE hWaitThread;
+ UINT64 OldTimeStamp;
+ UINT64 NewTimeStamp;
+ BOOL Alertable;
+ DWORD ret;
+ DWORD dwThreadId = 0;
+
+/*
+When a thread terminates, the thread object attains a signaled state,
+satisfying any threads that were waiting on the object.
+*/
+
+/* Create a thread that does not return immediately to maintain a non signaled test*/
+ hWaitThread = CreateThread( NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)WorkerThread,
+ NULL,
+ 0,
+ &dwThreadId);
+
+ if (hWaitThread == NULL)
+ {
+ Fail("ERROR: Was not able to create worker thread to wait on!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ Alertable = (BOOL) lpParameter;
+
+ LARGE_INTEGER performanceFrequency;
+ if (!QueryPerformanceFrequency(&performanceFrequency))
+ {
+ Fail("Failed to query performance frequency!");
+ }
+
+ OldTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+ ret = WaitForSingleObjectEx( hWaitThread,
+ ChildThreadWaitTime,
+ Alertable);
+
+ NewTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+
+ if (Alertable && ret != WAIT_IO_COMPLETION)
+ {
+ Fail("Expected the interrupted wait to return WAIT_IO_COMPLETION.\n"
+ "Got %d\n", ret);
+ }
+ else if (!Alertable && ret != WAIT_TIMEOUT)
+ {
+ Fail("WaitForSingleObjectEx did not timeout.\n"
+ "Expected return of WAIT_TIMEOUT, got %d.\n", ret);
+ }
+
+ ThreadWaitDelta = NewTimeStamp - OldTimeStamp;
+
+ ret = CloseHandle(hWaitThread);
+ if (!ret)
+ {
+ Fail("Unable to close handle to Thread!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ return 0;
+}
+
+
+void WorkerThread(void)
+{
+
+ //Make the worker thread sleep to test WFSOEx Functionality
+
+ Sleep(2*ChildThreadWaitTime);
+}
+
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/CMakeLists.txt
new file mode 100644
index 0000000000..374880e90f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ WFSOMutexTest.c
+)
+
+add_executable(paltest_waitforsingleobject_wfsomutextest
+ ${SOURCES}
+)
+
+add_dependencies(paltest_waitforsingleobject_wfsomutextest coreclrpal)
+
+target_link_libraries(paltest_waitforsingleobject_wfsomutextest
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/WFSOMutexTest.c b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/WFSOMutexTest.c
new file mode 100644
index 0000000000..5ecf517c2a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/WFSOMutexTest.c
@@ -0,0 +1,184 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: WFSOMutexTest.c
+**
+** Purpose: Test for WaitForSingleObjectTest.
+** Create Mutex Object
+** Create Two Threads, Each Threads does WFSO for the Mutex Object
+** Increments Counter
+** Releases Mutex
+** Test Passes if the above operations are successful
+**
+**
+**
+**=========================================================*/
+
+
+
+#include <palsuite.h>
+
+
+#define NUMBER_OF_WORKER_THREADS 2
+
+//Declaring Variables
+HANDLE hMutex = NULL;
+unsigned int globalcounter =0;
+int testReturnCode = PASS;
+
+//Declaring Function Prototypes
+DWORD WFSOMutexTest(LPVOID params);
+void incrementCounter(void);
+
+
+
+int __cdecl main(int argc, char **argv)
+{
+
+ //Declare local variables
+ int i =0;
+
+ // 2 dimensional array to hold thread handles for each worker thread
+ HANDLE hThread[NUMBER_OF_WORKER_THREADS];
+ DWORD dwThreadId=0;
+ int returnCode = 0;
+
+ //Initialize PAL
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ //Create Mutex
+ hMutex = CreateMutex(NULL, // no security attributes
+ FALSE, // initially not owned
+ NULL); // name of mutex
+
+ //Check for Mutex Creation
+
+ if (hMutex == NULL)
+ {
+ Fail("Create Mutex Failed, GetLastError: %d\n", GetLastError());
+ }
+
+
+ //Spawn 2 worker threads
+ for (i=0;i<NUMBER_OF_WORKER_THREADS;i++)
+ {
+ //Create Thread
+
+ hThread[i] = CreateThread(
+ NULL,
+ 0,
+ WFSOMutexTest,
+ NULL,
+ 0,
+ &dwThreadId);
+
+ if ( NULL == hThread[i] )
+ {
+ Fail ( "CreateThread() returned NULL. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ }
+
+ /* Test running */
+ returnCode = WaitForMultipleObjects( NUMBER_OF_WORKER_THREADS, hThread, TRUE, 5000);
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) returned %d, and GetLastError value is %d\n", returnCode, GetLastError());
+ testReturnCode = FAIL;
+ }
+
+//Close thread handles
+for (i=0;i<NUMBER_OF_WORKER_THREADS;i++)
+ {
+
+ if (0==CloseHandle(hThread[i]))
+ {
+ Trace("Could not Close thread handle\n");
+ Fail ( "GetLastError returned %d\n", GetLastError());
+ }
+ }
+
+//Close Mutex Handle
+if (0==CloseHandle(hMutex))
+ {
+ Trace("Could not close mutex handle\n");
+ Fail ( "GetLastError returned %d\n", GetLastError());
+ }
+
+
+PAL_TerminateEx(testReturnCode);
+return ( testReturnCode );
+
+}
+
+
+void incrementCounter(void)
+{
+ if (INT_MAX == globalcounter)
+ {
+ globalcounter = 0;
+ }
+
+ globalcounter++;
+ Trace("Global Counter Value: %d \n", globalcounter);
+}
+
+
+DWORD WFSOMutexTest(LPVOID params)
+{
+
+ DWORD dwWaitResult;
+
+ // Request ownership of mutex.
+
+ dwWaitResult = WaitForSingleObject(
+ hMutex, // handle to mutex
+ 5000L); // five-second time-out interval
+
+ switch (dwWaitResult)
+ {
+ // The thread got mutex ownership.
+ case WAIT_OBJECT_0:
+ {
+
+ incrementCounter();
+
+ //Release ownership of the mutex object.
+ if (! ReleaseMutex(hMutex))
+ {
+ Fail ( "ReleaseMutex() returned NULL. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ break;
+ }
+
+ // Cannot get mutex ownership due to time-out.
+ case WAIT_TIMEOUT:
+ {
+ Fail ( "Cannot get mutex ownership due to time-out. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ return FALSE;
+ }
+
+ // Got ownership of the abandoned mutex object.
+ case WAIT_ABANDONED:
+ {
+ Fail ( "Got ownership of the abandoned mutex object. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ return FALSE;
+ }
+ }
+
+ return 1;
+}
+
+
+
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/CMakeLists.txt
new file mode 100644
index 0000000000..a70b101988
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ WFSOProcessTest.c
+)
+
+add_executable(paltest_waitforsingleobject_wfsoprocesstest
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_waitforsingleobject_wfsoprocesstest coreclrpal)
+
+target_link_libraries(paltest_waitforsingleobject_wfsoprocesstest
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ ChildProcess.c
+)
+
+add_executable(paltest_waitforsingleobject_wfsoprocesstest_child
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_waitforsingleobject_wfsoprocesstest_child coreclrpal)
+
+target_link_libraries(paltest_waitforsingleobject_wfsoprocesstest_child
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/ChildProcess.c b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/ChildProcess.c
new file mode 100644
index 0000000000..91c24d87bb
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/ChildProcess.c
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: ChildProcess.c
+**
+** Purpose: Dummy Process which does some work on which the Main Test case waits
+**
+**
+
+**
+**=========================================================*/
+
+
+
+#include <palsuite.h>
+
+int __cdecl main(int argc, char **argv)
+{
+
+//Declare local variables
+int i =0;
+
+
+
+//Initialize PAL
+if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+//Do some work
+for (i=0; i<100000; i++);
+
+Trace("Counter Value was incremented to %d \n ",i);
+
+PAL_Terminate();
+return ( PASS );
+
+}
+
+
+
+
+
+
+
+
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.c b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.c
new file mode 100644
index 0000000000..2711e26c29
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.c
@@ -0,0 +1,119 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: WFSOProcessTest.c
+**
+** Purpose: Test for WaitForSingleObjectTest.
+** Create One Process and do some work
+** Use WFSO For the Process to finish
+**
+** Test Passes if the above operations are successful
+**
+**
+**
+**=========================================================*/
+
+
+
+#include <palsuite.h>
+
+int __cdecl main(int argc, char **argv)
+{
+
+//Declare local variables
+STARTUPINFO si;
+PROCESS_INFORMATION pi;
+
+DWORD dwWaitResult=0;
+
+//Initialize PAL
+if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+
+ZeroMemory( &si, sizeof(si) );
+si.cb = sizeof(si);
+ZeroMemory( &pi, sizeof(pi) );
+
+// Start the child process.
+if( !CreateProcess( NULL, // No module name (use command line).
+ "childprocess", // Command line.
+ NULL, // Process handle not inheritable.
+ NULL, // Thread handle not inheritable.
+ FALSE, // Set handle inheritance to FALSE.
+ 0, // No creation flags.
+ NULL, // Use parent's environment block.
+ NULL, // Use parent's starting directory.
+ &si, // Pointer to STARTUPINFO structure.
+ &pi ) // Pointer to PROCESS_INFORMATION structure.
+)
+
+{
+Fail ( "Create Process Failed. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+}
+
+// Wait until child process exits.
+ dwWaitResult = WaitForSingleObject( pi.hProcess, INFINITE );
+switch (dwWaitResult)
+ {
+ // The Process wait was successful
+ case WAIT_OBJECT_0:
+ {
+
+ Trace("Wait for Process was successful\n");
+ break;
+ }
+
+ // Time-out.
+ case WAIT_TIMEOUT:
+ {
+ Fail ( "Time -out. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ return FALSE;
+ }
+
+ // Got ownership of the abandoned process object.
+ case WAIT_ABANDONED:
+ {
+ Fail ( "Got ownership of the abandoned Process object. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ return FALSE;
+ }
+
+ //Error condition
+ case WAIT_FAILED:
+ {
+ Fail ( "Wait for Process Failed. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ return FALSE;
+ }
+
+}
+
+
+
+// Close process handle
+if (0==CloseHandle(pi.hProcess))
+ {
+ Trace("Could not close process handle\n");
+ Fail ( "GetLastError returned %d\n", GetLastError());
+ }
+
+
+PAL_Terminate();
+return ( PASS );
+
+}
+
+
+
+
+
+
+
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOSemaphoreTest/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOSemaphoreTest/CMakeLists.txt
new file mode 100644
index 0000000000..171e0583e5
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOSemaphoreTest/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ WFSOSemaphoreTest.c
+)
+
+add_executable(paltest_waitforsingleobject_wfsosemaphoretest
+ ${SOURCES}
+)
+
+add_dependencies(paltest_waitforsingleobject_wfsosemaphoretest coreclrpal)
+
+target_link_libraries(paltest_waitforsingleobject_wfsosemaphoretest
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOSemaphoreTest/WFSOSemaphoreTest.c b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOSemaphoreTest/WFSOSemaphoreTest.c
new file mode 100644
index 0000000000..9902d448cd
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOSemaphoreTest/WFSOSemaphoreTest.c
@@ -0,0 +1,183 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: WFSOMutexTest.c
+**
+** Purpose: Test for WaitForSingleObjectTest.
+** Create Semaphore Object
+** Create Two Threads, Each Threads does WFSO for the Semaphore Object
+** Increments Counter
+** Releases Semaphore
+** Test Passes if the above operations are successful
+**
+**
+**
+**=========================================================*/
+
+
+
+#include <palsuite.h>
+
+
+#define NUMBER_OF_WORKER_THREADS 2
+
+
+//Declaring Variables
+HANDLE hSemaphore = NULL;
+unsigned int globalcounter =0;
+int testReturnCode = PASS;
+
+//Declaring Function Prototypes
+DWORD WFSOSemaphoreTest(LPVOID params);
+void incrementCounter(void);
+
+int __cdecl main(int argc, char **argv)
+{
+
+ //Declare local variables
+ int i =0;
+ int cMax = 2;
+
+ int returnCode = 0;
+
+ // 2 dimensional array to hold thread handles for each worker thread
+ HANDLE hThread[NUMBER_OF_WORKER_THREADS];
+ DWORD dwThreadId=0;
+
+ //Initialize PAL
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ //Create Semaphore
+ hSemaphore = CreateSemaphore(
+ NULL, // no security attributes
+ cMax, // initial count
+ cMax, // maximum count
+ NULL); // unnamed semaphore
+
+ if (hSemaphore == NULL)
+ {
+ // Check for error.
+ Fail("Create Semaphore Failed, GetLastError: %d\n", GetLastError());
+ }
+
+
+
+ //Spawn 2 worker threads
+ for (i=0;i<NUMBER_OF_WORKER_THREADS;i++)
+ {
+ //Create Thread
+
+ hThread[i] = CreateThread(
+ NULL,
+ 0,
+ WFSOSemaphoreTest,
+ NULL,
+ 0,
+ &dwThreadId);
+
+ if ( NULL == hThread[i] )
+ {
+ Fail ( "CreateThread() returned NULL. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ }
+
+
+ /* Test running */
+ returnCode = WaitForMultipleObjects( NUMBER_OF_WORKER_THREADS, hThread, TRUE, 5000);
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) returned %d, and GetLastError value is %d\n", returnCode, GetLastError());
+ testReturnCode = FAIL;
+ }
+
+//Close thread handles
+for (i=0;i<NUMBER_OF_WORKER_THREADS;i++)
+ {
+
+ if (0==CloseHandle(hThread[i]))
+ {
+ Trace("Could not Close thread handle\n");
+ Fail ( "GetLastError returned %d\n", GetLastError());
+ }
+ }
+
+//Close Semaphore Handle
+if (0==CloseHandle(hSemaphore))
+ {
+ Trace("Could not close semaphore handle\n");
+ Fail ( "GetLastError returned %d\n", GetLastError());
+ }
+
+PAL_TerminateEx(testReturnCode);
+return ( testReturnCode );
+
+}
+
+
+void incrementCounter(void)
+{
+ if (INT_MAX == globalcounter)
+ {
+ globalcounter = 0;
+ }
+
+ globalcounter++;
+ Trace("Global Counter Value: %d \n", globalcounter);
+}
+
+
+DWORD WFSOSemaphoreTest(LPVOID params)
+{
+
+ DWORD dwWaitResult;
+
+ // Request ownership of Semaphore
+
+ dwWaitResult = WaitForSingleObject(
+ hSemaphore, // handle to semaphore
+ 0L); // zero-second time-out interval
+
+
+ switch (dwWaitResult)
+ {
+ // The semaphore object was signaled.
+ case WAIT_OBJECT_0:
+ {
+
+ incrementCounter();
+ // Increment the count of the semaphore.
+
+ if (!ReleaseSemaphore(
+ hSemaphore, // handle to semaphore
+ 1, // increase count by one
+ NULL) ) // not interested in previous count
+ {
+ Fail ( "ReleaseSemaphore() returned NULL. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ break;
+ }
+
+ // Semaphore was nonsignaled, so a time-out occurred.
+ case WAIT_TIMEOUT:
+ {
+ Fail ( "Semaphore was nonsignaled, so a time-out occurred. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ return FALSE;
+ }
+ }
+
+ return 1;
+}
+
+
+
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/CMakeLists.txt
new file mode 100644
index 0000000000..18701a9da5
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ WFSOThreadTest.c
+)
+
+add_executable(paltest_waitforsingleobject_wfsothreadtest
+ ${SOURCES}
+)
+
+add_dependencies(paltest_waitforsingleobject_wfsothreadtest coreclrpal)
+
+target_link_libraries(paltest_waitforsingleobject_wfsothreadtest
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/WFSOThreadTest.c b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/WFSOThreadTest.c
new file mode 100644
index 0000000000..378350671a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/WFSOThreadTest.c
@@ -0,0 +1,179 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: WFSOThreadTest.c
+**
+** Purpose: Test for WaitForSingleObjectTest.
+** Create One Thread and do some work
+** Use WFSO For the Thread to finish
+**
+** Test Passes if the above operations are successful
+**
+**
+**
+**=========================================================*/
+
+
+
+#include <palsuite.h>
+
+
+//Declaring Variables
+HANDLE hThread = NULL;
+HANDLE hEvent = NULL;
+
+unsigned int globalcounter =0;
+
+//Declaring Function Prototypes
+DWORD incrementCounter(LPVOID params);
+
+int __cdecl main(int argc, char **argv)
+{
+
+ //Declare local variables
+ DWORD dwThreadId=0;
+ DWORD dwWaitResult=0;
+
+ //Initialize PAL
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+
+ //Create Event
+ hEvent = CreateEvent(NULL,TRUE,FALSE, NULL);
+ if(hEvent == NULL)
+ {
+ Fail("Create Event Failed\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+
+ //Create Thread
+ hThread = CreateThread(
+ NULL,
+ 0,
+ incrementCounter,
+ NULL,
+ 0,
+ &dwThreadId);
+
+ if ( NULL == hThread )
+ {
+ Fail ( "CreateThread() returned NULL. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+
+ //Wait For Thread to signal start
+ dwWaitResult = WaitForSingleObject(hEvent,INFINITE);
+
+ switch (dwWaitResult)
+ {
+ // The thread wait was successful
+ case WAIT_OBJECT_0:
+ {
+
+ Trace ("Wait for Single Object (hEvent) was successful.\n");
+ break;
+ }
+
+ // Time-out.
+ case WAIT_TIMEOUT:
+ {
+ Fail ( "Time -out. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ return FALSE;
+ }
+
+ // Got ownership of the abandoned event object.
+ case WAIT_ABANDONED:
+ {
+ Fail ( "Got ownership of the abandoned event object. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ return FALSE;
+ }
+
+ }
+
+
+ //Wait for Thread to finish
+ dwWaitResult = WaitForSingleObject(
+ hThread, //handle to thread
+ 5000L); //Wait Indefinitely
+
+
+ switch (dwWaitResult)
+ {
+ // The thread wait was successful
+ case WAIT_OBJECT_0:
+ {
+
+ Trace("Wait for thread was successful\n");
+
+ break;
+ }
+
+ // Time-out.
+ case WAIT_TIMEOUT:
+ {
+ Fail ( "Time -out. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ return FALSE;
+ }
+
+ // Got ownership of the abandoned thread object.
+ case WAIT_ABANDONED:
+ {
+ Fail ( "Got ownership of the abandoned thread object. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ return FALSE;
+ }
+
+ }
+
+
+//Close Handles
+if (0==CloseHandle(hEvent))
+ {
+ Trace("Could not Close event handle\n");
+ Fail ( "GetLastError returned %d\n", GetLastError());
+ }
+if (0==CloseHandle(hThread))
+ {
+ Trace("Could not Close thread handle\n");
+ Fail ( "GetLastError returned %d\n", GetLastError());
+ }
+
+PAL_Terminate();
+return ( PASS );
+
+}
+
+DWORD incrementCounter(LPVOID params)
+{
+
+ //Signal Event so that main thread can start to wait for thread object
+ if (0==SetEvent(hEvent))
+ {
+ Fail ( "SetEvent returned Zero. Failing test.\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ for (globalcounter=0;globalcounter<100000;globalcounter++);
+
+ //Sleep(5000);
+
+ Trace("Global Counter Value: %d \n", globalcounter);
+ return 0;
+}
+
+
+
+
+
+
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForSingleObject/test1/CMakeLists.txt
new file mode 100644
index 0000000000..0a71330ba1
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_waitforsingleobject_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_waitforsingleobject_test1 coreclrpal)
+
+target_link_libraries(paltest_waitforsingleobject_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/test1/test1.c b/src/pal/tests/palsuite/threading/WaitForSingleObject/test1/test1.c
new file mode 100644
index 0000000000..2af80df677
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/test1/test1.c
@@ -0,0 +1,121 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test1.c
+**
+** Purpose: Test for WaitForSingleObjectTest. Create two events, one
+** with a TRUE and one with FALSE intial state. Ensure that WaitForSingle
+** returns correct values for each of these.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+BOOL WaitForSingleObjectTest()
+{
+
+ BOOL bRet = FALSE;
+ DWORD dwRet = 0;
+
+ LPSECURITY_ATTRIBUTES lpEventAttributes = 0;
+ BOOL bManualReset = TRUE;
+ BOOL bInitialState = TRUE;
+
+ HANDLE hEvent;
+
+ /* Create an event, and ensure the HANDLE is valid */
+ hEvent = CreateEvent(lpEventAttributes, bManualReset,
+ bInitialState, NULL);
+
+ if (hEvent != INVALID_HANDLE_VALUE)
+ {
+
+ /* Call WaitForSingleObject with 0 time on the event. It
+ should return WAIT_OBJECT_0
+ */
+
+ dwRet = WaitForSingleObject(hEvent,0);
+
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Trace("WaitForSingleObjectTest:WaitForSingleObject failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ bRet = CloseHandle(hEvent);
+
+ if (!bRet)
+ {
+ Trace("WaitForSingleObjectTest:CloseHandle failed (%x)\n", GetLastError());
+ }
+ }
+ }
+ else
+ {
+ Trace("WaitForSingleObjectTest:CreateEvent failed (%x)\n", GetLastError());
+ }
+
+ /* If the first section passed, Create another event, with the
+ intial state being FALSE this time.
+ */
+
+ if (bRet)
+ {
+ bRet = FALSE;
+
+ bInitialState = FALSE;
+
+ hEvent = CreateEvent( lpEventAttributes,
+ bManualReset, bInitialState, NULL);
+
+ if (hEvent != INVALID_HANDLE_VALUE)
+ {
+
+ /* Test WaitForSingleObject and ensure that it returns
+ WAIT_TIMEOUT in this case.
+ */
+
+ dwRet = WaitForSingleObject(hEvent,0);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("WaitForSingleObjectTest:WaitForSingleObject failed (%x)\n", GetLastError());
+ }
+ else
+ {
+ bRet = CloseHandle(hEvent);
+
+ if (!bRet)
+ {
+ Trace("WaitForSingleObjectTest:CloseHandle failed (%x)\n", GetLastError());
+ }
+ }
+ }
+ else
+ {
+ Trace("WaitForSingleObjectTest::CreateEvent failed (%x)\n", GetLastError());
+ }
+ }
+ return bRet;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!WaitForSingleObjectTest())
+ {
+ Fail ("Test failed\n");
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/WaitForSingleObject/test1/testinfo.dat b/src/pal/tests/palsuite/threading/WaitForSingleObject/test1/testinfo.dat
new file mode 100644
index 0000000000..89193d1c20
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/test1/testinfo.dat
@@ -0,0 +1,14 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = WaitForSingleObject
+Name = Positive Test for WaitForSingleObject
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Test for WaitForSingleObject. Create two events, one
+= with a TRUE and one with FALSE intial state. Ensure that WaitForSingle
+= returns correct values for each of these.
diff --git a/src/pal/tests/palsuite/threading/YieldProcessor/CMakeLists.txt b/src/pal/tests/palsuite/threading/YieldProcessor/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/YieldProcessor/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/YieldProcessor/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/YieldProcessor/test1/CMakeLists.txt
new file mode 100644
index 0000000000..5002bf763d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/YieldProcessor/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_yieldprocessor_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_yieldprocessor_test1 coreclrpal)
+
+target_link_libraries(paltest_yieldprocessor_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/YieldProcessor/test1/test1.c b/src/pal/tests/palsuite/threading/YieldProcessor/test1/test1.c
new file mode 100644
index 0000000000..6adbe989c2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/YieldProcessor/test1/test1.c
@@ -0,0 +1,92 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Source: test1.c
+**
+** Purpose: Test to ensure YieldProcessor works, without
+** causing test to hang
+**
+** Dependencies: PAL_Initialize
+** Fail
+** YieldProcessor
+** WaitForMultipleObject
+** CreateThread
+** GetLastError
+**
+
+**
+**===========================================================================*/
+
+
+#include <palsuite.h>
+#define THREAD_COUNT 10
+#define REPEAT_COUNT 1000
+#define TIMEOUT 60000
+void PALAPI Run_Thread(LPVOID lpParam);
+
+/**
+ * main
+ *
+ * executable entry point
+ */
+INT __cdecl main( INT argc, CHAR **argv )
+{
+ DWORD dwParam;
+ HANDLE hThread[THREAD_COUNT];
+ DWORD threadId[THREAD_COUNT];
+
+ int i = 0;
+ int returnCode = 0;
+
+ /*PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return FAIL;
+ }
+
+
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ dwParam = (int) i;
+ //Create thread
+ hThread[i] = CreateThread(
+ NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE)Run_Thread,/* thread function */
+ (LPVOID)dwParam, /* argument to thread function */
+ 0, /* use default creation flags */
+ &threadId[i] /* returns the thread identifier*/
+ );
+
+ if(hThread[i] == NULL)
+ {
+ Fail("Create Thread failed for iteration %d GetLastError value is %d\n", i, GetLastError());
+ }
+
+ }
+
+
+ returnCode = WaitForMultipleObjects(THREAD_COUNT, hThread, TRUE, TIMEOUT);
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) returned %d, expected value is %d, and GetLastError value is %d\n", returnCode, WAIT_OBJECT_0, GetLastError());
+ }
+
+ PAL_Terminate();
+ return PASS;
+
+}
+
+void PALAPI Run_Thread (LPVOID lpParam)
+{
+ int i = 0;
+
+ for(i=0; i < REPEAT_COUNT; i++ )
+ {
+ // No error code set nor does it have any return code
+ YieldProcessor();
+ }
+}
diff --git a/src/pal/tests/palsuite/threading/YieldProcessor/test1/testinfo.dat b/src/pal/tests/palsuite/threading/YieldProcessor/test1/testinfo.dat
new file mode 100644
index 0000000000..6d12110c34
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/YieldProcessor/test1/testinfo.dat
@@ -0,0 +1,13 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = YieldProcessor
+Name = Test for YieldProcessor
+TYPE = DEFAULT
+EXE1 = test
+Description
+= Purpose: Test to ensure YieldProcessor is
+= working properly on supported platforms
diff --git a/src/pal/tests/palsuite/threading/releasesemaphore/CMakeLists.txt b/src/pal/tests/palsuite/threading/releasesemaphore/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/releasesemaphore/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/threading/releasesemaphore/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/releasesemaphore/test1/CMakeLists.txt
new file mode 100644
index 0000000000..e415f91fb2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/releasesemaphore/test1/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test.c
+)
+
+add_executable(paltest_releasesemaphore_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_releasesemaphore_test1 coreclrpal)
+
+target_link_libraries(paltest_releasesemaphore_test1
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/releasesemaphore/test1/test.c b/src/pal/tests/palsuite/threading/releasesemaphore/test1/test.c
new file mode 100644
index 0000000000..4d736b7d9a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/releasesemaphore/test1/test.c
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================================
+**
+** Source: releasesemaphore/test1/createsemaphore.c
+**
+** Purpose: Check that ReleaseSemaphore fails when using a semaphore handle
+** which has been closed by a call to CloseHandle. Check that
+** ReleaseSemaphore fails when using a ReleaseCount of zero or less than
+** zero.
+**
+**
+**==========================================================================*/
+
+#include <palsuite.h>
+
+HANDLE hSemaphore;
+
+int __cdecl main (int argc, char **argv)
+{
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return (FAIL);
+ }
+ hSemaphore = CreateSemaphoreA (NULL, 1, 2, NULL);
+
+ if (NULL == hSemaphore)
+ {
+ Fail("PALSUITE ERROR: CreateSemaphoreA ('%p' '%ld' '%ld' "
+ "'%p') returned NULL.\nGetLastError returned %d.\n",
+ NULL, 1, 2, NULL, GetLastError());
+ }
+
+ if(ReleaseSemaphore(hSemaphore, 0, NULL))
+ {
+ Fail("PALSUITE ERROR: ReleaseSemaphore('%p' '%ld' '%p') "
+ "call returned %d\nwhen it should have returned "
+ "%d.\nGetLastError returned %d.\n",
+ hSemaphore, 0, NULL, FALSE, TRUE, GetLastError());
+ }
+
+ if(ReleaseSemaphore(hSemaphore, -1, NULL))
+ {
+ Fail("PALSUITE ERROR: ReleaseSemaphore('%p' '%ld' '%p') "
+ "call returned %d\nwhen it should have returned "
+ "%d.\nGetLastError returned %d.\n",
+ hSemaphore, -1, NULL, TRUE, FALSE, GetLastError());
+ }
+
+ if(!CloseHandle(hSemaphore))
+ {
+ Fail("PALSUITE ERROR: CloseHandle(%p) call failed. GetLastError "
+ "returned %d.\n", hSemaphore, GetLastError());
+ }
+
+ if(ReleaseSemaphore(hSemaphore, 1, NULL))
+ {
+ Fail("PALSUITE ERROR: ReleaseSemaphore('%p' '%ld' '%p') "
+ "call incremented semaphore %p count\nafter the handle "
+ "was closed by a call to CloseHandle.\n GetLastError returned "
+ "%d.\n", hSemaphore, -1, NULL, hSemaphore, GetLastError());
+ }
+
+ PAL_Terminate();
+ return (PASS);
+}
diff --git a/src/pal/tests/palsuite/threading/releasesemaphore/test1/testinfo.dat b/src/pal/tests/palsuite/threading/releasesemaphore/test1/testinfo.dat
new file mode 100644
index 0000000000..b4d647a592
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/releasesemaphore/test1/testinfo.dat
@@ -0,0 +1,15 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = ReleaseSemaphore
+Name = Positive Test for ReleaseSemaphore
+TYPE = DEFAULT
+EXE1 = test
+Description
+= Check that ReleaseSemaphore fails when using a semaphore handle
+= which has been closed by a call to CloseHandle. Check that
+= ReleaseSemaphore fails when using a ReleaseCount of zero or less than
+= zero.