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: test7.c
8**
9** Dependencies: PAL_Initialize
10** PAL_Terminate
11** CreateEvent
12** SetEvent
13** CreateThread
14** ResumeThread
15** WaitForMultipleObjectsEx
16** CloseHandle
17**
18** Purpose:
19**
20** Test to ensure proper operation of the QueueUserAPC()
21** API by trying to queue an APC function on a thread and
22** activating it with WaitForMultipleObjectsEx.
23**
24**
25**===========================================================================*/
26#include <palsuite.h>
27
28
29static HANDLE hSyncEvent = NULL;
30static HANDLE hTestEvent = NULL;
31static int nAPCExecuted = 0;
32static BOOL bThreadResult = FALSE;
33
34VOID PALAPI APCFunc( ULONG_PTR dwParam )
35{
36 ++nAPCExecuted;
37}
38
39/**
40 * ThreadFunc
41 *
42 * Dummy thread function for APC queuing.
43 */
44DWORD PALAPI ThreadFunc( LPVOID param )
45{
46 DWORD ret = 0;
47
48 /* pessimism */
49 bThreadResult = FALSE;
50
51 /* set the sync event to notify the main thread */
52 if( ! SetEvent( hSyncEvent ) )
53 {
54 Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
55 goto done;
56 }
57
58 /* wait until the test event is signalled */
59 ret = WaitForSingleObject( hTestEvent, INFINITE );
60 if( ret != WAIT_OBJECT_0 )
61 {
62 Trace( "ERROR:WaitForSingleObject() returned %lu, "
63 "expected WAIT_OBJECT_0\n",
64 ret );
65 goto done;
66 }
67
68 /* now do an alertable wait on the same event, which is now
69 in an unsignalled state */
70 ret = WaitForMultipleObjectsEx( 1, &hTestEvent, TRUE, 2000, TRUE );
71
72 /* verify that we got a WAIT_IO_COMPLETION result */
73 if( ret != WAIT_IO_COMPLETION )
74 {
75 Trace( "ERROR:WaitForMultipleObjectsEx returned %lu, "
76 "expected WAIT_IO_COMPLETION\n",
77 ret );
78 goto done;
79 }
80
81 /* set the event again */
82 if( ! SetEvent( hTestEvent ) )
83 {
84 Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
85 goto done;
86 }
87
88 /* do a non-alertable wait on the same event */
89 ret = WaitForMultipleObjectsEx( 1, &hTestEvent, TRUE, INFINITE, FALSE );
90
91 /* verify that we got a WAIT_OBJECT_0 result */
92 if( ret != WAIT_OBJECT_0 )
93 {
94 Trace( "ERROR:WaitForMultipleObjectsEx returned %lu, "
95 "expected WAIT_OBJECT_0\n",
96 ret );
97 goto done;
98 }
99
100 /* success at this point */
101 bThreadResult = TRUE;
102
103
104done:
105 return bThreadResult;
106}
107
108
109int __cdecl main( int argc, char **argv )
110
111{
112 /* local variables */
113 HANDLE hThread = NULL;
114 DWORD IDThread;
115 DWORD ret;
116 BOOL bResult = FALSE;
117
118 /* PAL initialization */
119 if( (PAL_Initialize(argc, argv)) != 0 )
120 {
121 return( FAIL );
122 }
123
124 /* create an auto-reset event for the other thread to wait on */
125 hTestEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
126 if( hTestEvent == NULL )
127 {
128 Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
129 }
130
131 /* create an auto-reset event for synchronization */
132 hSyncEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
133 if( hSyncEvent == NULL )
134 {
135 Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
136 if( ! CloseHandle( hTestEvent ) )
137 {
138 Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
139 }
140 Fail( "test failed\n" );
141 }
142
143 /* run another dummy thread to cause notification of the library */
144 hThread = CreateThread( NULL, /* no security attributes */
145 0, /* use default stack size */
146 (LPTHREAD_START_ROUTINE) ThreadFunc, /* thread function */
147 (LPVOID) NULL, /* pass thread index as */
148 /* function argument */
149 CREATE_SUSPENDED, /* create suspended */
150 &IDThread ); /* returns thread id */
151
152 /* Check the return value for success. */
153 if( hThread == NULL )
154 {
155 /* error creating thread */
156 Trace( "ERROR:%lu:CreateThread call failed\n", GetLastError() );
157 if( ! CloseHandle( hTestEvent ) )
158 {
159 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
160 }
161 Fail( "test failed\n" );
162 }
163
164 /* Resume the suspended thread */
165 ResumeThread( hThread );
166
167 /* wait until the other thread is ready to proceed */
168 ret = WaitForSingleObject( hSyncEvent, 10000 );
169 if( ret != WAIT_OBJECT_0 )
170 {
171 Trace( "ERROR:WaitForSingleObject returned %lu, "
172 "expected WAIT_OBJECT_0\n",
173 ret );
174 goto cleanup;
175 }
176
177
178 /* now queue our APC on the test thread */
179 ret = QueueUserAPC( APCFunc, hThread, 0 );
180 if( ret == 0 )
181 {
182 Trace( "ERROR:%lu:QueueUserAPC call failed\n", GetLastError() );
183 goto cleanup;
184 }
185
186 /* signal the test event so the other thread will proceed */
187 if( ! SetEvent( hTestEvent ) )
188 {
189 Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
190 goto cleanup;
191 }
192
193 /* wait on the other thread to complete */
194 ret = WaitForSingleObject( hThread, INFINITE );
195 if( ret != WAIT_OBJECT_0 )
196 {
197 Trace( "ERROR:WaitForSingleObject() returned %lu, "
198 "expected WAIT_OBJECT_0\n",
199 ret );
200 goto cleanup;
201 }
202
203 /* check the result of the other thread */
204 if( bThreadResult == FALSE )
205 {
206 goto cleanup;
207 }
208
209 /* check that the APC function was actually executed exactly one time */
210 if( nAPCExecuted != 1 )
211 {
212 Trace( "ERROR:APC function was executed %d times, "
213 "expected once\n", nAPCExecuted );
214 goto cleanup;
215 }
216
217 /* set the success flag */
218 bResult = PASS;
219
220
221cleanup:
222 /* close the global event handles */
223 if( ! CloseHandle( hTestEvent ) )
224 {
225 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
226 bResult = FAIL;
227 }
228
229 if( ! CloseHandle( hSyncEvent ) )
230 {
231 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
232 bResult = FAIL;
233 }
234
235 /* close the thread handle */
236 if( ! CloseHandle( hThread ) )
237 {
238 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
239 bResult = FAIL;
240 }
241
242 /* output final failure result for failure case */
243 if( bResult == FAIL )
244 {
245 Fail( "test failed\n" );
246 }
247
248 /* PAL termination */
249 PAL_Terminate();
250
251 /* return success */
252 return PASS;
253}
254