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: WFSOExThreadTest.c
8**
9** Purpose: Tests a child thread in the middle of a
10** WaitForSingleObjectEx 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 int ChildThreadWaitTime = 4000;
21const int InterruptTime = 2000;
22const DWORD AcceptableDelta = 300;
23
24void RunTest(BOOL AlertThread);
25VOID PALAPI APCFunc(ULONG_PTR dwParam);
26DWORD PALAPI WaiterProc(LPVOID lpParameter);
27void WorkerThread(void);
28
29int ThreadWaitDelta;
30
31int __cdecl main( int argc, char **argv )
32{
33 if (0 != (PAL_Initialize(argc, argv)))
34 {
35 return FAIL;
36 }
37
38 /*
39 On some platforms (e.g. FreeBSD 4.9) the first call to some synch objects
40 (such as conditions) involves some pthread internal initialization that
41 can make the first wait slighty longer, potentially going above the
42 acceptable delta for this test. Let's add a dummy wait to preinitialize
43 internal structures
44 */
45 Sleep(100);
46
47 /*
48 * Check that Queueing an APC in the middle of a wait does interrupt
49 * it, if it's in an alertable state.
50 */
51
52 RunTest(TRUE);
53 if (abs(ThreadWaitDelta - InterruptTime) > AcceptableDelta)
54 {
55 Fail("Expected thread to wait for %d ms (and get interrupted).\n"
56 "Thread waited for %d ms! (Acceptable delta: %d)\n",
57 InterruptTime, ThreadWaitDelta, AcceptableDelta);
58 }
59
60
61 /*
62 * Check that Queueing an APC in the middle of a wait does NOT interrupt
63 * it, if it is not in an alertable state.
64 */
65 RunTest(FALSE);
66 if (abs(ThreadWaitDelta - ChildThreadWaitTime) > AcceptableDelta)
67 {
68 Fail("Expected thread to wait for %d ms (and not be interrupted).\n"
69 "Thread waited for %d ms! (Acceptable delta: %d)\n",
70 ChildThreadWaitTime, ThreadWaitDelta, AcceptableDelta);
71 }
72
73
74 PAL_Terminate();
75 return PASS;
76}
77
78void RunTest(BOOL AlertThread)
79{
80 HANDLE hThread = 0;
81 DWORD dwThreadId = 0;
82 int ret;
83
84 //Create thread
85 hThread = CreateThread( NULL,
86 0,
87 (LPTHREAD_START_ROUTINE)WaiterProc,
88 (LPVOID) AlertThread,
89 0,
90 &dwThreadId);
91
92 if (hThread == NULL)
93 {
94 Fail("ERROR: Was not able to create the thread to test!\n"
95 "GetLastError returned %d\n", GetLastError());
96 }
97
98 Sleep(InterruptTime);
99
100 ret = QueueUserAPC(APCFunc, hThread, 0);
101 if (ret == 0)
102 {
103 Fail("QueueUserAPC failed! GetLastError returned %d\n",
104 GetLastError());
105 }
106
107
108 ret = WaitForSingleObject(hThread, INFINITE);
109 if (ret == WAIT_FAILED)
110 {
111 Fail("Unable to wait on child thread!\nGetLastError returned %d.\n",
112 GetLastError());
113 }
114
115 if (0==CloseHandle(hThread))
116 {
117 Trace("Could not close Thread handle\n");
118 Fail ( "GetLastError returned %d\n", GetLastError());
119 }
120}
121
122/* Function doesn't do anything, just needed to interrupt the wait*/
123VOID PALAPI APCFunc(ULONG_PTR dwParam)
124{
125}
126
127/* Entry Point for child thread. */
128DWORD PALAPI WaiterProc(LPVOID lpParameter)
129{
130 HANDLE hWaitThread;
131 UINT64 OldTimeStamp;
132 UINT64 NewTimeStamp;
133 BOOL Alertable;
134 DWORD ret;
135 DWORD dwThreadId = 0;
136
137/*
138When a thread terminates, the thread object attains a signaled state,
139satisfying any threads that were waiting on the object.
140*/
141
142/* Create a thread that does not return immediately to maintain a non signaled test*/
143 hWaitThread = CreateThread( NULL,
144 0,
145 (LPTHREAD_START_ROUTINE)WorkerThread,
146 NULL,
147 0,
148 &dwThreadId);
149
150 if (hWaitThread == NULL)
151 {
152 Fail("ERROR: Was not able to create worker thread to wait on!\n"
153 "GetLastError returned %d\n", GetLastError());
154 }
155
156 Alertable = (BOOL) lpParameter;
157
158 LARGE_INTEGER performanceFrequency;
159 if (!QueryPerformanceFrequency(&performanceFrequency))
160 {
161 Fail("Failed to query performance frequency!");
162 }
163
164 OldTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
165
166 ret = WaitForSingleObjectEx( hWaitThread,
167 ChildThreadWaitTime,
168 Alertable);
169
170 NewTimeStamp = GetHighPrecisionTimeStamp(performanceFrequency);
171
172
173 if (Alertable && ret != WAIT_IO_COMPLETION)
174 {
175 Fail("Expected the interrupted wait to return WAIT_IO_COMPLETION.\n"
176 "Got %d\n", ret);
177 }
178 else if (!Alertable && ret != WAIT_TIMEOUT)
179 {
180 Fail("WaitForSingleObjectEx did not timeout.\n"
181 "Expected return of WAIT_TIMEOUT, got %d.\n", ret);
182 }
183
184 ThreadWaitDelta = NewTimeStamp - OldTimeStamp;
185
186 ret = CloseHandle(hWaitThread);
187 if (!ret)
188 {
189 Fail("Unable to close handle to Thread!\n"
190 "GetLastError returned %d\n", GetLastError());
191 }
192
193 return 0;
194}
195
196
197void WorkerThread(void)
198{
199
200 //Make the worker thread sleep to test WFSOEx Functionality
201
202 Sleep(2*ChildThreadWaitTime);
203}
204
205