1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*=====================================================================
6**
7** Source: test2.c
8**
9** Purpose: Tests that a child thread in the middle of a SleepEx call will be
10** interrupted by QueueUserAPC if the alert flag was set.
11**
12**
13**===================================================================*/
14
15#include <palsuite.h>
16
17const int ChildThreadSleepTime = 2000;
18const int InterruptTime = 1000;
19/* We need to keep in mind that BSD has a timer resolution of 10ms, so
20 we need to adjust our delta to keep that in mind. Besides we need some
21 tolerance to account for different scheduling strategies, heavy load
22 scenarios, etc.
23
24 Real-world data also tells us we can expect a big difference between
25 values when run on real iron vs run in a hypervisor.
26
27 Thread-interruption times when run on bare metal will typically yield
28 around 0ms on Linux and between 0 and 16ms on FreeBSD. However, when run
29 in a hypervisor (like VMWare ESXi) we may get values around an order of
30 magnitude higher, up to 110 ms for some tests.
31*/
32const DWORD AcceptableDelta = 150;
33
34const int Iterations = 5;
35
36void RunTest(BOOL AlertThread);
37VOID PALAPI APCFunc(ULONG_PTR dwParam);
38DWORD PALAPI SleeperProc(LPVOID lpParameter);
39
40DWORD ThreadSleepDelta;
41
42int __cdecl main( int argc, char **argv )
43{
44 int i;
45 DWORD dwAvgDelta;
46
47 if (0 != (PAL_Initialize(argc, argv)))
48 {
49 return FAIL;
50 }
51
52 /*
53 On some platforms (e.g. FreeBSD 4.9) the first call to some synch objects
54 (such as conditions) involves some pthread internal initialization that
55 can make the first wait slighty longer, potentially going above the
56 acceptable delta for this test. Let's add a dummy wait to preinitialize
57 internal structures
58 */
59 Sleep(100);
60
61 /*
62 * Check that Queueing an APC in the middle of a sleep does interrupt
63 * it, if it's in an alertable state.
64 */
65 dwAvgDelta = 0;
66 for (i=0;i<Iterations;i++)
67 {
68 RunTest(TRUE);
69 dwAvgDelta += ThreadSleepDelta - InterruptTime;
70 }
71 dwAvgDelta /= Iterations;
72
73 if (dwAvgDelta > AcceptableDelta)
74 {
75 Fail("Expected thread to sleep for %d ms (and get interrupted).\n"
76 "Average delta: %u ms, acceptable delta: %u\n",
77 InterruptTime, dwAvgDelta, AcceptableDelta);
78 }
79
80 /*
81 * Check that Queueing an APC in the middle of a sleep does NOT interrupt
82 * it, if it is not in an alertable state.
83 */
84 dwAvgDelta = 0;
85 for (i=0;i<Iterations;i++)
86 {
87 RunTest(FALSE);
88 dwAvgDelta += ThreadSleepDelta - ChildThreadSleepTime;
89 }
90 dwAvgDelta /= Iterations;
91
92 if (dwAvgDelta > AcceptableDelta)
93 {
94 Fail("Expected thread to sleep for %d ms (and not be interrupted).\n"
95 "Average delta: %u ms, acceptable delta: %u\n",
96 ChildThreadSleepTime, dwAvgDelta, AcceptableDelta);
97 }
98
99 PAL_Terminate();
100 return PASS;
101}
102
103void RunTest(BOOL AlertThread)
104{
105 HANDLE hThread = 0;
106 DWORD dwThreadId = 0;
107 int ret;
108
109 hThread = CreateThread( NULL,
110 0,
111 (LPTHREAD_START_ROUTINE)SleeperProc,
112 (LPVOID) AlertThread,
113 0,
114 &dwThreadId);
115
116 if (hThread == NULL)
117 {
118 Fail("ERROR: Was not able to create the thread to test SleepEx!\n"
119 "GetLastError returned %d\n", GetLastError());
120 }
121
122 if (SleepEx(InterruptTime, FALSE) != 0)
123 {
124 Fail("The creating thread did not sleep!\n");
125 }
126
127 ret = QueueUserAPC(APCFunc, hThread, 0);
128 if (ret == 0)
129 {
130 Fail("QueueUserAPC failed! GetLastError returned %d\n", GetLastError());
131 }
132
133 ret = WaitForSingleObject(hThread, INFINITE);
134 if (ret == WAIT_FAILED)
135 {
136 Fail("Unable to wait on child thread!\nGetLastError returned %d.",
137 GetLastError());
138 }
139}
140
141/* Function doesn't do anything, just needed to interrupt SleepEx */
142VOID PALAPI APCFunc(ULONG_PTR dwParam)
143{
144
145}
146
147/* Entry Point for child thread. */
148DWORD PALAPI SleeperProc(LPVOID lpParameter)
149{
150 UINT64 OldTimeStamp;
151 UINT64 NewTimeStamp;
152 BOOL Alertable;
153 DWORD ret;
154
155 Alertable = (BOOL) lpParameter;
156
157 LARGE_INTEGER performanceFrequency;
158 if (!QueryPerformanceFrequency(&performanceFrequency))
159 {
160 return FAIL;
161 }
162
163 OldTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
164
165 ret = SleepEx(ChildThreadSleepTime, Alertable);
166
167 NewTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
168
169 if (Alertable && ret != WAIT_IO_COMPLETION)
170 {
171 Fail("Expected the interrupted sleep to return WAIT_IO_COMPLETION.\n"
172 "Got %d\n", ret);
173 }
174 else if (!Alertable && ret != 0)
175 {
176 Fail("Sleep did not timeout. Expected return of 0, got %d.\n", ret);
177 }
178
179
180 ThreadSleepDelta = NewTimeStamp - OldTimeStamp;
181
182 return 0;
183}
184