summaryrefslogtreecommitdiff
path: root/src/pal/tests/palsuite/threading/SleepEx/test2/test2.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/tests/palsuite/threading/SleepEx/test2/test2.c')
-rw-r--r--src/pal/tests/palsuite/threading/SleepEx/test2/test2.c183
1 files changed, 183 insertions, 0 deletions
diff --git a/src/pal/tests/palsuite/threading/SleepEx/test2/test2.c b/src/pal/tests/palsuite/threading/SleepEx/test2/test2.c
new file mode 100644
index 0000000000..c2ba4e704d
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/SleepEx/test2/test2.c
@@ -0,0 +1,183 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=====================================================================
+**
+** Source: test2.c
+**
+** Purpose: Tests that a child thread in the middle of a SleepEx call will be
+** interrupted by QueueUserAPC if the alert flag was set.
+**
+**
+**===================================================================*/
+
+#include <palsuite.h>
+
+const int ChildThreadSleepTime = 2000;
+const int InterruptTime = 1000;
+/* We need to keep in mind that BSD has a timer resolution of 10ms, so
+ we need to adjust our delta to keep that in mind. Besides we need some
+ tolerance to account for different scheduling strategies, heavy load
+ scenarios, etc.
+
+ Real-world data also tells us we can expect a big difference between
+ values when run on real iron vs run in a hypervisor.
+
+ Thread-interruption times when run on bare metal will typically yield
+ around 0ms on Linux and between 0 and 16ms on FreeBSD. However, when run
+ in a hypervisor (like VMWare ESXi) we may get values around an order of
+ magnitude higher, up to 110 ms for some tests.
+*/
+const DWORD AcceptableDelta = 150;
+
+const int Iterations = 5;
+
+void RunTest(BOOL AlertThread);
+VOID PALAPI APCFunc(ULONG_PTR dwParam);
+DWORD PALAPI SleeperProc(LPVOID lpParameter);
+
+DWORD ThreadSleepDelta;
+
+int __cdecl main( int argc, char **argv )
+{
+ int i;
+ DWORD dwAvgDelta;
+
+ if (0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ /*
+ On some platforms (e.g. FreeBSD 4.9) the first call to some synch objects
+ (such as conditions) involves some pthread internal initialization that
+ can make the first wait slighty longer, potentially going above the
+ acceptable delta for this test. Let's add a dummy wait to preinitialize
+ internal structures
+ */
+ Sleep(100);
+
+ /*
+ * Check that Queueing an APC in the middle of a sleep does interrupt
+ * it, if it's in an alertable state.
+ */
+ dwAvgDelta = 0;
+ for (i=0;i<Iterations;i++)
+ {
+ RunTest(TRUE);
+ dwAvgDelta += ThreadSleepDelta - InterruptTime;
+ }
+ dwAvgDelta /= Iterations;
+
+ if (dwAvgDelta > AcceptableDelta)
+ {
+ Fail("Expected thread to sleep for %d ms (and get interrupted).\n"
+ "Average delta: %u ms, acceptable delta: %u\n",
+ InterruptTime, dwAvgDelta, AcceptableDelta);
+ }
+
+ /*
+ * Check that Queueing an APC in the middle of a sleep does NOT interrupt
+ * it, if it is not in an alertable state.
+ */
+ dwAvgDelta = 0;
+ for (i=0;i<Iterations;i++)
+ {
+ RunTest(FALSE);
+ dwAvgDelta += ThreadSleepDelta - ChildThreadSleepTime;
+ }
+ dwAvgDelta /= Iterations;
+
+ if (dwAvgDelta > AcceptableDelta)
+ {
+ Fail("Expected thread to sleep for %d ms (and not be interrupted).\n"
+ "Average delta: %u ms, acceptable delta: %u\n",
+ ChildThreadSleepTime, dwAvgDelta, AcceptableDelta);
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
+void RunTest(BOOL AlertThread)
+{
+ HANDLE hThread = 0;
+ DWORD dwThreadId = 0;
+ int ret;
+
+ hThread = CreateThread( NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)SleeperProc,
+ (LPVOID) AlertThread,
+ 0,
+ &dwThreadId);
+
+ if (hThread == NULL)
+ {
+ Fail("ERROR: Was not able to create the thread to test SleepEx!\n"
+ "GetLastError returned %d\n", GetLastError());
+ }
+
+ if (SleepEx(InterruptTime, FALSE) != 0)
+ {
+ Fail("The creating thread did not sleep!\n");
+ }
+
+ ret = QueueUserAPC(APCFunc, hThread, 0);
+ if (ret == 0)
+ {
+ Fail("QueueUserAPC failed! GetLastError returned %d\n", GetLastError());
+ }
+
+ ret = WaitForSingleObject(hThread, INFINITE);
+ if (ret == WAIT_FAILED)
+ {
+ Fail("Unable to wait on child thread!\nGetLastError returned %d.",
+ GetLastError());
+ }
+}
+
+/* Function doesn't do anything, just needed to interrupt SleepEx */
+VOID PALAPI APCFunc(ULONG_PTR dwParam)
+{
+
+}
+
+/* Entry Point for child thread. */
+DWORD PALAPI SleeperProc(LPVOID lpParameter)
+{
+ UINT64 OldTimeStamp;
+ UINT64 NewTimeStamp;
+ BOOL Alertable;
+ DWORD ret;
+
+ Alertable = (BOOL) lpParameter;
+
+ LARGE_INTEGER performanceFrequency;
+ if (!QueryPerformanceFrequency(&performanceFrequency))
+ {
+ return FAIL;
+ }
+
+ OldTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+ ret = SleepEx(ChildThreadSleepTime, Alertable);
+
+ NewTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
+
+ if (Alertable && ret != WAIT_IO_COMPLETION)
+ {
+ Fail("Expected the interrupted sleep to return WAIT_IO_COMPLETION.\n"
+ "Got %d\n", ret);
+ }
+ else if (!Alertable && ret != 0)
+ {
+ Fail("Sleep did not timeout. Expected return of 0, got %d.\n", ret);
+ }
+
+
+ ThreadSleepDelta = NewTimeStamp - OldTimeStamp;
+
+ return 0;
+}