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.txt46
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/CMakeLists.txt6
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test1/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test1/test1.c100
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test2/test2.c89
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test2/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test3/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventA/test3/test3.c220
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test1/test1.c99
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test2/test2.c91
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test2/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test3/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateEventW/test3/test3.c234
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/CreateMutexA.c346
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/CreateMutexA.c332
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CreateMutexW.c346
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CreateMutexW.c341
-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.txt38
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test1/childProcess.c132
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test1/parentProcess.c202
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test1/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/CMakeLists.txt38
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/childprocess.c70
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/parentprocess.c244
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/test2.h73
-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.txt38
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test1/childProcess.c151
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test1/parentProcess.c211
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test1/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test2/CMakeLists.txt38
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test2/childprocess.c79
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.c246
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessW/test2/test2.h32
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/CreateSemaphore.c323
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/CreateSemaphore.c314
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/createsemaphore.c191
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.c324
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CreateSemaphore.c315
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/createsemaphore.c192
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test1/test1.c120
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test2/test2.c185
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test2/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test3/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CreateThread/test3/test3.c102
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/InitializeCriticalSection.c236
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/testinfo.dat21
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/test2.c227
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/testinfo.dat18
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/test3.c377
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/testinfo.dat29
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/test4.c242
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/test5.c188
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/testinfo.dat12
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/test6.c191
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/testinfo.dat12
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/test7.c189
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/testinfo.dat12
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/test8.c218
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/thistest.dat2
-rw-r--r--src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/test1.c146
-rw-r--r--src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/testlib.c48
-rw-r--r--src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/dllmain1.c67
-rw-r--r--src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/dllmain2.c67
-rw-r--r--src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/test2.c238
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test1/test1.c153
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test1/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test10/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test10/test10.c240
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test10/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test11/CMakeLists.txt38
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test11/childprocess.c75
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test11/myexitcode.h14
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test11/test11.c365
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test11/testinfo.dat19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test12/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test12/test12.c130
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test12/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test2/test2.c97
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test2/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test3/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test3/test3.c124
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test3/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test4/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test4/test4.c240
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test4/testinfo.dat19
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test5/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test5/test5.c146
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test5/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test6/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test6/test6.c147
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test6/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test7/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test7/test7.c150
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test7/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test8/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test8/test8.c155
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test8/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test9/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/DuplicateHandle/test9/test9.c128
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test1/ExitProcess.c33
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test2/test2.c31
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test2/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test3/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/ExitProcess/test3/test3.c41
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test1/test1.c115
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test2/CMakeLists.txt38
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test2/childprocess.c42
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test2/myexitcode.h15
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test2/test2.c169
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test2/testinfo.dat19
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test3/dllmain.c66
-rw-r--r--src/pal/tests/palsuite/threading/ExitThread/test3/test3.c163
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentProcess/test1/process.c41
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/processId.c42
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThread/test1/testinfo.dat12
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThread/test1/thread.c87
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThread/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThread/test2/test2.c142
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/threadId.c83
-rw-r--r--src/pal/tests/palsuite/threading/GetExitCodeProcess/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/CMakeLists.txt38
-rw-r--r--src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/childProcess.c32
-rw-r--r--src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/myexitcode.h15
-rw-r--r--src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/test1.c164
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/GetProcessTimes/test2/test2.c118
-rw-r--r--src/pal/tests/palsuite/threading/GetProcessTimes/test2/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/CMakeLists.txt8
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test1/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test1/test1.c135
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test1/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test2/test2.c195
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test2/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test3/CMakeLists.txt38
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test3/childprocess.c82
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test3/test3.c188
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test3/testinfo.dat21
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test4/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test4/test4.c113
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test4/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test5/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/OpenEventW/test5/test5.c198
-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.txt38
-rw-r--r--src/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.c76
-rw-r--r--src/pal/tests/palsuite/threading/OpenProcess/test1/myexitcode.h15
-rw-r--r--src/pal/tests/palsuite/threading/OpenProcess/test1/test1.c283
-rw-r--r--src/pal/tests/palsuite/threading/OpenProcess/test1/testinfo.dat19
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/CMakeLists.txt10
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test1/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test1/test1.c314
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test1/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test2/test2.c225
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test2/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test3/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test3/test3.c34
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test3/testinfo.dat12
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test4/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test4/test4.c73
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test4/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test5/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test5/test5.c201
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test5/testinfo.dat20
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test6/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test6/test6.c130
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test6/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test7/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/QueueUserAPC/test7/test7.c254
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/ReleaseMutex/test3/ReleaseMutex.c104
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test1/test1.c106
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test2/test2.c91
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test2/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test3/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test3/test3.c88
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test3/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test4/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/ResetEvent/test4/test4.c163
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/ResumeThread/test1/test1.c142
-rw-r--r--src/pal/tests/palsuite/threading/ResumeThread/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/CMakeLists.txt10
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test1/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test1/test1.c86
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test10/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test10/test10.c115
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test10/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test4/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test4/test4.c89
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test4/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test5/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test5/test5.c264
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test5/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test6/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test6/test6.c172
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test6/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test7/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test7/test7.c194
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test7/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test8/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test8/test8.c192
-rw-r--r--src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test8/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/SetErrorMode/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/SetErrorMode/test1/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SetErrorMode/test1/test1.c51
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test1/test1.c101
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test2/test2.c127
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test2/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test3/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test3/test3.c87
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test3/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test4/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SetEvent/test4/test4.c163
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/Sleep/test1/Sleep.c85
-rw-r--r--src/pal/tests/palsuite/threading/Sleep/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/Sleep/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/Sleep/test2/sleep.c78
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SleepEx/test1/test1.c95
-rw-r--r--src/pal/tests/palsuite/threading/SleepEx/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/SleepEx/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SleepEx/test2/test2.c178
-rw-r--r--src/pal/tests/palsuite/threading/SleepEx/test2/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/CMakeLists.txt8
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test1/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test1/test1.c158
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test2/test2.c186
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test2/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test3/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test3/test3.c199
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test3/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test4/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test4/test4.c208
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test4/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test5/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test5/test5.c427
-rw-r--r--src/pal/tests/palsuite/threading/SuspendThread/test5/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/threading/SwitchToThread/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/SwitchToThread/test1/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/SwitchToThread/test1/test1.c98
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test1/TLS.c183
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test2/TLS.c67
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test2/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test3/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test3/TLS.c91
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test3/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test4/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test4/test4.c138
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test4/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test5/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test5/test5.c109
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test5/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/test.c191
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/TerminateProcess/test1/TerminateProcess.c42
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/ThreadPriority/test1/ThreadPriority.c84
-rw-r--r--src/pal/tests/palsuite/threading/ThreadPriority/test1/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjects/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/test1.c208
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjects/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjects/test2/test2.c140
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjects/test2/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/CMakeLists.txt9
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/test1.c212
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/test2.c193
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/test3.c107
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/test4.c102
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/CMakeLists.txt38
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/commonconsts.h43
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/helper.c123
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.c507
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/testinfo.dat18
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/CMakeLists.txt38
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/child6.c212
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/test6.c710
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/WFSOExMutexTest.c217
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/WFSOExSemaphoreTest.c188
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/WFSOExThreadTest.c208
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/WFSOMutexTest.c186
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/CMakeLists.txt38
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/ChildProcess.c51
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.c120
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOSemaphoreTest/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOSemaphoreTest/WFSOSemaphoreTest.c184
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/WFSOThreadTest.c180
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/test1/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/WaitForSingleObject/test1/test1.c129
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/YieldProcessor/test1/test1.c93
-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.txt20
-rw-r--r--src/pal/tests/palsuite/threading/releasesemaphore/test1/test.c69
-rw-r--r--src/pal/tests/palsuite/threading/releasesemaphore/test1/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/threading/setthreadcontext/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/threading/setthreadcontext/test1/CMakeLists.txt20
-rw-r--r--src/pal/tests/palsuite/threading/setthreadcontext/test1/test1.c282
-rw-r--r--src/pal/tests/palsuite/threading/setthreadcontext/test1/testinfo.dat15
433 files changed, 28445 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..bbac41b414
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CMakeLists.txt
@@ -0,0 +1,46 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+# TODO: make these tests compile if they are needed
+# add_subdirectory(SetConsoleCtrlHandler)
+
+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(OpenEventW)
+add_subdirectory(OpenProcess)
+add_subdirectory(QueueUserAPC)
+add_subdirectory(ReleaseMutex)
+add_subdirectory(releasesemaphore)
+add_subdirectory(ResetEvent)
+add_subdirectory(ResumeThread)
+add_subdirectory(SetErrorMode)
+add_subdirectory(SetEvent)
+add_subdirectory(setthreadcontext)
+add_subdirectory(Sleep)
+add_subdirectory(SleepEx)
+add_subdirectory(SuspendThread)
+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..49cbfebd0e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..8a90c76985
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test1/test1.c
@@ -0,0 +1,100 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ LPCTSTR lpName = "Event #1";
+
+ /* Call CreateEvent, and check to ensure the returned HANDLE is a
+ valid event HANDLE
+ */
+
+ HANDLE hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ lpName);
+
+ 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 %s "
+ "failed (%x)\n",lpName,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 %s "
+ "failed (%x)\n",lpName,GetLastError());
+ }
+ }
+ }
+ else
+ {
+ Trace("CreateEventTest:CreateEvent %s "
+ "failed (%x)\n",lpName,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..7917e61f5d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test1/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..a5719498f4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..1079459b8a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test2/test2.c
@@ -0,0 +1,89 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ LPCTSTR lpName = "Event #2";
+
+ /* Create an event with the Initial State set to FALSE */
+
+ HANDLE hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ lpName);
+
+ 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 %s "
+ "failed (%x)\n",lpName,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 %s failed "
+ " (%x)\n",lpName,GetLastError());
+ }
+ }
+ }
+ else
+ {
+ Trace("CloseEventTest:CreateEvent %s "
+ "failed (%x)\n",lpName,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..c0ad88150d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test2/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..4437a9d4ba
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..69854e5292
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test3/test3.c
@@ -0,0 +1,220 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..687bad64cf
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventA/test3/testinfo.dat
@@ -0,0 +1,16 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..b1e9b8cb3f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..feee22c401
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test1/test1.c
@@ -0,0 +1,99 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ WCHAR TheName[] = {'E','v','e','n','t','\0'};
+ LPWSTR lpName = TheName;
+
+ /*
+ * Call CreateEvent, and check to ensure the returned HANDLE is a
+ * valid event HANDLE
+ */
+
+ HANDLE hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ lpName);
+
+ 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 %s "
+ "failed (%x)\n",lpName,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 %s "
+ "failed (%x)\n",lpName,GetLastError());
+ }
+ }
+ }
+ else
+ {
+ Trace("CreateEventTest:CreateEvent %s "
+ "failed (%x)\n",lpName,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..48b5403d09
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test1/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..13769327bf
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..c85c6545fb
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test2/test2.c
@@ -0,0 +1,91 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ WCHAR TheName[] = {'E','v','e','n','t','\0'};
+ LPWSTR lpName = TheName;
+
+
+ /* Create an event with the Initial State set to FALSE */
+
+ HANDLE hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ lpName);
+
+ 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 %s "
+ "failed (%x)\n",lpName,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 %s failed "
+ " (%x)\n",lpName,GetLastError());
+ }
+ }
+ }
+ else
+ {
+ Trace("CloseEventTest:CreateEvent %s "
+ "failed (%x)\n",lpName,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..b241b76f72
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test2/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..25bdd4f85a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..386e44e6f7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test3/test3.c
@@ -0,0 +1,234 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..a0c1dce1f8
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateEventW/test3/testinfo.dat
@@ -0,0 +1,16 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..84937e1a5e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..f6ffde8227
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/CreateMutexA.c
@@ -0,0 +1,346 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..a9073214b0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test1/testinfo.dat
@@ -0,0 +1,33 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..199462b901
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..3f13abfd39
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/CreateMutexA.c
@@ -0,0 +1,332 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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_PATH 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_PATH
+ */
+
+ szMaxPath = (char *)malloc(MAX_PATH+2);
+ memset(szMaxPath, 'A', MAX_PATH-60);
+ szMaxPath[MAX_PATH-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..067962c308
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexA_ReleaseMutex/test2/testinfo.dat
@@ -0,0 +1,24 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..e68e05bc6a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..6ef73c9a00
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CreateMutexW.c
@@ -0,0 +1,346 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..66d07cf41a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/testinfo.dat
@@ -0,0 +1,33 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..3e6f3d8999
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..7e4e3daa3c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CreateMutexW.c
@@ -0,0 +1,341 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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_PATH 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_PATH
+ */
+
+ szMaxPath = (char *)malloc(MAX_PATH+2);
+ memset(szMaxPath, 'A', MAX_PATH-60);
+ szMaxPath[MAX_PATH-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..3397186c42
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/testinfo.dat
@@ -0,0 +1,24 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..005d402199
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test1/CMakeLists.txt
@@ -0,0 +1,38 @@
+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
+ rt
+ 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
+ rt
+ 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..681d451451
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test1/childProcess.c
@@ -0,0 +1,132 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..ceddd4328a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test1/parentProcess.c
@@ -0,0 +1,202 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..df3dc8f903
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test1/testinfo.dat
@@ -0,0 +1,17 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..7b2558a55c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/CMakeLists.txt
@@ -0,0 +1,38 @@
+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
+ rt
+ 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
+ rt
+ 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..61c37fd960
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/childprocess.c
@@ -0,0 +1,70 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..5b5a717658
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/parentprocess.c
@@ -0,0 +1,244 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..ed93dadacd
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/test2.h
@@ -0,0 +1,73 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..093cee1e7a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/testinfo.dat
@@ -0,0 +1,20 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..06c456527b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test1/CMakeLists.txt
@@ -0,0 +1,38 @@
+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
+ rt
+ 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
+ rt
+ 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..91dd448fd2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test1/childProcess.c
@@ -0,0 +1,151 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..89473d0350
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test1/parentProcess.c
@@ -0,0 +1,211 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..3b9f7f83e7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test1/testinfo.dat
@@ -0,0 +1,17 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..7dbf6620a9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test2/CMakeLists.txt
@@ -0,0 +1,38 @@
+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
+ rt
+ 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
+ rt
+ 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..0d101b90d7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test2/childprocess.c
@@ -0,0 +1,79 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..33956d9092
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.c
@@ -0,0 +1,246 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..b0b3b58299
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test2/test2.h
@@ -0,0 +1,32 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..9f7030d393
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessW/test2/testinfo.dat
@@ -0,0 +1,20 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..97f13fa87e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..dc39e29556
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/CreateSemaphore.c
@@ -0,0 +1,323 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..484f020f36
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test1/testinfo.dat
@@ -0,0 +1,18 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..adc74576df
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..5c61b12a09
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/CreateSemaphore.c
@@ -0,0 +1,314 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..76706307c4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test2/testinfo.dat
@@ -0,0 +1,18 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..3e9f07f831
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..8e4ba450ea
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/createsemaphore.c
@@ -0,0 +1,191 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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}
+};
+
+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;
+ }
+ }
+ /* incriment semaphore count to lMaximumCount */
+ for (j = testCases[i].lInitialCount; j <= 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("");
+ }
+ }
+ }
+ /* 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..b41bd5cfd0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreA_ReleaseSemaphore/test3/testinfo.dat
@@ -0,0 +1,20 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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. \ No newline at end of file
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..7a64b17c78
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..3469ec8198
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.c
@@ -0,0 +1,324 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..335a700eb7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/testinfo.dat
@@ -0,0 +1,18 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..aead62b7a2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..ffad5e07de
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CreateSemaphore.c
@@ -0,0 +1,315 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..8a29b4f34b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/testinfo.dat
@@ -0,0 +1,18 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..b73e78b2d5
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..25d173f781
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/createsemaphore.c
@@ -0,0 +1,192 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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}
+};
+
+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;
+ }
+ }
+ /* incriment semaphore count to lMaximumCount */
+ for (j = testCases[i].lInitialCount; j <= 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("");
+ }
+ }
+ }
+ /* 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..2d8f74c69b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test3/testinfo.dat
@@ -0,0 +1,20 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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. \ No newline at end of file
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..194a4a4967
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..e6b4a21902
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test1/test1.c
@@ -0,0 +1,120 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..1c1b479b71
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test1/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..aa18f557c4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..3c1439c888
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test2/test2.c
@@ -0,0 +1,185 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..1dc0bff713
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test2/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..f7ea1d185e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..8ce0faed1c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test3/test3.c
@@ -0,0 +1,102 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..47ca6d57fe
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateThread/test3/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..863e3aea94
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..3771e98ee9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/InitializeCriticalSection.c
@@ -0,0 +1,236 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..8fccf19306
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test1/testinfo.dat
@@ -0,0 +1,21 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..af4c72d1af
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..9d881e0e7b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/test2.c
@@ -0,0 +1,227 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..8e05e2f52e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/testinfo.dat
@@ -0,0 +1,18 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..e469af37a9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..5337bb323e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/test3.c
@@ -0,0 +1,377 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..75295f3ad5
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test3/testinfo.dat
@@ -0,0 +1,29 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..ee4d87947b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..943222681b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/test4.c
@@ -0,0 +1,242 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..ad3031a887
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test4/testinfo.dat
@@ -0,0 +1,17 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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? \ No newline at end of file
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..74f9a6decb
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..131f2852a7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/test5.c
@@ -0,0 +1,188 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..8fa4280f4b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test5/testinfo.dat
@@ -0,0 +1,12 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..6d10df824e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..a3af8e3e0b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/test6.c
@@ -0,0 +1,191 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..b8c3e48eef
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test6/testinfo.dat
@@ -0,0 +1,12 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..3d03b92ed0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..882a4d9046
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/test7.c
@@ -0,0 +1,189 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..c779bfe070
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test7/testinfo.dat
@@ -0,0 +1,12 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..edec8d3057
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..a3959a3b55
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test8/test8.c
@@ -0,0 +1,218 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..15d085caa2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/test1.c
@@ -0,0 +1,146 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..1c0597411f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..be96027aa2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test1/testlib.c
@@ -0,0 +1,48 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..e2b7954188
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/dllmain1.c
@@ -0,0 +1,67 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..cb6d56ca03
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/dllmain2.c
@@ -0,0 +1,67 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..90e7b51e8e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/test2.c
@@ -0,0 +1,238 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..f223b6b177
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DisableThreadLibraryCalls/test2/testinfo.dat
@@ -0,0 +1,18 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..8e01a69108
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..81a34224dd
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test1/test1.c
@@ -0,0 +1,153 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..d8db24d46a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test1/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..96bdd38238
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test10/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..d8841c1a3b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test10/test10.c
@@ -0,0 +1,240 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..4ffbbbed6d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test10/testinfo.dat
@@ -0,0 +1,17 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..ddef2e4237
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/CMakeLists.txt
@@ -0,0 +1,38 @@
+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
+ rt
+ 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
+ rt
+ 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..16d52ea8ac
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/childprocess.c
@@ -0,0 +1,75 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..9d0d5f0616
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/myexitcode.h
@@ -0,0 +1,14 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..af58c8faf4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/test11.c
@@ -0,0 +1,365 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..aee2e237b6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test11/testinfo.dat
@@ -0,0 +1,19 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..bfc17916ff
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test12/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..7998724e13
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test12/test12.c
@@ -0,0 +1,130 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..df371b1eea
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test12/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..c4926c9f1a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..2099042235
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test2/test2.c
@@ -0,0 +1,97 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..0a44525998
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test2/testinfo.dat
@@ -0,0 +1,17 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..5fc70069d6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..cdc7a3760c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test3/test3.c
@@ -0,0 +1,124 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..75b941c07c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test3/testinfo.dat
@@ -0,0 +1,17 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..7910030e1f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test4/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..d0a27e895f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test4/test4.c
@@ -0,0 +1,240 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..9308b1e771
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test4/testinfo.dat
@@ -0,0 +1,19 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..43f30167d3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test5/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..195d6456e4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test5/test5.c
@@ -0,0 +1,146 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..3413703c62
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test5/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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. \ No newline at end of file
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..4ae0b46673
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test6/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..92abf7792c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test6/test6.c
@@ -0,0 +1,147 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..d1ea280ff5
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test6/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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. \ No newline at end of file
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..809ed5518c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test7/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..b0d059953e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test7/test7.c
@@ -0,0 +1,150 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..442c6f9b92
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test7/testinfo.dat
@@ -0,0 +1,16 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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. \ No newline at end of file
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..c9ae96b801
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test8/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..94b6a9c5c2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test8/test8.c
@@ -0,0 +1,155 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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);
+ }
+
+ /* 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);
+ 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..409a223376
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test8/testinfo.dat
@@ -0,0 +1,16 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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. \ No newline at end of file
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..3b8fdf2761
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test9/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..569888c76b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test9/test9.c
@@ -0,0 +1,128 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..2b12c6b83f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/DuplicateHandle/test9/testinfo.dat
@@ -0,0 +1,17 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..11da23e792
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..0832061566
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test1/ExitProcess.c
@@ -0,0 +1,33 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..3c330cdfbc
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test1/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..b4fc001ffc
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..adc703622f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test2/test2.c
@@ -0,0 +1,31 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..f0fb1a6432
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test2/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..4f2b34f22c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..90f689089f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test3/test3.c
@@ -0,0 +1,41 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..522325f96c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitProcess/test3/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..8b2e1eaec6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..c88066a735
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test1/test1.c
@@ -0,0 +1,115 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..9829a05993
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test1/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..91104df98c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test2/CMakeLists.txt
@@ -0,0 +1,38 @@
+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
+ rt
+ 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
+ rt
+ 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..30ec8557c6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test2/childprocess.c
@@ -0,0 +1,42 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..e437fa7679
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test2/myexitcode.h
@@ -0,0 +1,15 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..4dd4d7d679
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test2/test2.c
@@ -0,0 +1,169 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..b69b6ff8b6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test2/testinfo.dat
@@ -0,0 +1,19 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..59c51e4172
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test3/dllmain.c
@@ -0,0 +1,66 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..b43eaccc7e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test3/test3.c
@@ -0,0 +1,163 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..05b59c126c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ExitThread/test3/testinfo.dat
@@ -0,0 +1,16 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..d1e71317ec
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcess/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..8393967364
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcess/test1/process.c
@@ -0,0 +1,41 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..1254a5eade
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcess/test1/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..470b7842d0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..3d727b74cb
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/processId.c
@@ -0,0 +1,42 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..f5a43ccad0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentProcessId/test1/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..d9b42cb0ca
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThread/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..3ac0e7add3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThread/test1/testinfo.dat
@@ -0,0 +1,12 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..3f1d69022e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThread/test1/thread.c
@@ -0,0 +1,87 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 );
+ }
+
+ 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");
+ }
+ }
+
+ 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..458decc498
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThread/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..ac782c28d2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThread/test2/test2.c
@@ -0,0 +1,142 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 );
+ }
+
+
+ /* 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 );
+ }
+
+
+
+ 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..2d1bad2a9f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThread/test2/testinfo.dat
@@ -0,0 +1,18 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..ec59b1441e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..984bcd1871
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..fcdf1858ab
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetCurrentThreadId/test1/threadId.c
@@ -0,0 +1,83 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..b635e7e210
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/CMakeLists.txt
@@ -0,0 +1,38 @@
+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
+ rt
+ 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
+ rt
+ 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..9790ddaa7e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/childProcess.c
@@ -0,0 +1,32 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..81096a9f38
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/myexitcode.h
@@ -0,0 +1,15 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..8a05bdb0dc
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/test1.c
@@ -0,0 +1,164 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..dcb86a89a8
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetExitCodeProcess/test1/testinfo.dat
@@ -0,0 +1,16 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..a4d8aed7e9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetProcessTimes/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..9c1c448163
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetProcessTimes/test2/test2.c
@@ -0,0 +1,118 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 = 0;
+
+ 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++ )
+ {
+ total = j * i;
+ for( k=0; k<1000; k++ )
+ {
+ total += k + i;
+ }
+ }
+ }
+
+ /* 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..7983220916
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/GetProcessTimes/test2/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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/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..50cddffa98
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..36dea90eaa
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test1/test1.c
@@ -0,0 +1,135 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..29e65dd30d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test1/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..2f598d0e5b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..34fcf58eb6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test2/test2.c
@@ -0,0 +1,195 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..c5700185fb
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test2/testinfo.dat
@@ -0,0 +1,17 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..4ce74dab33
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test3/CMakeLists.txt
@@ -0,0 +1,38 @@
+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
+ rt
+ 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
+ rt
+ 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..b6e2ac0694
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test3/childprocess.c
@@ -0,0 +1,82 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..2176044c5e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test3/test3.c
@@ -0,0 +1,188 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..f510d65386
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test3/testinfo.dat
@@ -0,0 +1,21 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..cedb73d3ba
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test4/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..5b94addcda
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test4/test4.c
@@ -0,0 +1,113 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..7547722885
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test4/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..e69a9b0782
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test5/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..571e79ba9b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test5/test5.c
@@ -0,0 +1,198 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..0649ac678b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenEventW/test5/testinfo.dat
@@ -0,0 +1,19 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..431c8152bb
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenProcess/test1/CMakeLists.txt
@@ -0,0 +1,38 @@
+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
+ rt
+ 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
+ rt
+ 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..feffdd6f89
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.c
@@ -0,0 +1,76 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..b22380f01e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenProcess/test1/myexitcode.h
@@ -0,0 +1,15 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..df1e341976
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenProcess/test1/test1.c
@@ -0,0 +1,283 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..1dd4e2e98e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/OpenProcess/test1/testinfo.dat
@@ -0,0 +1,19 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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. \ No newline at end of file
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..b799dff6f3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..3b1df9303b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test1/test1.c
@@ -0,0 +1,314 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..d29989a3a1
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test1/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..1023c43bd4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..cd41399e9b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test2/test2.c
@@ -0,0 +1,225 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..cd95f27792
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test2/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..ab6398a8d3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..0d44957a07
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test3/test3.c
@@ -0,0 +1,34 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..74d3fd7014
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test3/testinfo.dat
@@ -0,0 +1,12 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..5f3e7096e4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test4/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..2efee6b384
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test4/test4.c
@@ -0,0 +1,73 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..ccd45ec100
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test4/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..f45a7a5b9b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test5/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..91e97237ea
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test5/test5.c
@@ -0,0 +1,201 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..612dfd2fd0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test5/testinfo.dat
@@ -0,0 +1,20 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..016a0b74e7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test6/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..4a8df5fe0d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test6/test6.c
@@ -0,0 +1,130 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..c8ee534128
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test6/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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. \ No newline at end of file
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..a621003019
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test7/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..30a6c8a1a2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test7/test7.c
@@ -0,0 +1,254 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..86ad1129c9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/QueueUserAPC/test7/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..1f0ae71c10
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ReleaseMutex/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..e3ec93d6fe
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ReleaseMutex/test3/ReleaseMutex.c
@@ -0,0 +1,104 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..d1cbe997ea
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ReleaseMutex/test3/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..e197980251
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..c0122e5946
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test1/test1.c
@@ -0,0 +1,106 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ LPCTSTR lpName = "Event #4";
+
+ /* Create an Event, ensure it is valid */
+ HANDLE hEvent = CreateEvent( lpEventAttributes,
+ bManualReset, bInitialState, lpName);
+
+ 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 %s "
+ "failed (%x)\n",lpName,GetLastError());
+ }
+ else
+ {
+ /* Call ResetEvent, which will reset the signal */
+ bRet = ResetEvent(hEvent);
+
+ if (!bRet)
+ {
+ Fail("ResetEventTest:ResetEvent %s failed "
+ "(%x)\n",lpName,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",lpName,GetLastError());
+ }
+ else
+ {
+ bRet = CloseHandle(hEvent);
+
+ if (!bRet)
+ {
+ Fail("ResetEventTest:CloseHandle %s failed"
+ "(%x)\n",lpName,GetLastError());
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Fail("ResetEventTest:CreateEvent %s failed "
+ "(%x)\n",lpName,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..bf8f5906b1
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test1/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..f294b83ef7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..f30c24ce70
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test2/test2.c
@@ -0,0 +1,91 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ LPCTSTR lpName = "HolyMoly";
+
+
+ /* 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,
+ lpName );
+
+ 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..83bb72010c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test2/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..fe5ac812e4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..8b83faa6df
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test3/test3.c
@@ -0,0 +1,88 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ LPCTSTR lpName = "WooBaby";
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* create an event which we can use with ResetEvent */
+ hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ lpName );
+
+ 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..32dcbe5706
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test3/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..c2dd95f5fa
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test4/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..2b6c8bf55a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test4/test4.c
@@ -0,0 +1,163 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ LPCTSTR lpName = "MamaMia";
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* create an event which we can use with ResetEvent */
+ hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ lpName );
+
+ 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..7644627bc2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResetEvent/test4/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..0535a853ae
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResumeThread/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..a22d08c1c8
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResumeThread/test1/test1.c
@@ -0,0 +1,142 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 occured 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..8dbee89f84
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ResumeThread/test1/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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/SetConsoleCtrlHandler/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/CMakeLists.txt
new file mode 100644
index 0000000000..75b3393664
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test10)
+add_subdirectory(test4)
+add_subdirectory(test5)
+add_subdirectory(test6)
+add_subdirectory(test7)
+add_subdirectory(test8)
+
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test1/CMakeLists.txt
new file mode 100644
index 0000000000..705cc0cc38
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_setconsolectrlhandler_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_setconsolectrlhandler_test1 CoreClrPal)
+
+target_link_libraries(paltest_setconsolectrlhandler_test1
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test1/test1.c b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test1/test1.c
new file mode 100644
index 0000000000..08ce81ca45
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test1/test1.c
@@ -0,0 +1,86 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*=====================================================================
+**
+** Source: test1.c
+**
+** Purpose: Set the CtrlHandler to flip a flag
+** when CTRL_C_EVENT is signalled. If the flag is flipped, the test
+** succeeds. Otherwise, it will fail. Finally, check to see that
+** removing the handler works alright.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+int Flag = 1;
+
+BOOL CtrlHandler(DWORD CtrlType)
+{
+ if(CtrlType == CTRL_C_EVENT)
+ {
+ Flag = 0;
+ return 1;
+ }
+
+ Trace("ERROR: The CtrlHandler was called, but the event was not a "
+ "CTRL_C_EVENT. This is considered failure.\n");
+
+ return 0;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+
+ if (0 != PAL_Initialize(argc,argv))
+ {
+ return FAIL;
+ }
+
+ /* Call the function to set the CtrlHandler */
+ if( SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE) == 0 )
+ {
+ Fail("ERROR: SetConsoleCtrlHandler returned zero, indicating failure."
+ " GetLastError() returned %d.\n",GetLastError());
+ }
+
+ /* Generate a CTRL_C event */
+ if(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0) == 0)
+ {
+ Fail("ERROR: GenerateConsoleCtrlEvent failed, returning zero. "
+ "GetLastError returned %d.\n",GetLastError());
+ }
+
+
+ /* Sleep for a couple seconds to ensure that the event has time to
+ complete.
+ */
+ Sleep(2000);
+
+ /* The event handling function should set Flag = 0 if it worked
+ properly. Otherwise this test fails.
+ */
+ if(Flag)
+ {
+ Fail("ERROR: When CTRL-C was generated it wasn't handled by the "
+ "Ctrl Handler which was defined.\n");
+ }
+
+
+ /* Call the function to remove the CtrlHandler */
+
+ if( SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, FALSE) == 0)
+ {
+ Fail("ERROR: SetConsoleCtrlHandler returned zero, indicating failure "
+ "when attempting to remove a handler. "
+ "GetLastError() returned %d.\n",GetLastError());
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test1/testinfo.dat b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test1/testinfo.dat
new file mode 100644
index 0000000000..04088663ed
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test1/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = SetConsoleCtrlHandler
+Name = See that CTRL-C behaviour can be set using this function.
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Set the CtrlHandler to flip a flag
+= when CTRL_C_EVENT is signalled. If the flag is flipped, the test
+= succeeds. Otherwise, it will fail.
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test10/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test10/CMakeLists.txt
new file mode 100644
index 0000000000..4c79c62a26
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test10/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test10.c
+)
+
+add_executable(paltest_setconsolectrlhandler_test10
+ ${SOURCES}
+)
+
+add_dependencies(paltest_setconsolectrlhandler_test10 CoreClrPal)
+
+target_link_libraries(paltest_setconsolectrlhandler_test10
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test10/test10.c b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test10/test10.c
new file mode 100644
index 0000000000..67c9f56368
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test10/test10.c
@@ -0,0 +1,115 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*=============================================================================
+**
+** Source: test10.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** GenerateConsoleCtrlEvent
+**
+** Purpose:
+**
+** Test to ensure proper operation of the SetConsoleCtrlHandler()
+** API by checking whether a console control handler function is
+** actually removed by the API when it returns success for that
+** operation.
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+
+/* global test value */
+static BOOL g_bFlag = FALSE;
+
+
+
+/* handler function */
+static BOOL PALAPI CtrlHandler( DWORD CtrlType )
+{
+ if( CtrlType == CTRL_BREAK_EVENT )
+ {
+ g_bFlag = TRUE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+
+
+/* main entry point function */
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ BOOL ret = PASS;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* set the console control handler function */
+ if( ! SetConsoleCtrlHandler( CtrlHandler, TRUE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to add "
+ "CtrlHandler\n",
+ GetLastError() );
+ Fail( "Test failed\n" );
+ }
+
+
+ /* test that the right control handler functions are set */
+ if( ! GenerateConsoleCtrlEvent( CTRL_BREAK_EVENT, 0 ) )
+ {
+ Trace( "ERROR:%lu:GenerateConsoleCtrlEvent() failed\n",
+ GetLastError() );
+ ret = FAIL;
+ goto done;
+ }
+
+ /* give the handlers a chance to execute */
+ Sleep( 2000 );
+
+ /* check the results */
+ if( ! g_bFlag )
+ {
+ Trace( "ERROR:CtrlHandler() was not called but should have been\n" );
+ ret = FAIL;
+ }
+
+
+
+done:
+ /* unset the control handle that was set */
+ if( ! SetConsoleCtrlHandler( CtrlHandler, FALSE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to "
+ "remove CtrlHandler\n",
+ GetLastError() );
+ Fail( "Test failed\n" );
+ }
+
+
+ /* PAL termination */
+ PAL_TerminateEx(ret);
+
+
+ /* return our result */
+ return ret;
+}
+
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test10/testinfo.dat b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test10/testinfo.dat
new file mode 100644
index 0000000000..e1eafb6e38
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test10/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = SetConsoleCtrlHandler
+Name = Positive test for SetConsoleCtrlHandler
+TYPE = DEFAULT
+EXE1 = test10
+Description
+= Test to ensure proper operation of the SetConsoleCtrlHandler()
+= API by checking whether a console control handler function can
+= be set and called for a CTRL_BREAK_EVENT.
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test4/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test4/CMakeLists.txt
new file mode 100644
index 0000000000..b648393cac
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test4/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test4.c
+)
+
+add_executable(paltest_setconsolectrlhandler_test4
+ ${SOURCES}
+)
+
+add_dependencies(paltest_setconsolectrlhandler_test4 CoreClrPal)
+
+target_link_libraries(paltest_setconsolectrlhandler_test4
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test4/test4.c b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test4/test4.c
new file mode 100644
index 0000000000..6368bbc659
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test4/test4.c
@@ -0,0 +1,89 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*=====================================================================
+**
+** Source: test1.c
+**
+** Purpose: This is a MANUAL test. This is also a NEGATIVE test. We want
+** this function to return 1 (FAIL). This test checks that we can set a
+** handler for CTRL_C and then remove that handler.
+**
+**
+**===================================================================*/
+
+/* Note: If an error occurs in this test, we have to return 0.
+ The only time this test passes, is when CTRL-C is signaled, and the
+ program exits with a code of 1.
+*/
+
+#include <palsuite.h>
+
+int Flag = 1;
+
+BOOL CtrlHandler(DWORD CtrlType)
+{
+ if(CtrlType == CTRL_C_EVENT)
+ {
+ Flag = 0;
+ return 1;
+ }
+
+ Trace("ERROR: The CtrlHandler was called, but the event was not a "
+ "CTRL_C_EVENT. This is considered failure.");
+
+ return 0;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ int counter = 0;
+
+ if (0 != PAL_Initialize(argc,argv))
+ {
+ return FAIL;
+ }
+
+ /* Call the function to set the CtrlHandler */
+ if( SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE) == 0 )
+ {
+ Trace("ERROR: SetConsoleCtrlHandler returned zero, indicating failure."
+ " GetLastError() returned %d.\n",GetLastError());
+ return 0;
+ }
+
+ /* Call the function to remove the CtrlHandler */
+
+ if( SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, FALSE) == 0)
+ {
+ Trace("ERROR: SetConsoleCtrlHandler returned zero, indicating failure "
+ "when attempting to remove a handler. "
+ "GetLastError() returned %d.\n",GetLastError());
+ return 0;
+ }
+
+ /* Prompt for the tester to press CTRL-C. They have a limited amount
+ of time to type this. (Incase the CTRL-C handler isn't working
+ properly)
+ */
+ printf("Please press CTRL-C now. This is timed. If CTRL-C is not "
+ "pressed by the time I count to 1000000000 then the test "
+ "will automatically fail.\n");
+
+ while(Flag)
+ {
+ counter++;
+ if(counter == 1000000000)
+ {
+ Trace("ERROR: The time ran out. CTRL-C was never pressed.");
+ return 0;
+ }
+ }
+
+ Trace("MANUAL: The test has failed. Now calling PAL_Terminate().\n");
+ PAL_Terminate();
+ return 0;
+}
+
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test4/testinfo.dat b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test4/testinfo.dat
new file mode 100644
index 0000000000..7169ee3556
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test4/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = SetConsoleCtrlHandler
+Name = Manual/Negative Test to see that CTRL-C behaviour can be set/unset.
+TYPE = DEFAULT
+EXE1 = test4
+Description
+= This is a MANUAL test. This is also a NEGATIVE test. We want
+= this function to return 1 (FAIL). This test checks that we can set a
+= handler for CTRL_C and then remove that handler.
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test5/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test5/CMakeLists.txt
new file mode 100644
index 0000000000..6fc7b02932
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test5/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test5.c
+)
+
+add_executable(paltest_setconsolectrlhandler_test5
+ ${SOURCES}
+)
+
+add_dependencies(paltest_setconsolectrlhandler_test5 CoreClrPal)
+
+target_link_libraries(paltest_setconsolectrlhandler_test5
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test5/test5.c b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test5/test5.c
new file mode 100644
index 0000000000..4355cdb29d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test5/test5.c
@@ -0,0 +1,264 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*=============================================================================
+**
+** Source: test5.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** GenerateConsoleCtrlEvent
+**
+** Purpose:
+**
+** Test to ensure proper operation of the SetConsoleCtrlHandler()
+** API by using it to chain multiple handler functions together.
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+/* helper test structure */
+struct handlerData
+{
+ PHANDLER_ROUTINE func; /* handler routine */
+ BOOL set; /* whether this handler was set */
+ BOOL stop; /* whether handling should stop with this */
+ BOOL result; /* flag tracking whether handler was called */
+};
+
+
+/* we'll work with three handler functions */
+#define NUM_HANDLERS 3
+
+/* handler function prototypes */
+static BOOL PALAPI CtrlHandler1( DWORD CtrlType );
+static BOOL PALAPI CtrlHandler2( DWORD CtrlType );
+static BOOL PALAPI CtrlHandler3( DWORD CtrlType );
+
+
+/* array of control handler data */
+static struct handlerData g_handlers[] =
+{
+ { CtrlHandler1, FALSE, TRUE, FALSE },
+ { CtrlHandler2, FALSE, FALSE, FALSE },
+ { CtrlHandler3, FALSE, FALSE, FALSE }
+};
+
+
+/* global flag to track whether the handlers are called in the wrong order */
+static BOOL g_bWrongOrder = FALSE;
+
+
+/* first handler function */
+static BOOL PALAPI CtrlHandler1( DWORD CtrlType )
+{
+ if( CtrlType == CTRL_C_EVENT )
+ {
+ /* set our handler flag to true */
+ g_handlers[0].result = TRUE;
+
+ /* check for right handler order */
+ if( (!g_handlers[1].result) || (!g_handlers[2].result) )
+ {
+ g_bWrongOrder = TRUE;
+ }
+ return g_handlers[0].stop;
+ }
+
+ return FALSE;
+}
+
+
+/* second handler function */
+static BOOL PALAPI CtrlHandler2( DWORD CtrlType )
+{
+ if( CtrlType == CTRL_C_EVENT )
+ {
+ /* set our handler flag to true */
+ g_handlers[1].result = TRUE;
+
+ /* check for right handler order */
+ if( g_handlers[0].result || (!g_handlers[2].result) )
+ {
+ g_bWrongOrder = TRUE;
+ }
+ return g_handlers[1].stop;
+ }
+
+ return FALSE;
+}
+
+
+/* third handler function */
+static BOOL PALAPI CtrlHandler3( DWORD CtrlType )
+{
+ if( CtrlType == CTRL_C_EVENT )
+ {
+ /* set our handler flag to true */
+ g_handlers[2].result = TRUE;
+
+ /* check for right handler order */
+ if( g_handlers[0].result || g_handlers[1].result )
+ {
+ g_bWrongOrder = TRUE;
+ }
+ return g_handlers[2].stop;
+ }
+
+ return FALSE;
+}
+
+
+
+
+
+
+/* main entry point function */
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ int i;
+ int finalHandler;
+ BOOL ret = PASS;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* chain together three simple control handlers */
+ for( i=0; i<NUM_HANDLERS; i++ )
+ {
+ if( SetConsoleCtrlHandler( g_handlers[i].func, TRUE ) )
+ {
+ g_handlers[i].set = TRUE;
+ }
+ else
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to add "
+ "handler #%d\n",
+ GetLastError(),
+ (i+1) );
+ goto done;
+ }
+ }
+
+
+ /* first test -- verify that all three handlers are called, in the */
+ /* correct sequence (CtrlHandler3, CtrlHandler2, CtrlHandler1) */
+ if( ! GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0 ) )
+ {
+ Trace( "ERROR:%lu:GenerateConsoleCtrlEvent() failed\n",
+ GetLastError() );
+ ret = FAIL;
+ goto done;
+ }
+
+ /* give the handlers a chance to execute */
+ Sleep( 2000 );
+
+
+ /* check the results and reset all the handler-called flags */
+ for( i=0; i<NUM_HANDLERS; i++ )
+ {
+ if( ! g_handlers[i].result )
+ {
+ Trace( "FAIL:Handler #%d was not called\n", (i+1) );
+ ret = FAIL;
+ }
+ else
+ {
+ g_handlers[i].result = FALSE;
+ }
+ }
+
+ if( g_bWrongOrder )
+ {
+ Trace( "FAIL:Handlers were called in the wrong order\n" );
+ ret = FAIL;
+ }
+
+ /* we're done if we got an error result */
+ if( ! ret )
+ {
+ goto done;
+ }
+
+
+ /* same test, only this time we want to stop at the second handler */
+ finalHandler = 1;
+ g_handlers[ finalHandler ].stop = TRUE;
+ if( ! GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0 ) )
+ {
+ Trace( "ERROR:%lu:GenerateConsoleCtrlEvent() failed\n",
+ GetLastError() );
+ ret = FAIL;
+ goto done;
+ }
+
+ /* give the handlers a chance to execute */
+ Sleep( 2000 );
+
+
+ /* check the results */
+ for( i=0; i<NUM_HANDLERS; i++ )
+ {
+ if( i < finalHandler )
+ {
+ /* should not have been called */
+ if( g_handlers[i].result )
+ {
+ Trace( "FAIL:Handler #%d was called but shouldn't "
+ "have been\n",
+ (i+1) );
+ ret = FAIL;
+ }
+ }
+ else
+ {
+ /* should have been called */
+ if( ! g_handlers[i].result )
+ {
+ Trace( "FAIL:Handler #%d was not called\n", (i+1) );
+ ret = FAIL;
+ }
+ }
+ }
+
+
+
+done:
+ /* unset any handlers that were set */
+ for( i=0; i<NUM_HANDLERS; i++ )
+ {
+ if( g_handlers[i].set )
+ {
+ if( ! SetConsoleCtrlHandler( g_handlers[i].func, FALSE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to add "
+ "handler #%d\n",
+ GetLastError(),
+ (i+1) );
+ }
+ }
+ }
+
+
+ /* PAL termination */
+ PAL_TerminateEx(ret);
+
+
+ /* return our result */
+ return ret;
+}
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test5/testinfo.dat b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test5/testinfo.dat
new file mode 100644
index 0000000000..1737394b55
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test5/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = SetConsoleCtrlHandler
+Name = Positive test for SetConsoleCtrlHandler
+TYPE = DEFAULT
+EXE1 = test5
+Description
+= Test to ensure proper operation of the SetConsoleCtrlHandler()
+= API by using it to chain multiple handler functions together.
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test6/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test6/CMakeLists.txt
new file mode 100644
index 0000000000..4318d225a1
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test6/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test6.c
+)
+
+add_executable(paltest_setconsolectrlhandler_test6
+ ${SOURCES}
+)
+
+add_dependencies(paltest_setconsolectrlhandler_test6 CoreClrPal)
+
+target_link_libraries(paltest_setconsolectrlhandler_test6
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test6/test6.c b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test6/test6.c
new file mode 100644
index 0000000000..ca232da772
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test6/test6.c
@@ -0,0 +1,172 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*=============================================================================
+**
+** Source: test6.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** GenerateConsoleCtrlEvent
+**
+** Purpose:
+**
+** Test to ensure proper operation of the SetConsoleCtrlHandler()
+** API by trying to remove a non-existent handler.
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+static BOOL g_bFlag1 = FALSE;
+static BOOL g_bFlag2 = FALSE;
+
+
+/* first handler function */
+static BOOL PALAPI CtrlHandler1( DWORD CtrlType )
+{
+ if( CtrlType == CTRL_C_EVENT )
+ {
+ g_bFlag1 = TRUE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/* second handler function */
+static BOOL PALAPI CtrlHandler2( DWORD CtrlType )
+{
+ if( CtrlType == CTRL_C_EVENT )
+ {
+ g_bFlag2 = TRUE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+
+
+/* main entry point function */
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ BOOL ret = PASS;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* set the console control handler function */
+ if( ! SetConsoleCtrlHandler( CtrlHandler1, TRUE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to add "
+ "CtrlHandler1\n",
+ GetLastError() );
+ Fail( "Test failed\n" );
+ }
+
+ /* test that the right control handler functions are set */
+ if( ! GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0 ) )
+ {
+ Trace( "ERROR:%lu:GenerateConsoleCtrlEvent() failed\n",
+ GetLastError() );
+ ret = FAIL;
+ goto done;
+ }
+
+ /* give the handlers a chance to execute */
+ Sleep( 2000 );
+
+ /* check the results */
+ if( g_bFlag2 )
+ {
+ Trace( "ERROR:CtrlHandler2() was inexplicably called\n" );
+ ret = FAIL;
+ goto done;
+ }
+
+ if( ! g_bFlag1 )
+ {
+ Trace( "ERROR:CtrlHandler1() was not called but should have been\n" );
+ ret = FAIL;
+ goto done;
+ }
+
+ /* reset our flags */
+ g_bFlag1 = FALSE;
+
+
+ /* try to unset CtrlHandler2, which isn't set in the first place */
+ if( SetConsoleCtrlHandler( CtrlHandler2, FALSE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:SetConsoleCtrlHandler() succeeded trying to "
+ "remove CtrlHandler2, which isn't set\n" );
+ goto done;
+ }
+
+
+ /* make sure that the existing control handler functions are still set */
+ if( ! GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0 ) )
+ {
+ Trace( "ERROR:%lu:GenerateConsoleCtrlEvent() failed\n",
+ GetLastError() );
+ ret = FAIL;
+ goto done;
+ }
+
+ /* give the handlers a chance to execute */
+ Sleep( 2000 );
+
+ /* check the results */
+ if( g_bFlag2 )
+ {
+ Trace( "ERROR:CtrlHandler2() was inexplicably called\n" );
+ ret = FAIL;
+ goto done;
+ }
+
+ if( ! g_bFlag1 )
+ {
+ Trace( "ERROR:CtrlHandler1() was not called but should have been\n" );
+ ret = FAIL;
+ goto done;
+ }
+
+
+
+done:
+ /* unset any handlers that were set */
+ if( ! SetConsoleCtrlHandler( CtrlHandler1, FALSE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to "
+ "remove CtrlHandler1\n",
+ GetLastError() );
+ Fail( "Test failed\n" );
+ }
+
+
+ /* PAL termination */
+ PAL_TerminateEx(ret);
+
+
+ /* return our result */
+ return ret;
+}
+
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test6/testinfo.dat b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test6/testinfo.dat
new file mode 100644
index 0000000000..2a320a7a16
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test6/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = SetConsoleCtrlHandler
+Name = Negative test for SetConsoleCtrlHandler
+TYPE = DEFAULT
+EXE1 = test6
+Description
+= Test to ensure proper operation of the SetConsoleCtrlHandler()
+= API by trying to remove a non-existent handler.
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test7/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test7/CMakeLists.txt
new file mode 100644
index 0000000000..8cc2e4a4a0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test7/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test7.c
+)
+
+add_executable(paltest_setconsolectrlhandler_test7
+ ${SOURCES}
+)
+
+add_dependencies(paltest_setconsolectrlhandler_test7 CoreClrPal)
+
+target_link_libraries(paltest_setconsolectrlhandler_test7
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test7/test7.c b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test7/test7.c
new file mode 100644
index 0000000000..eabfb19532
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test7/test7.c
@@ -0,0 +1,194 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*=============================================================================
+**
+** Source: test7.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** GenerateConsoleCtrlEvent
+**
+** Purpose:
+**
+** Test to ensure proper operation of the SetConsoleCtrlHandler()
+** API by checking whether a console control handler function is
+** actually removed by the API when it returns success for that
+** operation.
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+static BOOL g_bFlag1 = FALSE;
+static BOOL g_bFlag2 = FALSE;
+
+
+/* first handler function */
+static BOOL PALAPI CtrlHandler1( DWORD CtrlType )
+{
+ if( CtrlType == CTRL_C_EVENT )
+ {
+ g_bFlag1 = TRUE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/* second handler function */
+static BOOL PALAPI CtrlHandler2( DWORD CtrlType )
+{
+ if( CtrlType == CTRL_C_EVENT )
+ {
+ g_bFlag2 = TRUE;
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+
+
+
+
+/* main entry point function */
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ BOOL ret = PASS;
+ BOOL bSetHandler1 = FALSE;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* set the console control handler functions */
+ if( SetConsoleCtrlHandler( CtrlHandler1, TRUE ) )
+ {
+ bSetHandler1 = TRUE;
+ }
+ else
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to add "
+ "CtrlHandler1\n",
+ GetLastError() );
+ Fail( "Test failed\n" );
+ }
+
+ if( ! SetConsoleCtrlHandler( CtrlHandler2, TRUE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to add "
+ "CtrlHandler2\n",
+ GetLastError() );
+ Fail( "Test failed\n" );
+ }
+
+
+ /* test that the right control handler functions are set */
+ if( ! GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0 ) )
+ {
+ Trace( "ERROR:%lu:GenerateConsoleCtrlEvent() failed\n",
+ GetLastError() );
+ ret = FAIL;
+ goto done;
+ }
+
+ /* give the handlers a chance to execute */
+ Sleep( 2000 );
+
+ /* check the results */
+ if( ! g_bFlag1 )
+ {
+ Trace( "ERROR:CtrlHandler1() was not called but should have been\n" );
+ ret = FAIL;
+ goto done;
+ }
+ if( ! g_bFlag2 )
+ {
+ Trace( "ERROR:CtrlHandler2() was not called but should have been\n" );
+ ret = FAIL;
+ goto done;
+ }
+
+
+ /* reset our flags */
+ g_bFlag1 = FALSE;
+ g_bFlag2 = FALSE;
+
+
+ /* try to unset CtrlHandler2 */
+ if( ! SetConsoleCtrlHandler( CtrlHandler2, FALSE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to "
+ "remove CtrlHandler2\n",
+ GetLastError() );
+ goto done;
+ }
+
+
+ /* make sure that CtrlHandler1 is set and CtrlHandler2 isn't */
+ if( ! GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0 ) )
+ {
+ Trace( "ERROR:%lu:GenerateConsoleCtrlEvent() failed\n",
+ GetLastError() );
+ ret = FAIL;
+ goto done;
+ }
+
+ /* give the handlers a chance to execute */
+ Sleep( 2000 );
+
+ /* check the results */
+ if( g_bFlag2 )
+ {
+ Trace( "ERROR:CtrlHandler2() was called after it was unset\n" );
+ ret = FAIL;
+ goto done;
+ }
+
+ if( ! g_bFlag1 )
+ {
+ Trace( "ERROR:CtrlHandler1() was not called but should have been\n" );
+ ret = FAIL;
+ goto done;
+ }
+
+
+
+done:
+ /* unset CtrlHandler1 if it was set */
+ if( bSetHandler1 )
+ {
+ if( ! SetConsoleCtrlHandler( CtrlHandler1, FALSE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to "
+ "remove CtrlHandler1\n",
+ GetLastError() );
+ Fail( "Test failed\n" );
+ }
+ }
+
+
+ /* PAL termination */
+ PAL_TerminateEx(ret);
+
+
+ /* return our result */
+ return ret;
+}
+
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test7/testinfo.dat b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test7/testinfo.dat
new file mode 100644
index 0000000000..67bdb1c389
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test7/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = SetConsoleCtrlHandler
+Name = Positive test for SetConsoleCtrlHandler
+TYPE = DEFAULT
+EXE1 = test7
+Description
+= Test to ensure proper operation of the SetConsoleCtrlHandler()
+= API by checking whether a console control handler function is
+= actually removed by the API when it returns success for that
+= operation.
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test8/CMakeLists.txt b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test8/CMakeLists.txt
new file mode 100644
index 0000000000..abcf15f669
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test8/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test8.c
+)
+
+add_executable(paltest_setconsolectrlhandler_test8
+ ${SOURCES}
+)
+
+add_dependencies(paltest_setconsolectrlhandler_test8 CoreClrPal)
+
+target_link_libraries(paltest_setconsolectrlhandler_test8
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test8/test8.c b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test8/test8.c
new file mode 100644
index 0000000000..1c3ee2b71d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test8/test8.c
@@ -0,0 +1,192 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*=============================================================================
+**
+** Source: test8.c
+**
+** Dependencies: PAL_Initialize
+** PAL_Terminate
+** GenerateConsoleCtrlEvent
+**
+** Purpose:
+**
+** Test to ensure proper operation of the SetConsoleCtrlHandler()
+** API by attempting to add and remove the same handler multiple
+** times.
+**
+
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+
+
+/* the number of times to set the console control handler function */
+#define HANDLER_SET_COUNT 5
+
+
+
+/* the number of times the console control handler function's been called */
+static int g_count = 0;
+
+
+
+
+/* to avoid having the default control handler abort our process */
+static BOOL PALAPI FinalCtrlHandler( DWORD CtrlType )
+{
+ return (CtrlType == CTRL_C_EVENT);
+}
+
+
+/* test handler function */
+static BOOL PALAPI CtrlHandler1( DWORD CtrlType )
+{
+ if( CtrlType == CTRL_C_EVENT )
+ {
+ ++g_count;
+ }
+
+ return FALSE;
+}
+
+
+
+
+
+/* main entry point function */
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ int i;
+ BOOL ret = PASS;
+ BOOL bSetFinalHandler = FALSE;
+ int nSetCount = 0;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* set our final console control handler function */
+ if( SetConsoleCtrlHandler( FinalCtrlHandler, TRUE ) )
+ {
+ bSetFinalHandler = TRUE;
+ }
+ else
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to add "
+ "FinalCtrlHandler\n",
+ GetLastError() );
+ Fail( "Test failed\n" );
+ }
+
+ /* try to set our test handler multiple times */
+ for( i=0; i<HANDLER_SET_COUNT; i++ )
+ {
+ if( SetConsoleCtrlHandler( CtrlHandler1, TRUE ) )
+ {
+ nSetCount++;
+ }
+ else
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to add "
+ "CtrlHandler1 at attempt #%d\n",
+ GetLastError(),
+ i );
+ goto done;
+ }
+ }
+
+ /* loop here -- generate an event and verify that our handler */
+ /* was called the correct number of times, then unset it one */
+ /* time and repeat until it's completely unset. */
+ for( ; nSetCount>0; nSetCount-- )
+ {
+ /* test that the control handler functions are set */
+ if( ! GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0 ) )
+ {
+ Trace( "ERROR:%lu:GenerateConsoleCtrlEvent() failed\n",
+ GetLastError() );
+ ret = FAIL;
+ goto done;
+ }
+
+ /* give the handlers a chance to execute */
+ Sleep( 2000 );
+
+ /* check the results */
+ if( g_count != nSetCount )
+ {
+ Trace( "ERROR:CtrlHandler1() was not called %d times, "
+ "expected %d\n",
+ g_count,
+ nSetCount );
+ ret = FAIL;
+ goto done;
+ }
+
+ /* unset the control handler one time */
+ if( ! SetConsoleCtrlHandler( CtrlHandler1, FALSE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to "
+ "remove CtrlHandler instance #%d\n",
+ GetLastError(),
+ nSetCount );
+ Fail( "Test failed\n" );
+ }
+
+ /* reset our counter */
+ g_count = 0;
+ }
+
+
+
+done:
+ /* unset any lingering instances of our test control handler */
+ for( ; nSetCount>0; nSetCount-- )
+ {
+ /* unset the control handler one time */
+ if( ! SetConsoleCtrlHandler( CtrlHandler1, FALSE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to "
+ "remove CtrlHandler instance #%d\n",
+ GetLastError(),
+ nSetCount );
+ Fail( "Test failed\n" );
+ }
+ }
+
+
+ /* unset our final control handler if it was set */
+ if( bSetFinalHandler )
+ {
+ if( ! SetConsoleCtrlHandler( FinalCtrlHandler, FALSE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to "
+ "remove FinalCtrlHandler\n",
+ GetLastError() );
+ Fail( "Test failed\n" );
+ }
+ }
+
+
+ /* PAL termination */
+ PAL_TerminateEx(ret);
+
+
+ /* return our result */
+ return ret;
+}
diff --git a/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test8/testinfo.dat b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test8/testinfo.dat
new file mode 100644
index 0000000000..7aaf38e2e9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetConsoleCtrlHandler/test8/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = SetConsoleCtrlHandler
+Name = Positive test for SetConsoleCtrlHandler
+TYPE = DEFAULT
+EXE1 = test8
+Description
+= Test to ensure proper operation of the SetConsoleCtrlHandler()
+= API by attempting to add and remove the same handler multiple
+= times.
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..85bda9f011
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetErrorMode/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..8fdfb525f1
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetErrorMode/test1/test1.c
@@ -0,0 +1,51 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..f1b8ca0abc
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetErrorMode/test1/testinfo.dat
@@ -0,0 +1,16 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..37e1b4d387
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..9df5e0987c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test1/test1.c
@@ -0,0 +1,101 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ LPCTSTR lpName = "Event #3";
+
+ /* Create an event which we can use with SetEvent */
+ HANDLE hEvent = CreateEvent( lpEventAttributes,
+ bManualReset, bInitialState, lpName);
+
+ if (hEvent != INVALID_HANDLE_VALUE)
+ {
+ dwRet = WaitForSingleObject(hEvent,0);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("SetEventTest:WaitForSingleObject %s "
+ "failed (%x)\n",lpName,GetLastError());
+ }
+ else
+ {
+ /* Set the event to the previously created event and check
+ the return value.
+ */
+ bRet = SetEvent(hEvent);
+
+ if (!bRet)
+ {
+ Trace("SetEventTest:SetEvent %s "
+ "failed (%x)\n",lpName,GetLastError());
+ }
+ else
+ {
+ dwRet = WaitForSingleObject(hEvent,0);
+
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Trace("SetEventTest:WaitForSingleObject %s "
+ "failed (%x)\n",lpName,GetLastError());
+ }
+ else
+ {
+ dwRet = CloseHandle(hEvent);
+
+ if (!dwRet)
+ {
+ Trace("SetEventTest:CloseHandle %s "
+ "failed (%x)\n",lpName,GetLastError());
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Trace("SetEventTest:CreateEvent %s "
+ "failed (%x)\n",lpName,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..b5f3209410
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test1/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..dbec4fb81a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..df3f064f1d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test2/test2.c
@@ -0,0 +1,127 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ LPCTSTR lpName = "WooBaby";
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* create an event which we can use with SetEvent */
+ hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ lpName );
+
+ 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..696c0bfd0c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test2/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..a9de8bc9a7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..a8c7f80350
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test3/test3.c
@@ -0,0 +1,87 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ LPCTSTR lpName = "WooBaby";
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* create an event which we can use with SetEvent */
+ hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ lpName );
+
+ 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..4416dc928d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test3/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..a4e1861c66
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test4/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..39ff419305
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test4/test4.c
@@ -0,0 +1,163 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ LPCTSTR lpName = "OhYeah";
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* create an event which we can use with SetEvent */
+ hEvent = CreateEvent( lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ lpName );
+
+ 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..49b7736a15
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SetEvent/test4/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..d920ce25b2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/Sleep/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..08d4a81dfb
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/Sleep/test1/Sleep.c
@@ -0,0 +1,85 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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,
+ 1000
+};
+
+/* Milliseconds of error which are acceptable Function execution time, etc.
+ FreeBSD has a "standard" resolution of 10ms for waiting operations, so we
+ must take that into account as well */
+DWORD AcceptableTimeError = 15;
+
+int __cdecl main( int argc, char **argv )
+{
+ DWORD OldTickCount;
+ DWORD NewTickCount;
+ DWORD MaxDelta;
+ DWORD TimeDelta;
+ DWORD i;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ for( i = 0; i < sizeof(SleepTimes) / sizeof(DWORD); i++)
+ {
+ OldTickCount = GetTickCount();
+ Sleep(SleepTimes[i]);
+ NewTickCount = GetTickCount();
+
+ /*
+ * Check for DWORD wraparound
+ */
+ if (OldTickCount>NewTickCount)
+ {
+ OldTickCount -= NewTickCount+1;
+ NewTickCount = 0xFFFFFFFF;
+ }
+ TimeDelta = NewTickCount-OldTickCount;
+
+ /* 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..16bf4f0c77
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/Sleep/test1/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..122b46d6a6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/Sleep/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..387b627a79
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/Sleep/test2/sleep.c
@@ -0,0 +1,78 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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.
+ FreeBSD has a "standard" resolution of 10ms for waiting operations, so we
+ must take that into account as well */
+DWORD AcceptableTimeError = 150;
+
+int __cdecl main( int argc, char **argv )
+{
+ DWORD OldTickCount;
+ DWORD NewTickCount;
+ DWORD MaxDelta;
+ DWORD TimeDelta;
+ DWORD i;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ for( i = 0; i < sizeof(SleepTimes) / sizeof(DWORD); i++)
+ {
+ OldTickCount = GetTickCount();
+ Sleep(SleepTimes[i]);
+ NewTickCount = GetTickCount();
+
+ /*
+ * Check for DWORD wraparound
+ */
+ if (OldTickCount>NewTickCount)
+ {
+ OldTickCount -= NewTickCount+1;
+ NewTickCount = 0xFFFFFFFF;
+ }
+ TimeDelta = NewTickCount-OldTickCount;
+
+ 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..16bf4f0c77
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/Sleep/test2/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..35661db5c4
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..2dad00dc05
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/test1/test1.c
@@ -0,0 +1,95 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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},
+ {1000, FALSE},
+
+ {0, TRUE},
+ {50, TRUE},
+ {100, TRUE},
+ {500, TRUE},
+ {1000, TRUE},
+};
+
+/* Milliseconds of error which are acceptable Function execution time, etc.
+ FreeBSD has a "standard" resolution of 10ms for waiting operations, so we
+ must take that into account as well */
+DWORD AcceptableTimeError = 15;
+
+int __cdecl main( int argc, char **argv )
+{
+ DWORD OldTickCount;
+ DWORD NewTickCount;
+ DWORD MaxDelta;
+ DWORD TimeDelta;
+ DWORD i;
+
+ if (0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ for (i = 0; i<sizeof(testCases) / sizeof(testCases[0]); i++)
+ {
+ OldTickCount = GetTickCount();
+
+ SleepEx(testCases[i].SleepTime, testCases[i].Alertable);
+
+ NewTickCount = GetTickCount();
+
+ /*
+ * Check for DWORD wraparound
+ */
+ if (OldTickCount>NewTickCount)
+ {
+ OldTickCount -= NewTickCount+1;
+ NewTickCount = 0xFFFFFFFF;
+ }
+
+ TimeDelta = NewTickCount - OldTickCount;
+
+ /* 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..d806474140
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/test1/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..7e6d8d5f2c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..b0fcdcee89
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/test2/test2.c
@@ -0,0 +1,178 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 */
+const DWORD AcceptableDelta = 50;
+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 += abs(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 += abs(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)
+{
+ DWORD OldTickCount;
+ DWORD NewTickCount;
+ BOOL Alertable;
+ DWORD ret;
+
+ Alertable = (BOOL) lpParameter;
+
+ OldTickCount = GetTickCount();
+
+ ret = SleepEx(ChildThreadSleepTime, Alertable);
+
+ NewTickCount = GetTickCount();
+
+
+ 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);
+ }
+
+
+ /*
+ * Check for DWORD wraparound
+ */
+ if (OldTickCount>NewTickCount)
+ {
+ OldTickCount -= NewTickCount+1;
+ NewTickCount = 0xFFFFFFFF;
+ }
+
+ ThreadSleepDelta = NewTickCount - OldTickCount;
+
+ 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..b80c48451f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/test2/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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/SuspendThread/CMakeLists.txt b/src/pal/tests/palsuite/threading/SuspendThread/CMakeLists.txt
new file mode 100644
index 0000000000..8083faf655
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/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/SuspendThread/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/SuspendThread/test1/CMakeLists.txt
new file mode 100644
index 0000000000..ae55599f51
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_suspendthread_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_suspendthread_test1 CoreClrPal)
+
+target_link_libraries(paltest_suspendthread_test1
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test1/test1.c b/src/pal/tests/palsuite/threading/SuspendThread/test1/test1.c
new file mode 100644
index 0000000000..a0bff0b1f2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test1/test1.c
@@ -0,0 +1,158 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*============================================================
+**
+** Source: test1.c
+**
+** Purpose: Test for SuspendThread. Create a thread, which is a
+** function that is counting. Then suspend it, and make sure that it
+** has stopped increasing the counter.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+volatile DWORD dwSuspendThreadTestParameter = 0;
+volatile DWORD dwSuspendThreadTestCounter = 0;
+
+DWORD PALAPI SuspendThreadTestThread( LPVOID lpParameter)
+{
+ DWORD dwRet = 0;
+
+ /* save parameter for test */
+ dwSuspendThreadTestParameter = (DWORD)lpParameter;
+
+ while (dwSuspendThreadTestParameter)
+ {
+ dwSuspendThreadTestCounter++;
+ }
+
+ return dwRet;
+}
+
+BOOL SuspendThreadTest()
+{
+ BOOL bRet = FALSE;
+ DWORD dwRet = 0;
+
+ LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL;
+ DWORD dwStackSize = 0;
+ LPTHREAD_START_ROUTINE lpStartAddress = &SuspendThreadTestThread;
+ LPVOID lpParameter = (LPVOID)1;
+ DWORD dwCreationFlags = 0; /* run immediately */
+ DWORD dwThreadId = 0;
+
+ HANDLE hThread = 0;
+
+ dwSuspendThreadTestParameter = 0;
+
+
+ /* Create a thread and ensure it returned a valid handle */
+
+ hThread = CreateThread( lpThreadAttributes,
+ dwStackSize, lpStartAddress, lpParameter,
+ dwCreationFlags, &dwThreadId );
+
+ if (hThread != INVALID_HANDLE_VALUE)
+ {
+ /* Wait and ensure that WAIT_TIMEOUT is returned */
+ dwRet = WaitForSingleObject(hThread,1000);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("SuspendThreadTest:WaitForSingleObject "
+ "failed (%x)\n",GetLastError());
+ }
+ else
+ {
+ /* Suspend the thread */
+ dwRet = SuspendThread(hThread);
+
+ if (dwRet != 0)
+ {
+ Trace("SuspendThreadTest:SuspendThread "
+ "failed (%x)\n",GetLastError());
+ }
+ else
+ {
+ /* now check parameter, it should be greater than 0 */
+ if (dwSuspendThreadTestCounter == 0)
+ {
+ Trace("SuspendThreadTest:parameter error\n");
+ }
+ else
+ {
+ /* Save the counter */
+ dwRet = dwSuspendThreadTestCounter;
+
+ /* Wait a second */
+ Sleep(1000);
+
+ /* Ensure the counter hasn't changed becuase the
+ thread was suspended.
+ */
+
+ if (dwSuspendThreadTestCounter != dwRet)
+ {
+ Trace("SuspendThreadTest:parameter error\n");
+ }
+ else
+ {
+ /* Resume the thread */
+ dwRet = ResumeThread(hThread);
+
+ if (dwRet != 1)
+ {
+ Trace("SuspendThreadTest:ResumeThread "
+ "failed (%x)\n",GetLastError());
+ }
+ else
+ {
+ dwSuspendThreadTestParameter = 0;
+
+ /* set thread to exit and wait */
+ dwRet = WaitForSingleObject(hThread,INFINITE);
+
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Trace("SuspendThreadTest:WaitForSingleObject"
+ " failed (%x)\n",GetLastError());
+ }
+ else
+ {
+ bRet = TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Trace("SuspendThreadTest:CreateThread failed (%x)\n",GetLastError());
+ }
+
+ return bRet;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!SuspendThreadTest())
+ {
+ Fail ("Test failed\n");
+ }
+
+ PAL_Terminate();
+ return (PASS);
+
+}
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test1/testinfo.dat b/src/pal/tests/palsuite/threading/SuspendThread/test1/testinfo.dat
new file mode 100644
index 0000000000..0c06dd0060
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test1/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = SuspendThread
+Name = Positive Test for SuspendThread
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Test for SuspendThread. Create a thread, which is a
+= function that is counting. Then suspend it, and make sure that it
+= has stopped increasing the counter.
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/SuspendThread/test2/CMakeLists.txt
new file mode 100644
index 0000000000..7d6dda8940
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_suspendthread_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_suspendthread_test2 CoreClrPal)
+
+target_link_libraries(paltest_suspendthread_test2
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test2/test2.c b/src/pal/tests/palsuite/threading/SuspendThread/test2/test2.c
new file mode 100644
index 0000000000..2ac99706c0
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test2/test2.c
@@ -0,0 +1,186 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*============================================================
+**
+** Source: test2.c
+**
+** Purpose: Test for SuspendThread. Tests mutex contention inside
+** SuspendThread and calls to PAL suspension safe memory functions.
+** The expected failure case for this test is a hang due to mutex
+** acquisition deadlock.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+#define NUM_MALLOCS 256
+#define MAX_THREADS 64
+int numThreads, numIterations;
+HANDLE targetThreads[MAX_THREADS];
+
+void MemoryRoutine()
+{
+ int *ptr[NUM_MALLOCS];
+ unsigned long int i, j;
+
+ for(i = 0; i < NUM_MALLOCS; i++)
+ {
+ ptr[i] = (int *)malloc(10);
+ }
+
+ for(j = 0; j < NUM_MALLOCS; j++)
+ {
+ if(ptr[j] != NULL)
+ free((void*)ptr[j]);
+ }
+
+}
+
+HANDLE CreateSuspendedThread(LPTHREAD_START_ROUTINE lpStartAddress, DWORD lpParameter)
+{
+ DWORD dwThreadId = 0;
+ HANDLE hThread = CreateThread(NULL, 0, lpStartAddress,
+ (LPVOID)lpParameter, CREATE_SUSPENDED, &dwThreadId);
+ if (hThread == NULL)
+ {
+ Fail("Failed to create a suspended thread.\n");
+ }
+ return hThread;
+}
+
+DWORD PALAPI TargetThreadRoutine1(LPVOID lpParameter)
+{
+ long int i, r;
+ float j;
+ LONG dwRet = -1;
+ // targetIndex is the index of the target thread in the targetThreads array.
+ DWORD targetIndex = (DWORD)lpParameter;
+
+ for(i = 1; i <= numIterations; i++)
+ {
+ r = 4 * (rand() % 101);
+ for(j = 0; j < r; j+=1.45);
+ {
+ MemoryRoutine();
+ dwRet = SuspendThread(targetThreads[targetIndex]);
+ if (dwRet < 0)
+ {
+ Fail("Failed to suspend a target thread - SuspendThread returned %d.\n", dwRet);
+ }
+ MemoryRoutine();
+ dwRet = ResumeThread(targetThreads[targetIndex]);
+ if (dwRet < 0)
+ {
+ Fail("Failed to resume a target thread - ResumeThread returned %d.\n", dwRet);
+ }
+ MemoryRoutine();
+ }
+ }
+ printf("A suspender routine is done\n");
+ return 0;
+
+}
+
+DWORD PALAPI TargetThreadRoutine2(LPVOID lpParameter)
+{
+ long int i, r;
+ float j;
+ LONG dwRet = -1;
+ // targetIndex is the index of the target thread in the targetThreads array.
+ DWORD targetIndex = (DWORD)lpParameter;
+
+ for(i = 1; i <= numIterations; i++)
+ {
+ r = 5.3 * (rand() % 107);
+ for(j = 0; j < r; j+=1.67);
+ {
+ MemoryRoutine();
+ SuspendThread(targetThreads[targetIndex]);
+ if (dwRet < 0)
+ {
+ Fail("Failed to suspend a target thread - SuspendThread returned %d.\n", dwRet);
+ }
+ MemoryRoutine();
+ dwRet = ResumeThread(targetThreads[targetIndex]);
+ if (dwRet < 0)
+ {
+ Fail("Failed to resume a target thread - ResumeThread returned %d.\n", dwRet);
+ }
+ MemoryRoutine();
+ }
+
+ }
+ printf("A target routine is done\n");
+ return 0;
+
+}
+
+int __cdecl main(int argc, char *argv[])
+{
+ int i;
+ DWORD dwRet;
+
+ if(0 != PAL_Initialize(argc, argv))
+ {
+ return(FAIL);
+ }
+
+ if(argc == 3)
+ {
+ numThreads = atoi(argv[1]); // must be divisible by 2 - check for this.
+ numIterations = atoi(argv[2]); // must be greater than 0.
+ }
+ else
+ {
+ numThreads = 2;
+ numIterations = 1000;
+ }
+
+ if(numThreads < 2 || numThreads > MAX_THREADS)
+ {
+ Fail("numThreads must be greater than or equal to 2 and less than %d.\n", MAX_THREADS);
+ }
+
+ if(numThreads % 2 != 0)
+ {
+ Fail("numThreads must be divisible by 2.\n");
+ }
+
+ if(numIterations < 1)
+ {
+ Fail("numIterations must be greater than 0.\n");
+ }
+
+ srand((unsigned)time(NULL)); // seed random number generator
+
+ for(i = 0; i < numThreads / 2; i++)
+ {
+ targetThreads[i] = CreateSuspendedThread(&TargetThreadRoutine1, (i + (numThreads / 2)));
+ if (-1 == ResumeThread(targetThreads[i]))
+ {
+ Fail("Failed to resume a target thread.\n");
+ }
+
+ targetThreads[(i + (numThreads / 2))] = CreateSuspendedThread(&TargetThreadRoutine2, i);
+ if (-1 == ResumeThread(targetThreads[(i + (numThreads / 2))]))
+ {
+ Fail("Failed to resume a target thread.\n");
+ }
+ }
+
+ dwRet = WaitForMultipleObjects(numThreads, targetThreads, TRUE, INFINITE); // wait for all threads
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed\n");
+ }
+
+ PAL_Terminate();
+ return(PASS);
+
+}
+
+
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test2/testinfo.dat b/src/pal/tests/palsuite/threading/SuspendThread/test2/testinfo.dat
new file mode 100644
index 0000000000..6caa2376ef
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test2/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = SuspendThread
+Name = MutexContentionTestforSuspendThread
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Test for SuspendThread. Tests mutex contention inside
+= SuspendThread and calls to PAL suspension safe memory functions.
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test3/CMakeLists.txt b/src/pal/tests/palsuite/threading/SuspendThread/test3/CMakeLists.txt
new file mode 100644
index 0000000000..481812fa72
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test3.c
+)
+
+add_executable(paltest_suspendthread_test3
+ ${SOURCES}
+)
+
+add_dependencies(paltest_suspendthread_test3 CoreClrPal)
+
+target_link_libraries(paltest_suspendthread_test3
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test3/test3.c b/src/pal/tests/palsuite/threading/SuspendThread/test3/test3.c
new file mode 100644
index 0000000000..ac16a1c705
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test3/test3.c
@@ -0,0 +1,199 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*============================================================
+**
+** Source: test3.c
+**
+** Purpose: Test for SuspendThread. Tests a set of threads
+** suspending each other in a cycle, along with testing safe
+** memory allocation. The expected failure case for this test
+** is a hang due to mutex acquisition deadlock or suspending
+** a thread holding an internal lock.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+#define NUM_MALLOCS 256
+#define MAX_THREADS 64
+int numThreads, numIterations;
+HANDLE targetThreads[MAX_THREADS];
+
+void MemoryRoutine()
+{
+ int *ptr[NUM_MALLOCS];
+ unsigned long int i, j;
+
+ for(i = 0; i < NUM_MALLOCS; i++)
+ {
+ ptr[i] = (int *)malloc(10);
+ }
+
+ for(j = 0; j < NUM_MALLOCS; j++)
+ {
+ if(ptr[j] != NULL)
+ free((void*)ptr[j]);
+ }
+
+}
+
+HANDLE CreateSuspendedThread(LPTHREAD_START_ROUTINE lpStartAddress, DWORD lpParameter)
+{
+ DWORD dwThreadId = 0;
+ HANDLE hThread = CreateThread(NULL, 0, lpStartAddress,
+ (LPVOID)lpParameter, CREATE_SUSPENDED, &dwThreadId);
+ if (hThread == NULL)
+ {
+ Fail("Failed to create a suspended thread.\n");
+ }
+ return hThread;
+}
+
+DWORD PALAPI TargetThreadRoutine1(LPVOID lpParameter)
+{
+ long int i, r;
+ float j;
+ LONG dwRet = -1;
+ // targetIndex is the index of the target thread in the targetThreads array.
+ DWORD targetIndex = (DWORD)lpParameter;
+
+ for(i = 1; i <= numIterations; i++)
+ {
+ r = 4 * (rand() % 101);
+ for(j = 0; j < r; j+=1.45);
+ {
+ MemoryRoutine();
+ dwRet = SuspendThread(targetThreads[targetIndex]);
+ if (dwRet < 0)
+ {
+ Fail("Failed to suspend a target thread - SuspendThread returned %d.\n", dwRet);
+ }
+ MemoryRoutine();
+ dwRet = ResumeThread(targetThreads[targetIndex]);
+ if (dwRet < 0)
+ {
+ Fail("Failed to resume a target thread - ResumeThread returned %d.\n", dwRet);
+ }
+ MemoryRoutine();
+ }
+ }
+ printf("A suspender routine is done\n");
+ return 0;
+
+}
+
+DWORD PALAPI TargetThreadRoutine2(LPVOID lpParameter)
+{
+ long int i, r;
+ float j;
+ LONG dwRet = -1;
+ // targetIndex is the index of the target thread in the targetThreads array.
+ DWORD targetIndex = (DWORD)lpParameter;
+
+ for(i = 1; i <= numIterations; i++)
+ {
+ r = 5.3 * (rand() % 107);
+ for(j = 0; j < r; j+=1.67);
+ {
+ MemoryRoutine();
+ dwRet = SuspendThread(targetThreads[targetIndex]);
+ if (dwRet < 0)
+ {
+ Fail("Failed to suspend a target thread - SuspendThread returned %d.\n", dwRet);
+ }
+ MemoryRoutine();
+ dwRet = ResumeThread(targetThreads[targetIndex]);
+ if (dwRet < 0)
+ {
+ Fail("Failed to resume a target thread - ResumeThread returned %d.\n", dwRet);
+ }
+ MemoryRoutine();
+ }
+
+ }
+ printf("A target routine is done\n");
+ return 0;
+
+}
+
+int __cdecl main(int argc, char *argv[])
+{
+ int i;
+ DWORD dwRet;
+
+ if(0 != PAL_Initialize(argc, argv))
+ {
+ return(FAIL);
+ }
+
+ if(argc == 3)
+ {
+ numThreads = atoi(argv[1]); // must be divisible by 2 - check for this.
+ numIterations = atoi(argv[2]); // must be greater than 0.
+ }
+ else
+ {
+ numThreads = 4;
+ numIterations = 1000;
+ }
+
+ if(numThreads < 4 || numThreads > MAX_THREADS)
+ {
+ Fail("numThreads must be greater than or equal to 4 and less than %d.\n", MAX_THREADS);
+ }
+
+ if(numThreads % 4 != 0)
+ {
+ Fail("numThreads must be divisible by 4.\n");
+ }
+
+ if(numIterations < 1)
+ {
+ Fail("numIterations must be greater than 0.\n");
+ }
+
+ srand((unsigned)time(NULL)); // seed random number generator
+
+ for(i = 0; i < numThreads / 4; i++)
+ {
+ targetThreads[i + ((3 * numThreads) / 4)] = CreateSuspendedThread(&TargetThreadRoutine2, i);
+ if (-1 == ResumeThread(targetThreads[i + ((3 * numThreads) / 4)]))
+ {
+ Fail("Failed to resume a target thread.\n");
+ }
+
+ targetThreads[(i + (numThreads / 2))] = CreateSuspendedThread(&TargetThreadRoutine1, (i + ((3 * numThreads) / 4)));
+ if (-1 == ResumeThread(targetThreads[(i + (numThreads / 2))]))
+ {
+ Fail("Failed to resume a target thread.\n");
+ }
+
+ targetThreads[(i + (numThreads / 4))] = CreateSuspendedThread(&TargetThreadRoutine2, (i + (numThreads / 2)));
+ if (-1 == ResumeThread(targetThreads[(i + (numThreads / 4))]))
+ {
+ Fail("Failed to resume a target thread.\n");
+ }
+
+ targetThreads[i] = CreateSuspendedThread(&TargetThreadRoutine1, (i + (numThreads / 4)));
+ if (-1 == ResumeThread(targetThreads[i]))
+ {
+ Fail("Failed to resume a target thread.\n");
+ }
+ }
+
+ dwRet = WaitForMultipleObjects(numThreads, targetThreads, TRUE, INFINITE); // wait for all threads
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed\n");
+ }
+
+ PAL_Terminate();
+ return(PASS);
+
+}
+
+
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test3/testinfo.dat b/src/pal/tests/palsuite/threading/SuspendThread/test3/testinfo.dat
new file mode 100644
index 0000000000..20a5cde7c1
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test3/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = SuspendThread
+Name = CycleSuspensionTestforSuspendThread
+TYPE = DEFAULT
+EXE1 = test3
+Description
+= Test for SuspendThread. Tests a set of threads
+= suspending each other in a cycle, along with testing safe
+= memory allocation.
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test4/CMakeLists.txt b/src/pal/tests/palsuite/threading/SuspendThread/test4/CMakeLists.txt
new file mode 100644
index 0000000000..e0ba19c94f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test4/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test4.c
+)
+
+add_executable(paltest_suspendthread_test4
+ ${SOURCES}
+)
+
+add_dependencies(paltest_suspendthread_test4 CoreClrPal)
+
+target_link_libraries(paltest_suspendthread_test4
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test4/test4.c b/src/pal/tests/palsuite/threading/SuspendThread/test4/test4.c
new file mode 100644
index 0000000000..4b3c5b7d99
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test4/test4.c
@@ -0,0 +1,208 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*============================================================
+**
+** Source: test4.c
+**
+** Purpose: Test for SuspendThread. Suspending threads suspend
+** targets from a pool using random selection or iterative
+** selection. This also tests SuspendThread and ResumeThread
+** being invoked in a shutdown scenario. Finally, suspension safe
+** memory allocation functions are tested. The expected failure
+** case for this test is a hang.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+#define NUM_MALLOCS 256
+#define MAX_THREADS 64
+int numThreads, numIterations, targetThreadsSelectionAlgo, targetThreadsPos;
+HANDLE targetThreads[MAX_THREADS];
+
+void MemoryRoutine()
+{
+ int *ptr[NUM_MALLOCS];
+ unsigned long int i, j;
+
+ for(i = 0; i < NUM_MALLOCS; i++)
+ {
+ ptr[i] = (int *)malloc(10);
+ }
+
+ for(j = 0; j < NUM_MALLOCS; j++)
+ {
+ if(ptr[j] != NULL)
+ free((void*)ptr[j]);
+ }
+
+}
+
+HANDLE CreateSuspendedThread(LPTHREAD_START_ROUTINE lpStartAddress)
+{
+ DWORD dwThreadId = 0;
+ HANDLE hThread = CreateThread(NULL, 0, lpStartAddress,
+ (LPVOID)0, CREATE_SUSPENDED, &dwThreadId);
+ if (hThread == NULL)
+ {
+ Fail("Failed to create a suspended thread.\n");
+ }
+ return hThread;
+}
+
+HANDLE GetRandomTargetThread()
+{
+ return targetThreads[(int)(rand() % (numThreads / 2))];
+}
+
+HANDLE GetNextTargetThread()
+{
+ // next thread in queue.
+ HANDLE h = targetThreads[targetThreadsPos];
+ if(targetThreadsPos < ((numThreads / 2) - 1))
+ {
+ targetThreadsPos++;
+ }
+ else if(targetThreadsPos == ((numThreads / 2) - 1))
+ {
+ targetThreadsPos = 0;
+ }
+ else
+ {
+ Fail("The target thread index cannot be %d in GetNextTargetThread.\n", targetThreadsPos);
+ }
+ return h;
+
+}
+
+DWORD PALAPI SuspenderThreadRoutine()
+{
+ HANDLE targetThread;
+ LONG dwRet;
+ long int i;
+
+ for(i = 0; i < numIterations; i++)
+ {
+ if(targetThreadsSelectionAlgo)
+ {
+ targetThread = GetRandomTargetThread();
+ }
+ else
+ {
+ targetThread = GetNextTargetThread();
+ }
+
+ dwRet = SuspendThread(targetThread);
+ if (dwRet < 0)
+ {
+ Fail("Failed to suspend a target thread - SuspendThread returned %d.\n", dwRet);
+ }
+ MemoryRoutine();
+ dwRet = ResumeThread(targetThread);
+ if (dwRet < 0)
+ {
+ Fail("Failed to resume a target thread - ResumeThread returned %d.\n", dwRet);
+ }
+ }
+
+ printf("Suspender Routine is done.\n");
+
+ return 0;
+
+}
+
+DWORD PALAPI TargetThreadRoutine()
+{
+
+ while(TRUE)
+ {
+ MemoryRoutine();
+ sched_yield();
+ }
+
+ printf("Target Routine is done.\n");
+ return 0;
+
+}
+
+int __cdecl main(int argc, char *argv[])
+{
+ int i, j, k;
+ DWORD dwRet;
+ HANDLE suspenderRoutineHandles[1024];
+ targetThreadsPos = 0;
+
+ if(0 != PAL_Initialize(argc, argv))
+ {
+ return(FAIL);
+ }
+
+ if(argc == 4)
+ {
+ numThreads = atoi(argv[1]); // must be divisible by 2 - check for this.
+ numIterations = atoi(argv[2]); // must be greater than 0.
+ targetThreadsSelectionAlgo = atoi(argv[3]); // must be 0 or 1.
+ }
+ else
+ {
+ numThreads = 2;
+ numIterations = 1000;
+ targetThreadsSelectionAlgo = 0;
+ }
+
+ if(numThreads < 2 || numThreads > MAX_THREADS)
+ {
+ Fail("numThreads must be greater than or equal to 2 and less than %d.\n", MAX_THREADS);
+ }
+
+ if(numThreads % 2 != 0)
+ {
+ Fail("numThreads must be divisible by 2.\n");
+ }
+
+ if(numIterations < 1)
+ {
+ Fail("numIterations must be greater than 0.\n");
+ }
+
+ if(targetThreadsSelectionAlgo != 0 && targetThreadsSelectionAlgo != 1)
+ {
+ Fail("The target threads selection algorithm should be 0 for iterative thread selection or 1 for random selection.\n");
+ }
+
+ srand((unsigned)time(NULL)); // seed random number generator
+
+ for(i = 0; i < (numThreads / 2); i++)
+ {
+ targetThreads[i] = CreateSuspendedThread(&TargetThreadRoutine);
+ targetThreads[(i + (numThreads / 2))] = CreateSuspendedThread(&SuspenderThreadRoutine);
+ }
+
+ for(k = 0; k < (numThreads / 2); k++)
+ {
+ suspenderRoutineHandles[k] = targetThreads[(k + (numThreads / 2))];
+ }
+
+ for(j = 0; j < numThreads; j++)
+ {
+ if (-1 == ResumeThread(targetThreads[j])) // start all the target threads first.
+ {
+ Fail("Failed to resume a target thread.\n");
+ }
+ }
+
+ dwRet = WaitForMultipleObjects(numThreads / 2, suspenderRoutineHandles, TRUE, 60000);
+ if (WAIT_FAILED == dwRet)
+ {
+ Fail("WaitForMultipleObjects failed\n");
+ }
+
+ PAL_Terminate();
+ return(PASS);
+
+}
+
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test4/testinfo.dat b/src/pal/tests/palsuite/threading/SuspendThread/test4/testinfo.dat
new file mode 100644
index 0000000000..048648e43c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test4/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = SuspendThread
+Name = TargetPoolTestforSuspendThread
+TYPE = DEFAULT
+EXE1 = test4
+Description
+= Test for SuspendThread. Suspending threads suspend
+= targets from a pool using random selection or iterative
+= selection. Also tests suspension safe memory allocation
+= functions.
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test5/CMakeLists.txt b/src/pal/tests/palsuite/threading/SuspendThread/test5/CMakeLists.txt
new file mode 100644
index 0000000000..45913ec67f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test5/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test5.c
+)
+
+add_executable(paltest_suspendthread_test5
+ ${SOURCES}
+)
+
+add_dependencies(paltest_suspendthread_test5 CoreClrPal)
+
+target_link_libraries(paltest_suspendthread_test5
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test5/test5.c b/src/pal/tests/palsuite/threading/SuspendThread/test5/test5.c
new file mode 100644
index 0000000000..b8d7941f75
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test5/test5.c
@@ -0,0 +1,427 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*=============================================================================
+**
+** Source: test8.c
+**
+** Purpose:
+**
+** QueueUserAPC, Wait Subsystem and Thread Suspension stress test
+**
+** This test was written while researching a failure where a thread gets
+** stuck inside pthread_condition_signal, which is calling cond_queue_deq.
+** The thread would endlessly loop while nativigating the thread list, waiting
+** on the target condition. This occurs in the context of a thread posting an
+** APC to a thread of the VM's pool. This test partially reproduces the VM
+** scenario by posting (from different threads) lots of APCs to a couple of
+** threads which are looping in alterable SleepEx with a very short timeout.
+**
+**
+**
+**===========================================================================*/
+#include <palsuite.h>
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+
+#define MAX_THREAD_COUNT 128
+#define DEFAULT_EXECUTING_THREAD_COUNT 2
+#define DEFAULT_POSTING_THREAD_COUNT 3
+#define MAX_RECURSION_DEPTH 10
+#define APC_FUNC_SLEEPTIME 100 // ms
+#define DEFAULT_TEST_DURATION 60 // s
+
+volatile LONG g_rglOutstandingAPCs[MAX_THREAD_COUNT];
+
+LONG g_lExecutingThreadCount = DEFAULT_EXECUTING_THREAD_COUNT;
+LONG g_lPostingThreadCount = DEFAULT_POSTING_THREAD_COUNT;
+
+volatile LONG g_lCurrentExecutingThreadCount = 0;
+LONG g_lAPCPosted = 0;
+LONG g_lSuspCount = 0;
+LONG g_lResumeCount = 0;
+LONG g_lSuspFailCount = 0;
+LONG g_lResumeFailCount = 0;
+
+int g_iTestDuration = DEFAULT_TEST_DURATION;
+
+typedef struct _executing_thread_info
+{
+ DWORD dwThreadIdx;
+ LONG lDepth;
+ LONG lAPCCount;
+ HANDLE hDummyEvent;
+} executing_thread_info;
+
+executing_thread_info g_rgEti[MAX_THREAD_COUNT];
+
+HANDLE g_rghExecutingThread[MAX_THREAD_COUNT] = { 0 };
+HANDLE g_rghPostingThread[MAX_THREAD_COUNT] = { 0 };
+
+HANDLE g_hDoneEvent = NULL;
+HANDLE g_hDummyEvent = NULL;
+
+BOOL g_bDone = FALSE;
+
+VOID PALAPI APCFunc(ULONG_PTR ulptrParam)
+{
+ LONG lThreadIdx = (LONG)ulptrParam;
+ executing_thread_info * peti = (executing_thread_info *)&g_rgEti[lThreadIdx];
+ LONG lVal;
+ BOOL bAlertableSleep = (BOOL)(peti->lDepth < MAX_RECURSION_DEPTH);
+
+ lVal = InterlockedDecrement(&g_rglOutstandingAPCs[lThreadIdx]);
+
+ peti->lAPCCount++;
+ peti->lDepth += 1;
+ SleepEx(rand() % APC_FUNC_SLEEPTIME, bAlertableSleep);
+ peti->lDepth -= 1;
+}
+
+DWORD PALAPI ExecutingThread(LPVOID pvParam)
+{
+ DWORD dwRet = 0;
+ DWORD dwThreadId = (DWORD)pvParam;
+ HANDLE rghWaitobjs[2];
+ DWORD dwTmo = 10;
+
+ Trace("[Executing thread %u] Started \n", dwThreadId);
+
+ InterlockedIncrement(&g_lCurrentExecutingThreadCount);
+
+ rghWaitobjs[0] = g_hDoneEvent;
+ rghWaitobjs[1] = g_rgEti[dwThreadId].hDummyEvent;
+
+ do
+ {
+ switch (rand()%2)
+ {
+ case 0:
+ {
+ dwRet = SleepEx(1 + (rand() % dwTmo), TRUE);
+ break;
+ }
+ case 1:
+ default:
+ {
+ dwRet = WaitForMultipleObjectsEx(2, rghWaitobjs, FALSE, 1 + (rand() % dwTmo), TRUE);
+ break;
+ }
+ }
+ } while (!g_bDone);
+
+ InterlockedDecrement(&g_lCurrentExecutingThreadCount);
+ Trace("[Executing thread %u] Done\n", dwThreadId);
+ return 0;
+}
+
+DWORD PALAPI PostingThread(LPVOID pvParam)
+{
+ DWORD dwRet = 0;
+ DWORD dwThreadId = (DWORD)pvParam;
+ int i;
+ LONG lRet;
+
+ Trace("[Posting thread %u] Started \n", dwThreadId);
+
+ do
+ {
+ i = rand() % g_lExecutingThreadCount;
+ lRet = InterlockedIncrement(&g_rglOutstandingAPCs[i]);
+
+ if (MAX_RECURSION_DEPTH > lRet)
+ {
+ dwRet = QueueUserAPC(APCFunc, g_rghExecutingThread[i], (ULONG_PTR)i);
+ if (dwRet == 0)
+ {
+ Fail( "ERROR:%lu:QueueUserAPC call failed\n", GetLastError());
+ }
+
+ InterlockedIncrement(&g_lAPCPosted);
+ }
+ else
+ {
+ InterlockedDecrement(&g_rglOutstandingAPCs[i]);
+ }
+
+ if (0 == rand()%4)
+ {
+ SetEvent(g_hDummyEvent);
+ }
+ } while (WAIT_TIMEOUT == WaitForSingleObject(g_hDoneEvent, rand()%10));
+
+ Trace("[Posting thread %u] Done\n", dwThreadId);
+ return 0;
+}
+
+DWORD PALAPI SuspenderThread()
+{
+ HANDLE targetThread;
+ DWORD ret;
+ int i,j,idx,jdx;
+
+ printf("[Suspender Thread] Starting\n");
+
+ while (!g_bDone)
+ {
+ jdx = rand()%2;
+ for (j=0; j<2; j++, jdx++)
+ {
+ switch(jdx % 2)
+ {
+ case 0:
+ {
+ idx = rand() % g_lCurrentExecutingThreadCount;
+ for (i=0; i < g_lCurrentExecutingThreadCount; i++)
+ {
+ targetThread = g_rghExecutingThread[idx];
+ if (NULL != targetThread)
+ {
+ ret = SuspendThread(targetThread);
+ if (-1 != ret)
+ {
+ g_lSuspCount += 1;
+ }
+ else
+ {
+ g_lSuspFailCount += 1;
+ }
+ }
+ idx = (idx+1) % g_lCurrentExecutingThreadCount;
+ }
+ break;
+ }
+ case 1:
+ default:
+ {
+ idx = rand() % g_lPostingThreadCount;
+ for (i=0; i < g_lPostingThreadCount; i++)
+ {
+ targetThread = g_rghPostingThread[idx];
+ if (NULL != targetThread)
+ {
+ ret = SuspendThread(targetThread);
+ if (-1 != ret)
+ {
+ g_lSuspCount += 1;
+ }
+ else
+ {
+ g_lSuspFailCount += 1;
+ }
+ }
+ idx = (idx+1) % g_lPostingThreadCount;
+ }
+ break;
+ }
+ }
+ }
+
+ Sleep(rand() % 100);
+
+ jdx = rand() % 2;
+ for (j=0; j<2; j++, jdx++)
+ {
+ switch(jdx % 2)
+ {
+ case 0:
+ {
+ idx = rand() % g_lCurrentExecutingThreadCount;
+ for (i=0; i < g_lCurrentExecutingThreadCount; i++)
+ {
+ targetThread = g_rghExecutingThread[idx];
+ if (NULL != targetThread)
+ {
+ ret = ResumeThread(targetThread);
+ if (-1 != ret)
+ {
+ g_lResumeCount += 1;
+ }
+ else
+ {
+ g_lResumeFailCount += 1;
+ }
+ }
+ idx = (idx+1) % g_lCurrentExecutingThreadCount;
+ }
+ break;
+ }
+ case 1:
+ default:
+ {
+ idx = rand() % g_lPostingThreadCount;
+ for (i=0; i < g_lPostingThreadCount; i++)
+ {
+ targetThread = g_rghPostingThread[idx];
+ if (NULL != targetThread)
+ {
+ ret = ResumeThread(targetThread);
+ if (-1 != ret)
+ {
+ g_lResumeCount += 1;
+ }
+ else
+ {
+ g_lResumeFailCount += 1;
+ }
+ }
+ idx = (idx+1) % g_lPostingThreadCount;
+ }
+ break;
+ }
+ }
+ }
+
+ Sleep(rand() % 100);
+ }
+
+ printf("[Suspender Thread] Done\n");
+
+ return 0;
+}
+
+int __cdecl main( int argc, char **argv )
+{
+ int i, j;
+ DWORD dwThreadId;
+ DWORD dwRet;
+ HANDLE hSuspenderThread = NULL;
+
+ if ((PAL_Initialize(argc, argv)) != 0)
+ {
+ return(FAIL);
+ }
+
+ i = (int)(GetTickCount() % INT_MAX);
+ srand(i);
+
+ Trace("[main] Starting [seed=%d r1=%d r2=%d r3=%d]\n", i, rand(), rand(), rand());
+
+ g_hDoneEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ if ( g_hDoneEvent == NULL )
+ {
+ Fail("ERROR:%#x: CreateEvent() call failed\n", GetLastError());
+ }
+ g_hDummyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+
+ for (i=0; i<g_lExecutingThreadCount; i++)
+ {
+ g_rgEti[i].hDummyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if (g_rgEti[i].hDummyEvent == NULL)
+ {
+ Fail("ERROR:%#x: CreateEvent() call failed\n", GetLastError());
+ }
+ }
+
+ for (i=0, j=0; i<g_lExecutingThreadCount; i++)
+ {
+ g_rgEti[j].dwThreadIdx = j;
+ g_rgEti[j].lDepth = 0;
+ g_rgEti[j].lAPCCount = 0;
+
+ g_rghExecutingThread[j] = CreateThread(
+ NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)ExecutingThread,
+ (LPVOID)j,
+ 0,
+ &dwThreadId);
+
+ if (NULL != g_rghExecutingThread[j])
+ {
+ j++;
+ }
+ }
+
+ g_lExecutingThreadCount = j;
+
+ if (1 > g_lExecutingThreadCount)
+ {
+ Fail("Unable to create enough executing threads\n");
+ }
+ Trace("[main] %d executing threads created\n", g_lExecutingThreadCount);
+
+ for (i=0, j=0; i<g_lPostingThreadCount; i++)
+ {
+ g_rghPostingThread[j] = CreateThread(
+ NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)PostingThread,
+ (LPVOID)j,
+ 0,
+ &dwThreadId);
+ if(NULL != g_rghPostingThread[j])
+ {
+ j++;
+ }
+ }
+ g_lPostingThreadCount = j;
+
+ if (1 > g_lPostingThreadCount)
+ {
+ Fail("Unable to create enough posting threads\n");
+ }
+ Trace("[main] %d posting threads created\n", g_lPostingThreadCount);
+
+ hSuspenderThread = CreateThread(
+ NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)SuspenderThread,
+ NULL,
+ 0,
+ &dwThreadId);
+
+ Sleep (g_iTestDuration * 1000);
+
+ if (!SetEvent(g_hDoneEvent))
+ {
+ Fail("ERROR:%lu:SetEvent() call failed\n", GetLastError());
+ }
+
+ Sleep(g_iTestDuration / 10);
+
+ g_bDone = TRUE;
+
+ Trace("[main] Initiating shutdown\n" );
+
+ /* wait on the other thread to complete */
+ for (i=0; i < g_lExecutingThreadCount; i += MAXIMUM_WAIT_OBJECTS)
+ {
+ do
+ {
+ dwRet = WaitForMultipleObjects(
+ MIN(g_lExecutingThreadCount - i, MAXIMUM_WAIT_OBJECTS),
+ g_rghExecutingThread + i,
+ TRUE,
+ 1000);
+ } while (WAIT_TIMEOUT == dwRet);
+
+ if (WAIT_OBJECT_0 != dwRet)
+ {
+ Fail("Wait for all threads failed\n");
+ }
+ }
+
+ j = 0;
+ Trace("[main] Number of APC executed per target thread: { " );
+ for (i=0; i < g_lExecutingThreadCount; i++)
+ {
+ Trace("%d ", (int)g_rgEti[i].lAPCCount);
+ j += (int)g_rgEti[i].lAPCCount;
+ }
+ Trace(" }\n");
+ Trace("[main] Total number of APC executed: %d\n", j);
+ Trace("[main] Total number of APC posted: %d\n", g_lAPCPosted);
+ Trace("[main] Successfull thread suspensions: %d [%d failures]\n", g_lSuspCount, g_lSuspFailCount);
+ Trace("[main] Successfull thread resumes: %d [%d failures]\n", g_lResumeCount, g_lResumeFailCount);
+
+ /* PAL termination */
+ PAL_Terminate();
+
+ /* return success */
+ return PASS;
+}
diff --git a/src/pal/tests/palsuite/threading/SuspendThread/test5/testinfo.dat b/src/pal/tests/palsuite/threading/SuspendThread/test5/testinfo.dat
new file mode 100644
index 0000000000..9ef83f1b8d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SuspendThread/test5/testinfo.dat
@@ -0,0 +1,16 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = SuspendThread
+Name = QueueUserAPC test for SuspendThread
+TYPE = DEFAULT
+EXE1 = test5
+Description
+= Test for SuspendThread, QueueUserAPC, and the wait subsystem.
+= This test reproduces a VM scenario where a thread posts an APC
+= to a thread of the VM's pool. It was used to confirm the
+= signal unsafety of the pthread library on BSD 5.2.
+
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..cbfb5bdf2b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SwitchToThread/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..fb8ef83325
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SwitchToThread/test1/test1.c
@@ -0,0 +1,98 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..6dca81dd37
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SwitchToThread/test1/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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 \ No newline at end of file
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..7d326c71d1
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..2ba1f0343e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test1/TLS.c
@@ -0,0 +1,183 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..4cd279dc60
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test1/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..be4cc6e118
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..fe769a5066
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test2/TLS.c
@@ -0,0 +1,67 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..dcddc97f0a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test2/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..c8ee06e3ad
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..4e0909d14d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test3/TLS.c
@@ -0,0 +1,91 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..f2a0392754
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test3/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..850cb60f2b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test4/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..29363aacec
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test4/test4.c
@@ -0,0 +1,138 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..74b43f70f9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test4/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..957945db4a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test5/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..89a0a73ab9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test5/test5.c
@@ -0,0 +1,109 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..c34949db24
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test5/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..2b4d3479f2
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..1798844e87
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/test.c
@@ -0,0 +1,191 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..c399492be7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TLS/test6_optimizedtls/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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 \ No newline at end of file
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..23ad31cd06
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TerminateProcess/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..4092704b6d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TerminateProcess/test1/TerminateProcess.c
@@ -0,0 +1,42 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..149c36421f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/TerminateProcess/test1/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..676a5d38c5
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ThreadPriority/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..74d6474976
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ThreadPriority/test1/ThreadPriority.c
@@ -0,0 +1,84 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..788114e615
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/ThreadPriority/test1/testinfo.dat
@@ -0,0 +1,17 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..ef14ea5352
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/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/WaitForMultipleObjects/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/CMakeLists.txt
new file mode 100644
index 0000000000..453e89477e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..e5e23d8a2e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/test1.c
@@ -0,0 +1,208 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ LPTSTR lpName[MAX_EVENTS];
+
+ HANDLE hEvent[MAX_EVENTS];
+
+ /* Run through this for loop and create 4 events */
+ for (i = 0; i < MAX_EVENTS; i++)
+ {
+ lpName[i] = (TCHAR*)malloc(MAX_PATH);
+ sprintf(lpName[i],"Event #%d",i);
+
+ hEvent[i] = CreateEvent( lpEventAttributes,
+ bManualReset, bInitialState, lpName[i]);
+
+ if (hEvent[i] == INVALID_HANDLE_VALUE)
+ {
+ Trace("WaitForMultipleObjectsTest:CreateEvent "
+ "%s failed (%x)\n",lpName[i],GetLastError());
+ bRet = FALSE;
+ break;
+ }
+
+ /* Set the current event */
+ bRet = SetEvent(hEvent[i]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsTest:SetEvent %s "
+ "failed (%x)\n",lpName[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 "
+ "%s failed (%x)\n",lpName[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 %s "
+ "failed (%x)\n",lpName[i],GetLastError());
+ bRet = FALSE;
+ break;
+ }
+
+ dwRet = WaitForSingleObject(hEvent[i],0);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("WaitForMultipleObjectsTest:WaitForSingleObject "
+ "%s failed (%x)\n",lpName[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 "
+ "%s failed (%x)\n",lpName[0],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 "
+ "%s failed (%x)\n",
+ lpName[j],GetLastError());
+ break;
+ }
+ }
+ else
+ {
+ bRet = ResetEvent(hEvent[j]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsTest:ResetEvent "
+ "%s failed (%x)\n",
+ lpName[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"
+ " %s failed (%x)\n",lpName[0],GetLastError());
+ bRet = FALSE;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < MAX_EVENTS; i++)
+ {
+ bRet = CloseHandle(hEvent[i]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsTest:CloseHandle %s "
+ "failed (%x)\n",lpName[i],GetLastError());
+ }
+
+ free((void*)lpName[i]);
+ }
+ }
+
+ return bRet;
+}
+
+
+int __cdecl main(int argc, char **argv)
+{
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!WaitForMultipleObjectsTest())
+ {
+ 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..ecff2fca14
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test1/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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/WaitForMultipleObjects/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test2/CMakeLists.txt
new file mode 100644
index 0000000000..6f7be518e5
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test2.c
+)
+
+add_executable(paltest_waitformultipleobjects_test2
+ ${SOURCES}
+)
+
+add_dependencies(paltest_waitformultipleobjects_test2 CoreClrPal)
+
+target_link_libraries(paltest_waitformultipleobjects_test2
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test2/test2.c b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test2/test2.c
new file mode 100644
index 0000000000..057df66a70
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test2/test2.c
@@ -0,0 +1,140 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*============================================================================
+**
+** Source: test1.c
+**
+** Purpose: Test to ensure that WaitForMultipleObjects() performs
+** as expected when called with an INFINITE timeout.
+**
+**
+**==========================================================================*/
+#include <palsuite.h>
+
+/* global data */
+static BOOL bEventSet = FALSE;
+static HANDLE hEvent = NULL;
+
+
+/* handler function */
+static BOOL PALAPI CtrlHandler( DWORD CtrlType )
+{
+ if( CtrlType == CTRL_C_EVENT )
+ {
+ if( hEvent != NULL )
+ {
+ bEventSet = TRUE;
+ if( ! SetEvent( hEvent ) )
+ {
+ Fail( "ERROR:%lu:SetEvent call failed", GetLastError() );
+ }
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/* main entry point function */
+int __cdecl main( int argc, char **argv )
+
+{
+ /* local variables */
+ BOOL ret = FAIL;
+ DWORD dwRet;
+
+
+ /* PAL initialization */
+ if( (PAL_Initialize(argc, argv)) != 0 )
+ {
+ return( FAIL );
+ }
+
+
+ /* set the console control handler function */
+ if( ! SetConsoleCtrlHandler( CtrlHandler, TRUE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to add "
+ "CtrlHandler\n",
+ GetLastError() );
+ goto done;
+ }
+
+
+ /* create a manual-reset event on which to wait */
+ hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ if( hEvent == NULL )
+ {
+ Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
+ goto done;
+ }
+
+ /* helpful instructions */
+ Trace( "This program is designed to test WaitForMultipleObjects using\n"
+ "a timeout value of INFINITE, and consequently it's expected\n"
+ "to run forever, waiting and waiting. Press CTRL-C when you feel\n"
+ "you've sufficiently approximated infinity to end the test.\n" );
+
+ /* wait on the event forever, until the user stops the program */
+ dwRet = WaitForMultipleObjects( 1, &hEvent, TRUE, INFINITE );
+ if( dwRet != WAIT_OBJECT_0 )
+ {
+ Trace( "ERROR:WaitForMultipleObjects returned %lu, "
+ "expected WAIT_OBJECT_0\n",
+ dwRet );
+ goto done;
+ }
+
+ /* verify that the bEventSet flag has been set */
+ if( ! bEventSet )
+ {
+ Trace( "ERROR:WaitForMultipleObjects returned WAIT_OBJECT_0 "
+ "but the event was never set\n" );
+ goto done;
+ }
+
+ /* success if we get here */
+ ret = PASS;
+
+
+done:
+ /* close our event handle */
+ if( hEvent != NULL )
+ {
+ if( ! CloseHandle( hEvent ) )
+ {
+ Trace( "ERROR:%lu:CloseHandle call failed\n", GetLastError() );
+ ret = FAIL;
+ }
+ }
+
+ /* unset the control handle that was set */
+ if( ! SetConsoleCtrlHandler( CtrlHandler, FALSE ) )
+ {
+ ret = FAIL;
+ Trace( "ERROR:%lu:SetConsoleCtrlHandler() failed to "
+ "remove CtrlHandler\n",
+ GetLastError() );
+ }
+
+ /* check for failure */
+ if( ret == FAIL )
+ {
+ Fail( "test failed\n" );
+ }
+
+
+ /* PAL termination */
+ PAL_Terminate();
+
+
+ /* return success */
+ return PASS;
+}
+
+
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test2/testinfo.dat b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test2/testinfo.dat
new file mode 100644
index 0000000000..ce80a8f5a1
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjects/test2/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = WaitForMultipleObjects
+Name = Positive test for WaitForMultipleObjects
+TYPE = DEFAULT
+EXE1 = test2
+Description
+= Test to ensure that WaitForMultipleObjects() performs
+= as expected when called with an INFINITE timeout.
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..bea297acae
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..b762ce4f87
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/test1.c
@@ -0,0 +1,212 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ LPTSTR lpName[MAX_EVENTS];
+
+ HANDLE hEvent[MAX_EVENTS];
+
+ /* Run through this for loop and create 4 events */
+ for (i = 0; i < MAX_EVENTS; i++)
+ {
+ lpName[i] = (TCHAR*)malloc(MAX_PATH);
+ sprintf(lpName[i],"Event #%d",i);
+
+ hEvent[i] = CreateEvent( lpEventAttributes,
+ bManualReset, bInitialState, lpName[i]);
+
+ if (hEvent[i] == INVALID_HANDLE_VALUE)
+ {
+ Trace("WaitForMultipleObjectsExTest:CreateEvent "
+ "%s failed (%x)\n",lpName[i],GetLastError());
+ bRet = FALSE;
+ break;
+ }
+
+ /* Set the current event */
+ bRet = SetEvent(hEvent[i]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsExTest:SetEvent %s "
+ "failed (%x)\n",lpName[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 "
+ "%s failed (%x)\n",lpName[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 %s "
+ "failed (%x)\n",lpName[i],GetLastError());
+ bRet = FALSE;
+ break;
+ }
+
+ dwRet = WaitForSingleObject(hEvent[i],0);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("WaitForMultipleObjectsExTest:WaitForSingleObject "
+ "%s failed (%x)\n",lpName[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 "
+ "%s failed (%x)\n",lpName[0],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 "
+ "%s failed (%x)\n",
+ lpName[j],GetLastError());
+ break;
+ }
+ }
+ else
+ {
+ bRet = ResetEvent(hEvent[j]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsExTest:ResetEvent "
+ "%s failed (%x)\n",
+ lpName[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 %s failed (%x)\n",
+ lpName[0],GetLastError());
+ bRet = FALSE;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < MAX_EVENTS; i++)
+ {
+ bRet = CloseHandle(hEvent[i]);
+
+ if (!bRet)
+ {
+ Trace("WaitForMultipleObjectsExTest:CloseHandle %s "
+ "failed (%x)\n",lpName[i],GetLastError());
+ }
+
+ free((void*)lpName[i]);
+ }
+ }
+
+ return bRet;
+}
+
+
+int __cdecl main(int argc, char **argv)
+{
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(!WaitForMultipleObjectsExTest())
+ {
+ 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..7e5ff93561
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test1/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..66a1c04172
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..1f8534246b
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/test2.c
@@ -0,0 +1,193 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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) && ( abs( ThreadWaitDelta - ChildThreadWaitTime) > TOLERANCE ))
+ || (( ThreadWaitDelta < InterruptTime) && ( abs( 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 = abs( 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;
+ DWORD OldTickCount;
+ DWORD NewTickCount;
+ 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;
+
+ OldTickCount = GetTickCount();
+
+ ret = WaitForMultipleObjectsEx(1, &Semaphore, FALSE, ChildThreadWaitTime,
+ Alertable);
+
+ NewTickCount = GetTickCount();
+
+
+ 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);
+ }
+
+
+ /*
+ * Check for DWORD wraparound
+ */
+ if (OldTickCount>NewTickCount)
+ {
+ OldTickCount -= NewTickCount+1;
+ NewTickCount = 0xFFFFFFFF;
+ }
+
+ ThreadWaitDelta = NewTickCount - OldTickCount;
+
+ 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..3c53010ddf
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..25410f382d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..fc2f401c0e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/test3.c
@@ -0,0 +1,107 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..6f2e9084ed
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..2784b2da06
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..83ec1e8ec6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/test4.c
@@ -0,0 +1,102 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..8d87cf8c86
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..a6fb1996ec
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/CMakeLists.txt
@@ -0,0 +1,38 @@
+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
+ rt
+ 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
+ rt
+ 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..f90de0e922
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/commonconsts.h
@@ -0,0 +1,43 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..9d52f2fedb
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/helper.c
@@ -0,0 +1,123 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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_PATH];
+ 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_PATH-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_PATH-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..c16ab99fbe
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.c
@@ -0,0 +1,507 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..baca8354ea
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/testinfo.dat
@@ -0,0 +1,18 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..e9e91a33d7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/CMakeLists.txt
@@ -0,0 +1,38 @@
+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
+ rt
+ 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
+ rt
+ 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..e29407d5f7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/child6.c
@@ -0,0 +1,212 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..2e9ef51a7a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/test6.c
@@ -0,0 +1,710 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..611e1f7a9c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..35ecb1ee5f
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/WFSOExMutexTest.c
@@ -0,0 +1,217 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 int AcceptableDelta = 100;
+
+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
+ "SomeMutex"); //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 (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);
+ }
+
+
+
+ //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)
+{
+ DWORD OldTickCount;
+ DWORD NewTickCount;
+ BOOL Alertable;
+ DWORD ret;
+
+ Alertable = (BOOL) lpParameter;
+
+ OldTickCount = GetTickCount();
+
+ ret = WaitForSingleObjectEx( hMutex,
+ ChildThreadWaitTime,
+ Alertable);
+
+ NewTickCount = GetTickCount();
+
+ 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);
+ }
+
+ /*
+ * Check for DWORD wraparound
+ */
+ if (OldTickCount>NewTickCount)
+ {
+ OldTickCount -= NewTickCount+1;
+ NewTickCount = 0xFFFFFFFF;
+ }
+ ThreadWaitDelta = NewTickCount - OldTickCount;
+
+ 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..fc49935fda
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..ad8697c52c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/WFSOExSemaphoreTest.c
@@ -0,0 +1,188 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 int AcceptableDelta = 100;
+
+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 (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;
+
+ 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;
+ DWORD OldTickCount;
+ DWORD NewTickCount;
+ BOOL Alertable;
+ DWORD ret;
+
+ /* 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;
+
+ OldTickCount = GetTickCount();
+
+ ret = WaitForSingleObjectEx( hSemaphore,
+ ChildThreadWaitTime,
+ Alertable);
+
+ NewTickCount = GetTickCount();
+
+
+ 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);
+ }
+
+
+ /*
+ * Check for DWORD wraparound
+ */
+ if (OldTickCount>NewTickCount)
+ {
+ OldTickCount -= NewTickCount+1;
+ NewTickCount = 0xFFFFFFFF;
+ }
+
+ ThreadWaitDelta = NewTickCount - OldTickCount;
+
+ 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..788dc36fbf
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..b7b035819a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/WFSOExThreadTest.c
@@ -0,0 +1,208 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 int AcceptableDelta = 100;
+
+void RunTest(BOOL AlertThread);
+VOID PALAPI APCFunc(ULONG_PTR dwParam);
+DWORD PALAPI WaiterProc(LPVOID lpParameter);
+void WorkerThread(void);
+
+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 (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;
+ DWORD OldTickCount;
+ DWORD NewTickCount;
+ 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;
+
+ OldTickCount = GetTickCount();
+
+ ret = WaitForSingleObjectEx( hWaitThread,
+ ChildThreadWaitTime,
+ Alertable);
+
+ NewTickCount = GetTickCount();
+
+
+ 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);
+ }
+
+ /*
+ * Check for DWORD wraparound
+ */
+ if (OldTickCount>NewTickCount)
+ {
+ OldTickCount -= NewTickCount+1;
+ NewTickCount = 0xFFFFFFFF;
+ }
+
+ ThreadWaitDelta = NewTickCount - OldTickCount;
+
+ 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..302556240a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..f28d1a5a00
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/WFSOMutexTest.c
@@ -0,0 +1,186 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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
+ "MutexToProtectGlobalCounter"); // 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..33b8ef0ed6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/CMakeLists.txt
@@ -0,0 +1,38 @@
+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
+ rt
+ 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
+ rt
+ 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..b1592d437e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/ChildProcess.c
@@ -0,0 +1,51 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..71bc4069b7
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.c
@@ -0,0 +1,120 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..db8c58d083
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOSemaphoreTest/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..ca66d827b6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOSemaphoreTest/WFSOSemaphoreTest.c
@@ -0,0 +1,184 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..06d28c1c86
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..a2e5a87645
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/WFSOThreadTest.c
@@ -0,0 +1,180 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..2d343ed420
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..7bea24bbd6
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/test1/test1.c
@@ -0,0 +1,129 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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;
+ LPCTSTR lpName = "Event #6";
+
+ HANDLE hEvent;
+
+ /* Create an event, and ensure the HANDLE is valid */
+ hEvent = CreateEvent(lpEventAttributes, bManualReset,
+ bInitialState, lpName);
+
+ 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 %s "
+ "failed (%x)\n",lpName,GetLastError());
+ }
+ else
+ {
+ bRet = CloseHandle(hEvent);
+
+ if (!bRet)
+ {
+ Trace("WaitForSingleObjectTest:CloseHandle %s "
+ "failed (%x)\n",lpName,GetLastError());
+ }
+ }
+ }
+ else
+ {
+ Trace("WaitForSingleObjectTest:CreateEvent %s "
+ "failed (%x)\n",lpName,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, lpName);
+
+ 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 %s "
+ "failed (%x)\n",lpName,GetLastError());
+ }
+ else
+ {
+ bRet = CloseHandle(hEvent);
+
+ if (!bRet)
+ {
+ Trace("WaitForSingleObjectTest:CloseHandle %s failed "
+ "(%x)\n",lpName,GetLastError());
+ }
+ }
+ }
+ else
+ {
+ Trace("WaitForSingleObjectTest::CreateEvent %s failed "
+ "(%x)\n",lpName,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..210b87517c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForSingleObject/test1/testinfo.dat
@@ -0,0 +1,14 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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..4de6544c77
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/YieldProcessor/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..a3bfd65435
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/YieldProcessor/test1/test1.c
@@ -0,0 +1,93 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..8f22989d55
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/YieldProcessor/test1/testinfo.dat
@@ -0,0 +1,13 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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 \ No newline at end of file
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..79cbfd9ac3
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/releasesemaphore/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+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
+ rt
+ 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..6433e3dc6a
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/releasesemaphore/test1/test.c
@@ -0,0 +1,69 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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..15e590e085
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/releasesemaphore/test1/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+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. \ No newline at end of file
diff --git a/src/pal/tests/palsuite/threading/setthreadcontext/CMakeLists.txt b/src/pal/tests/palsuite/threading/setthreadcontext/CMakeLists.txt
new file mode 100644
index 0000000000..f6aa0cb2d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/setthreadcontext/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/setthreadcontext/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/setthreadcontext/test1/CMakeLists.txt
new file mode 100644
index 0000000000..f7531acb06
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/setthreadcontext/test1/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ test1.c
+)
+
+add_executable(paltest_setthreadcontext_test1
+ ${SOURCES}
+)
+
+add_dependencies(paltest_setthreadcontext_test1 CoreClrPal)
+
+target_link_libraries(paltest_setthreadcontext_test1
+ pthread
+ rt
+ m
+ CoreClrPal
+)
diff --git a/src/pal/tests/palsuite/threading/setthreadcontext/test1/test1.c b/src/pal/tests/palsuite/threading/setthreadcontext/test1/test1.c
new file mode 100644
index 0000000000..f460e70258
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/setthreadcontext/test1/test1.c
@@ -0,0 +1,282 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*============================================================
+**
+** Source: test1.c
+**
+** Purpose: Test for SetThreadContext. Create a thread, which is a
+** function that is counting. Then suspend it, and then make sure
+** GetThreadContext and SetThreadContext can run.
+** This test case is modified from suspendthread test1 test case.
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+volatile DWORD dwSuspendThreadTestParameter = 0;
+volatile DWORD dwSuspendThreadTestCounter = 0;
+
+#if defined(_PPC_)
+void ModifyContext(CONTEXT *lpContext)
+{
+ ULONG *gpr;
+ double *fpr;
+ int i;
+ /* Since Fpscr is a double in PAL,
+ This variable is used for setting Fpscr.
+ Only the lower 32-bits are valid. */
+ ULONGLONG fpscrll = 0x12345678;
+
+ gpr = &lpContext->Gpr0;
+ for (i = 0; i < 32; i++, gpr++)
+ {
+ *gpr = i;
+ }
+
+ /* Note: Msr cannot be changed. */
+ lpContext->Cr = 1;
+ lpContext->Xer = 2;
+ lpContext->Iar = 3;
+ lpContext->Lr = 4;
+ lpContext->Ctr = 5;
+
+ lpContext->Fpscr = *(double *)&fpscrll;
+ fpr = &lpContext->Fpr0;
+ for (i = 0; i < 32; i++, fpr++)
+ {
+ *fpr = (double) (i*0.1);
+ }
+}
+#elif defined(_SPARC_)
+void ModifyContext(CONTEXT *lpContext)
+{
+ ULONG *gpr;
+ double *fpr;
+ int i;
+
+ gpr = &lpContext->g0;
+ for (i = 0; i < 32; i++, gpr++)
+ {
+ *gpr = i;
+ }
+
+ /* Note: psr, and fsr cannot be changed. */
+ /* pc and npc must be 4-byte aligned. */
+ lpContext->y = 1;
+ lpContext->pc = 0x11223344;
+ lpContext->npc = 0x12341234;
+
+ fpr = &lpContext->fprs.d[0];
+ for (i = 0; i < 16; i++, fpr++)
+ {
+ *fpr = (double) (i*0.1);
+ }
+}
+#else
+void ModifyContext(CONTEXT *lpContext)
+{
+}
+#endif
+
+
+DWORD PALAPI SuspendThreadTestThread( LPVOID lpParameter)
+{
+ DWORD dwRet = 0;
+
+ /* save parameter for test */
+ dwSuspendThreadTestParameter = (DWORD) ((UINT_PTR) lpParameter);
+
+ while (dwSuspendThreadTestParameter)
+ {
+ dwSuspendThreadTestCounter++;
+ }
+
+ return dwRet;
+}
+
+BOOL SuspendThreadTest()
+{
+ BOOL bRet = FALSE;
+ DWORD dwRet = 0;
+
+ LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL;
+ DWORD dwStackSize = 0;
+ LPTHREAD_START_ROUTINE lpStartAddress = &SuspendThreadTestThread;
+ LPVOID lpParameter = (LPVOID)1;
+ DWORD dwCreationFlags = 0; /* run immediately */
+ DWORD dwThreadId = 0;
+ CONTEXT savedContext;
+ CONTEXT modifiedContext;
+ CONTEXT verifyContext;
+
+
+ HANDLE hThread = 0;
+
+ dwSuspendThreadTestParameter = 0;
+
+ /* Create a thread and ensure it returned a valid handle */
+
+ hThread = CreateThread( lpThreadAttributes,
+ dwStackSize, lpStartAddress, lpParameter,
+ dwCreationFlags, &dwThreadId );
+
+ if (hThread != INVALID_HANDLE_VALUE)
+ {
+ /* Wait and ensure that WAIT_TIMEOUT is returned */
+ dwRet = WaitForSingleObject(hThread,1000);
+
+ if (dwRet != WAIT_TIMEOUT)
+ {
+ Trace("SuspendThreadTest:WaitForSingleObject "
+ "failed (%x)\n",GetLastError());
+ }
+ else
+ {
+ /* Suspend the thread */
+ dwRet = SuspendThread(hThread);
+
+ if (dwRet != 0)
+ {
+ Trace("SuspendThreadTest:SuspendThread "
+ "failed (%x)\n",GetLastError());
+ }
+ else
+ {
+ /* now check parameter, it should be greater than 0 */
+ if (dwSuspendThreadTestCounter == 0)
+ {
+ Trace("SuspendThreadTest:parameter error\n");
+ }
+ else
+ {
+
+ /* Initialize CONTEXT variables before testing */
+ memset(&savedContext, 0, sizeof(CONTEXT));
+ savedContext.ContextFlags = CONTEXT_FULL;
+ memset(&verifyContext, 0, sizeof(CONTEXT));
+ verifyContext.ContextFlags = CONTEXT_FULL;
+
+ /* Read the suspended thread's context using saveContext. */
+ dwRet = GetThreadContext(hThread, &savedContext);
+ if (dwRet != 1)
+ {
+ Fail("GetThreadContext failed for savedContext: %d (%d)\n", dwRet, GetLastError());
+ }
+
+ /* Make sure GeThreadContext dose get some non-zero values in registers */
+ dwRet = memcmp(&savedContext, &verifyContext, sizeof(CONTEXT));
+ if (dwRet == 0)
+ Fail("GetThreadContext for savedContext do not return meaningful values: (%d)\n", dwRet);
+
+ /* Copy savedContext to modifiedContext before modification */
+ memcpy(&modifiedContext, &savedContext, sizeof(CONTEXT));
+
+ ModifyContext(&modifiedContext);
+
+ /* Modify the suspended thread's context using modifiedContext. */
+ dwRet = SetThreadContext(hThread, &modifiedContext);
+ if (dwRet != 1)
+ {
+ Fail("SetThreadContext failed for modifiedContext: %d (%d)\n", dwRet, GetLastError());
+ }
+
+ /* Read the suspended thread's context again using verifyContext. */
+ dwRet = GetThreadContext(hThread, &verifyContext);
+ if (dwRet != 1)
+ {
+ Fail("GetThreadContext failed for verifyContext: %d (%d)\n", dwRet, GetLastError());
+ }
+
+ /* modifiedContext and verifyContext should be the same for suspended thread. */
+ dwRet = memcmp(&modifiedContext, &verifyContext, sizeof(CONTEXT));
+ if (dwRet != 0)
+ Fail("modifiedContext and verifyContext do not match: (%d)\n", dwRet);
+
+ /* Restore the suspended thread's context. */
+ dwRet = SetThreadContext(hThread, &savedContext);
+ if (dwRet != 1)
+ {
+ Fail("SetThreadContext failed for savedContext: %d (%d)\n", dwRet, GetLastError());
+ }
+
+ /* Save the counter */
+ dwRet = dwSuspendThreadTestCounter;
+
+ /* Wait a second */
+ Sleep(1000);
+
+ /* Ensure the counter hasn't changed becuase the
+ thread was suspended.
+ */
+
+ if (dwSuspendThreadTestCounter != dwRet)
+ {
+ Trace("1SuspendThreadTest:parameter error\n");
+ }
+ else
+ {
+ /* Resume the thread */
+ dwRet = ResumeThread(hThread);
+
+ if (dwRet != 1)
+ {
+ Trace("SuspendThreadTest:ResumeThread "
+ "failed (%x)\n",GetLastError());
+ }
+ else
+ {
+ dwSuspendThreadTestParameter = 0;
+
+ /* set thread to exit and wait */
+ dwRet = WaitForSingleObject(hThread,INFINITE);
+
+ if (dwRet != WAIT_OBJECT_0)
+ {
+ Trace("SuspendThreadTest:WaitForSingleObject"
+ " failed (%x)\n",GetLastError());
+ }
+ else
+ {
+ bRet = TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Trace("SuspendThreadTest:CreateThread failed (%x)\n",GetLastError());
+ }
+
+ return bRet;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+#if defined(PLATFORM_UNIX) && (defined(_X86_) || defined(_IA64_) || defined(_SPARC_))
+ /* Cannot do setthreadcontext test on FreeBSD and HPUX/IA64 when
+ both calling thread and the suspended thread are in the same process.
+ SetThreadContext is disabled on Solaris due to a threading library issue
+ on Solaris 8. See VSWhidbey 343949 for details.*/
+#else
+ if(!SuspendThreadTest())
+ {
+ Fail ("Test failed\n");
+ }
+#endif
+
+ PAL_Terminate();
+ return (PASS);
+
+}
diff --git a/src/pal/tests/palsuite/threading/setthreadcontext/test1/testinfo.dat b/src/pal/tests/palsuite/threading/setthreadcontext/test1/testinfo.dat
new file mode 100644
index 0000000000..4827213066
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/setthreadcontext/test1/testinfo.dat
@@ -0,0 +1,15 @@
+#
+# Copyright (c) Microsoft Corporation. All rights reserved.
+#
+
+Version = 1.0
+Section = threading
+Function = SetThreadContext
+Name = Positive Test for SetThreadContext
+TYPE = DEFAULT
+EXE1 = test1
+Description
+= Purpose: Test for SetThreadContext. Create a thread, which is a
+= function that is counting. Then suspend it, and then make sure
+= getThreadContext and setThreadContext can run.
+= This test case is modified from suspendthread test1 test case.