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: test1.c
8**
9** Purpose: Tests that APCs sent to a thread in an alertable state via
10** QueueUserAPC are executed in FIFO order. Also tests that the APC
11** function is executed within the context of the correct thread and
12** that the dwData parameter gets sent correctly.
13**
14**
15**===================================================================*/
16
17#include <palsuite.h>
18
19const int ChildThreadSleepTime = 2000;
20const int InterruptTime = 1000;
21
22VOID PALAPI APCFuncA(ULONG_PTR dwParam);
23VOID PALAPI APCFuncB(ULONG_PTR dwParam);
24VOID PALAPI APCFuncC(ULONG_PTR dwParam);
25VOID PALAPI APCFuncD(ULONG_PTR dwParam);
26DWORD PALAPI SleeperProc(LPVOID lpParameter);
27
28const char *ExpectedResults = "A0B0C0D0A1B1C1D1A2B2C2D2A3B3C3D3";
29char ResultBuffer[256];
30char *ResultPtr;
31DWORD ChildThread;
32
33/* synchronization events */
34static HANDLE hSyncEvent1 = NULL;
35static HANDLE hSyncEvent2 = NULL;
36
37/* thread result because we have no GetExitCodeThread() API */
38BOOL bThreadResult = FAIL;
39
40int __cdecl main (int argc, char **argv)
41{
42 HANDLE hThread = NULL;
43 int ret;
44 int i,j;
45 BOOL bResult = FAIL;
46
47 PAPCFUNC APCFuncs[] =
48 {
49 APCFuncA,
50 APCFuncB,
51 APCFuncC,
52 APCFuncD,
53 };
54
55 /* initialize the PAL */
56 if (0 != (PAL_Initialize(argc, argv)))
57 {
58 return FAIL;
59 }
60
61 ResultPtr = ResultBuffer;
62
63 /* create a pair of synchronization events to coordinate our threads */
64 hSyncEvent1 = CreateEvent( NULL, FALSE, FALSE, NULL );
65 if( hSyncEvent1 == NULL )
66 {
67 Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
68 goto cleanup;
69 }
70
71 hSyncEvent2 = CreateEvent( NULL, FALSE, FALSE, NULL );
72 if( hSyncEvent2 == NULL )
73 {
74 Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
75 goto cleanup;
76 }
77
78 /* create a child thread which will call SleepEx */
79 hThread = CreateThread( NULL,
80 0,
81 (LPTHREAD_START_ROUTINE)SleeperProc,
82 0,
83 0,
84 &ChildThread);
85
86 if( hThread == NULL )
87 {
88 Trace( "ERROR:%lu:CreateThread() call failed\n",
89 GetLastError());
90 goto cleanup;
91 }
92
93
94 /* wait on our synchronization event to ensure the thread is running */
95 ret = WaitForSingleObject( hSyncEvent1, 20000 );
96 if( ret != WAIT_OBJECT_0 )
97 {
98 Trace( "ERROR:WaitForSingleObject() returned %lu, "
99 "expected WAIT_OBJECT_0\n",
100 ret );
101 goto cleanup;
102 }
103
104
105 /* queue our user APC functions on the thread */
106 for (i=0; i<4; i++)
107 {
108 for (j=0; j<sizeof(APCFuncs)/sizeof(APCFuncs[0]); j++)
109 {
110 ret = QueueUserAPC(APCFuncs[j], hThread, '0' + i);
111 if (ret == 0)
112 {
113 Trace( "ERROR:%lu:QueueUserAPC() call failed\n",
114 GetLastError());
115 goto cleanup;
116 }
117 }
118 }
119
120 /* signal the child thread to continue */
121 if( ! SetEvent( hSyncEvent2 ) )
122 {
123 Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
124 goto cleanup;
125 }
126
127
128 /* wait on our synchronization event to ensure the other thread is done */
129 ret = WaitForSingleObject( hSyncEvent1, 20000 );
130 if( ret != WAIT_OBJECT_0 )
131 {
132 Trace( "ERROR:WaitForSingleObject() returned %lu, "
133 "expected WAIT_OBJECT_0\n",
134 ret );
135 goto cleanup;
136 }
137
138
139 /* check that the thread executed successfully */
140 if( bThreadResult == FAIL )
141 {
142 goto cleanup;
143 }
144
145
146 /* check the result buffer */
147 if (strcmp(ExpectedResults, ResultBuffer) != 0)
148 {
149 Trace( "FAIL:Expected the APC function calls to produce a result of "
150 " \"%s\", got \"%s\"\n",
151 ExpectedResults,
152 ResultBuffer );
153 goto cleanup;
154 }
155
156 /* success if we get here */
157 bResult = PASS;
158
159cleanup:
160 /* wait for the other thread to finish */
161 if( hThread != NULL )
162 {
163 ret = WaitForSingleObject( hThread, INFINITE );
164 if (ret == WAIT_FAILED)
165 {
166 Trace( "ERROR:%lu:WaitForSingleObject() returned %lu, "
167 "expected WAIT_OBJECT_0\n",
168 ret );
169 bResult = FAIL;
170 }
171 }
172
173 /* close our synchronization handles */
174 if( hSyncEvent1 != NULL )
175 {
176 if( ! CloseHandle( hSyncEvent1 ) )
177 {
178 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
179 bResult = FAIL;
180 }
181 }
182
183 if( hSyncEvent2 != NULL )
184 {
185 if( ! CloseHandle( hSyncEvent2 ) )
186 {
187 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
188 bResult = FAIL;
189 }
190 }
191
192 if( bResult == FAIL )
193 {
194 Fail( "test failed\n" );
195 }
196
197 /* terminate the PAL */
198 PAL_Terminate();
199
200 /* return success */
201 return PASS;
202}
203
204VOID PALAPI APCFuncA(ULONG_PTR dwParam)
205{
206 char val = (int) dwParam;
207
208 if (GetCurrentThreadId() != ChildThread)
209 {
210 Fail("Executing APC in thread %d, should be in %d!\n",
211 GetCurrentThreadId(), ChildThread);
212 }
213
214 *ResultPtr++ = 'A';
215 *ResultPtr++ = val;
216 *ResultPtr = 0;
217}
218
219VOID PALAPI APCFuncB(ULONG_PTR dwParam)
220{
221 char val = (int) dwParam;
222
223 if (GetCurrentThreadId() != ChildThread)
224 {
225 Fail("Executing APC in thread %d, should be in %d!\n",
226 GetCurrentThreadId(), ChildThread);
227 }
228
229 *ResultPtr++ = 'B';
230 *ResultPtr++ = val;
231 *ResultPtr = 0;
232}
233
234VOID PALAPI APCFuncC(ULONG_PTR dwParam)
235{
236 char val = (int) dwParam;
237
238 if (GetCurrentThreadId() != ChildThread)
239 {
240 Fail("Executing APC in thread %d, should be in %d!\n",
241 GetCurrentThreadId(), ChildThread);
242 }
243
244 *ResultPtr++ = 'C';
245 *ResultPtr++ = val;
246 *ResultPtr = 0;
247}
248
249VOID PALAPI APCFuncD(ULONG_PTR dwParam)
250{
251 char val = (int) dwParam;
252
253 if (GetCurrentThreadId() != ChildThread)
254 {
255 Fail("Executing APC in thread %d, should be in %d!\n",
256 GetCurrentThreadId(), ChildThread);
257 }
258
259 *ResultPtr++ = 'D';
260 *ResultPtr++ = val;
261 *ResultPtr = 0;
262}
263
264/* Entry Point for child thread. All it does is call SleepEx. */
265DWORD PALAPI SleeperProc(LPVOID lpParameter)
266{
267 DWORD ret;
268
269 /* signal the main thread that we're ready to proceed */
270 if( ! SetEvent( hSyncEvent1 ) )
271 {
272 Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
273 bThreadResult = FAIL;
274 goto done;
275 }
276
277 /* wait for notification from the main thread */
278 ret = WaitForSingleObject( hSyncEvent2, 20000 );
279 if( ret != WAIT_OBJECT_0 )
280 {
281 Trace( "ERROR:WaitForSingleObject() returned %lu, "
282 "expected WAIT_OBJECT_0\n",
283 ret );
284 bThreadResult = FAIL;
285 goto done;
286 }
287
288 /* call SleepEx to activate any queued APCs */
289 ret = SleepEx(ChildThreadSleepTime, TRUE);
290 if (ret != WAIT_IO_COMPLETION)
291 {
292 Trace( "ERROR:SleepEx() call returned %lu, "
293 "expected WAIT_IO_COMPLETION\n",
294 ret );
295 bThreadResult = FAIL;
296 goto done;
297 }
298
299 /* everything passed here */
300 bThreadResult = PASS;
301
302
303done:
304 /* signal the main thread that we're finished */
305 if( ! SetEvent( hSyncEvent1 ) )
306 {
307 Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
308 bThreadResult = FAIL;
309 }
310
311 /* return success or failure */
312 return bThreadResult;
313}
314