summaryrefslogtreecommitdiff
path: root/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.cpp')
-rw-r--r--src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.cpp506
1 files changed, 506 insertions, 0 deletions
diff --git a/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.cpp b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.cpp
new file mode 100644
index 0000000000..a0216494ba
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.cpp
@@ -0,0 +1,506 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================
+**
+** Source: test5.c
+**
+** Purpose: Test the functionality of simultaneously waiting
+** on multiple processes. Create the same number of helper
+** processes and helper threads.
+** Helper threads wait on helper processes to finish.
+** Helper processes wait on the event signal from test
+** thread before exit.
+** The test thread can wake up one helper
+** thread at a time by signaling the corresponding helper
+** process to finish.
+** The test thread can also wake up all helper threads at once
+** by signaling help process 0 to exit.
+**
+**
+**============================================================*/
+
+#define UNICODE
+
+#include "commonconsts.h"
+
+#include <palsuite.h>
+
+/* The maximum number of objects a thread can wait is MAXIMUM_WAIT_OBJECTS.
+ The last helper thread in this test case will wait on all helper processes
+ plus a thread finish event so the maximum number of helper processes
+ can be created in this test case is (MAXIMUM_WAIT_OBJECTS-1). */
+#define MAX_HELPER_PROCESS (MAXIMUM_WAIT_OBJECTS-1)
+
+int MaxNumHelperProcess = MAX_HELPER_PROCESS;
+
+/* indicate how the test thread wake up helper thread. */
+typedef enum _TestCaseType {
+ WakeUpOneThread, /* wake up one helper thread at a time. */
+ WakeUpAllThread /* wake up all helper threads at once */
+} TestCaseType;
+
+TestCaseType TestCase = WakeUpOneThread;
+
+/* When the test thread wakes up one thread at a time,
+ ThreadIndexOfThreadFinishEvent specifies the index of the thread that
+ should be waked up using hThreadFinishEvent instead of helper process. */
+DWORD ThreadIndexOfThreadFinishEvent = 0;
+
+struct helper_process_t
+{
+ PROCESS_INFORMATION pi;
+ HANDLE hProcessReadyEvent;
+ HANDLE hProcessFinishEvent;
+} helper_process[MAX_HELPER_PROCESS];
+
+HANDLE hProcessStartEvent;
+
+struct helper_thread_t
+{
+ HANDLE hThread;
+ DWORD dwThreadId;
+ HANDLE hThreadReadyEvent;
+ HANDLE hThreadFinishEvent;
+} helper_thread[MAX_HELPER_PROCESS];
+
+/*
+ * Entry Point for helper thread.
+ */
+DWORD PALAPI WaitForProcess(LPVOID lpParameter)
+{
+ DWORD index, i;
+ DWORD dwRet;
+ HANDLE handles[MAX_HELPER_PROCESS+1];
+
+ index = (DWORD) lpParameter;
+
+ /* The helper thread 0 will wait for helper process 0, helper thread 1 will
+ wait for helper process 0 and 1, helper thread 2 will wait for helper
+ process 0, 1, and 2, and so on ..., and the last helper thread will wait
+ on all helper processes.
+ Each helper thread also waits on hThreadFinishEvent so that
+ it can exit without waiting on any process to finish. */
+
+ for (i = 0; i <= index; i++)
+ {
+ handles[i] = helper_process[i].pi.hProcess;
+ }
+
+ handles[index+1] = helper_thread[index].hThreadFinishEvent;
+
+ if(!SetEvent(helper_thread[index].hThreadReadyEvent))
+ {
+ Fail("test5.WaitProcess: SetEvent of hThreadReadyEvent failed for thread %d. "
+ "GetLastError() returned %d.\n", index,
+ GetLastError());
+ }
+
+ dwRet = WaitForMultipleObjectsEx(index+2, &handles[0], FALSE, TIMEOUT, TRUE);
+ if (WakeUpAllThread == TestCase)
+ {
+ /* If the test thread signals helper process 0 to exit, all threads will be waked up,
+ and the return value must be (WAIT_OBJECT_0+0) because the handle of helper process 0
+ is in handle[0]. */
+ if (dwRet != (WAIT_OBJECT_0+0))
+ {
+ Fail("test5.WaitForProcess: invalid return value %d for WakupAllThread from WaitForMultipleObjectsEx for thread %d\n"
+ "LastError:(%u)\n",
+ dwRet, index,
+ GetLastError());
+ }
+ }
+ else if (WakeUpOneThread == TestCase)
+ {
+ /* If the test thread wakes up one helper thread at a time,
+ the return value must be either (WAIT_OBJECT_0+index) if the helper thread
+ wakes up because the corresponding help process exits,
+ or (index+1) if the helper thread wakes up because of hThreadReadyEvent. */
+ if ((index != ThreadIndexOfThreadFinishEvent && dwRet != (WAIT_OBJECT_0+index)) ||
+ (index == ThreadIndexOfThreadFinishEvent && dwRet != (index+1)))
+ {
+ Fail("test5.WaitForProcess: invalid return value %d for WakupOneThread from WaitForMultipleObjectsEx for thread %d\n"
+ "LastError:(%u)\n",
+ dwRet, index,
+ GetLastError());
+ }
+ }
+ else
+ {
+ Fail("Unknown TestCase %d\n", TestCase);
+ }
+ return 0;
+}
+
+/*
+ * Setup the helper processes and helper threads.
+ */
+void
+Setup()
+{
+
+ STARTUPINFO si;
+ DWORD dwRet;
+ int i;
+
+ char szEventName[MAX_PATH];
+ PWCHAR uniStringHelper;
+ PWCHAR uniString;
+
+ /* Create the event to start helper process after it was created. */
+ uniString = convert(szcHelperProcessStartEvName);
+ hProcessStartEvent = CreateEvent(NULL, TRUE, FALSE, uniString);
+ free(uniString);
+ if (!hProcessStartEvent)
+ {
+ Fail("test5.Setup: CreateEvent of '%s' failed. "
+ "GetLastError() returned %d.\n", szcHelperProcessStartEvName,
+ GetLastError());
+ }
+
+ /* Create the helper processes. */
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ uniStringHelper = convert("helper");
+ for (i = 0; i < MaxNumHelperProcess; i++)
+ {
+ ZeroMemory( &helper_process[i].pi, sizeof(PROCESS_INFORMATION));
+
+ if(!CreateProcess( NULL, uniStringHelper, NULL, NULL,
+ FALSE, 0, NULL, NULL, &si, &helper_process[i].pi))
+ {
+ Fail("test5.Setup: CreateProcess failed to load executable for helper process %d. "
+ "GetLastError() returned %u.\n",
+ i, GetLastError());
+ }
+
+ /* Create the event to let helper process tell us it is ready. */
+ if (sprintf_s(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 (sprintf_s(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;
+}