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
10** WaitForMultipleObjectsEx call will be interrupted by QueueUserAPC
11** if the alert flag was set.
12**
13**
14**===================================================================*/
15
16#include <palsuite.h>
17
18/* Based on SleepEx/test2 */
19
20const unsigned int ChildThreadWaitTime = 1000;
21const unsigned int InterruptTime = 500;
22
23#define TOLERANCE 10
24
25void RunTest(BOOL AlertThread);
26VOID PALAPI APCFunc(ULONG_PTR dwParam);
27DWORD PALAPI WaiterProc(LPVOID lpParameter);
28
29DWORD ThreadWaitDelta;
30
31int __cdecl main( int argc, char **argv )
32{
33
34 DWORD delta = 0;
35
36 if (0 != (PAL_Initialize(argc, argv)))
37 {
38 return FAIL;
39 }
40
41 /*
42 On some platforms (e.g. FreeBSD 4.9) the first call to some synch objects
43 (such as conditions) involves some pthread internal initialization that
44 can make the first wait slighty longer, potentially going above the
45 acceptable delta for this test. Let's add a dummy wait to preinitialize
46 internal structures
47 */
48 Sleep(100);
49
50
51 /*
52 * Check that Queueing an APC in the middle of a wait does interrupt
53 * it, if it's in an alertable state.
54 */
55 RunTest(TRUE);
56 // Make sure that the wait returns in time greater than interrupt and less than
57 // wait timeout
58 if (
59 ((ThreadWaitDelta >= ChildThreadWaitTime) && (ThreadWaitDelta - ChildThreadWaitTime) > TOLERANCE)
60 || (( ThreadWaitDelta < InterruptTime) && (ThreadWaitDelta - InterruptTime) > TOLERANCE)
61 )
62 {
63 Fail("Expected thread to wait for %d ms (and get interrupted).\n"
64 "Interrupt Time: %d ms, ThreadWaitDelta %u\n",
65 ChildThreadWaitTime, InterruptTime, ThreadWaitDelta);
66 }
67
68 /*
69 * Check that Queueing an APC in the middle of a wait does NOT interrupt
70 * it, if it is not in an alertable state.
71 */
72 RunTest(FALSE);
73
74 // Make sure that time taken for thread to return from wait is more than interrupt
75 // and also not less than the complete child thread wait time
76
77 delta = ThreadWaitDelta - ChildThreadWaitTime;
78 if( (ThreadWaitDelta < ChildThreadWaitTime) && ( delta > TOLERANCE) )
79 {
80 Fail("Expected thread to wait for %d ms (and not get interrupted).\n"
81 "Interrupt Time: %d ms, ThreadWaitDelta %u\n",
82 ChildThreadWaitTime, InterruptTime, ThreadWaitDelta);
83 }
84
85
86 PAL_Terminate();
87 return PASS;
88}
89
90void RunTest(BOOL AlertThread)
91{
92 HANDLE hThread = 0;
93 DWORD dwThreadId = 0;
94 int ret;
95
96 hThread = CreateThread( NULL,
97 0,
98 (LPTHREAD_START_ROUTINE)WaiterProc,
99 (LPVOID) AlertThread,
100 0,
101 &dwThreadId);
102
103 if (hThread == NULL)
104 {
105 Fail("ERROR: Was not able to create the thread to test!\n"
106 "GetLastError returned %d\n", GetLastError());
107 }
108
109 Sleep(InterruptTime);
110
111 ret = QueueUserAPC(APCFunc, hThread, 0);
112 if (ret == 0)
113 {
114 Fail("QueueUserAPC failed! GetLastError returned %d\n",
115 GetLastError());
116 }
117
118 ret = WaitForSingleObject(hThread, INFINITE);
119 if (ret == WAIT_FAILED)
120 {
121 Fail("Unable to wait on child thread!\nGetLastError returned %d.\n",
122 GetLastError());
123 }
124}
125
126/* Function doesn't do anything, just needed to interrupt the wait*/
127VOID PALAPI APCFunc(ULONG_PTR dwParam)
128{
129}
130
131/* Entry Point for child thread. */
132DWORD PALAPI WaiterProc(LPVOID lpParameter)
133{
134 HANDLE Semaphore;
135 UINT64 OldTimeStamp;
136 UINT64 NewTimeStamp;
137 BOOL Alertable;
138 DWORD ret;
139
140 /* Create a semaphore that is not in the signalled state */
141 Semaphore = CreateSemaphoreW(NULL, 0, 1, NULL);
142
143 if (Semaphore == NULL)
144 {
145 Fail("Failed to create semaphore! GetLastError returned %d.\n",
146 GetLastError());
147 }
148
149 Alertable = (BOOL) lpParameter;
150
151 LARGE_INTEGER performanceFrequency;
152 if (!QueryPerformanceFrequency(&performanceFrequency))
153 {
154 Fail("Failed to query performance frequency!");
155 }
156
157 OldTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
158
159 ret = WaitForMultipleObjectsEx(1, &Semaphore, FALSE, ChildThreadWaitTime,
160 Alertable);
161
162 NewTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
163
164
165 if (Alertable && ret != WAIT_IO_COMPLETION)
166 {
167 Fail("Expected the interrupted wait to return WAIT_IO_COMPLETION.\n"
168 "Got %d\n", ret);
169 }
170 else if (!Alertable && ret != WAIT_TIMEOUT)
171 {
172 Fail("WaitForMultipleObjectsEx did not timeout.\n"
173 "Expected return of WAIT_TIMEOUT, got %d.\n", ret);
174 }
175
176 ThreadWaitDelta = NewTimeStamp - OldTimeStamp;
177
178 ret = CloseHandle(Semaphore);
179 if (!ret)
180 {
181 Fail("Unable to close handle to semaphore!\n"
182 "GetLastError returned %d\n", GetLastError());
183 }
184
185 return 0;
186}
187
188
189