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: test5.c
8**
9** Dependencies: PAL_Initialize
10** PAL_Terminate
11** CreateEvent
12** SetEvent
13** CreateThread
14** ResumeThread
15** WaitForSingleObject
16** CloseHandle
17**
18** Purpose:
19**
20** Test to ensure proper operation of the QueueUserAPC()
21** API by trying to queue and activate APC functions on
22** a thread that was created suspended, prior to resuming
23** it. We're verifying the following behavior:
24**
25** "If an application queues an APC before the thread begins
26** running, the thread begins by calling the APC function.
27** After the thread calls an APC function, it calls the APC
28** functions for all APCs in its APC queue."
29**
30**
31**===========================================================================*/
32#include <palsuite.h>
33
34
35static HANDLE hEvent = NULL;
36static BOOL bAPCExecuted = FALSE;
37
38VOID PALAPI APCFunc( ULONG_PTR dwParam )
39{
40 bAPCExecuted = TRUE;
41}
42
43/**
44 * ThreadFunc
45 *
46 * Dummy thread function for APC queuing.
47 */
48DWORD PALAPI ThreadFunc( LPVOID param )
49{
50 DWORD ret = 0;
51
52 /* alertable wait until the global event is signalled */
53 ret = WaitForSingleObject( hEvent, INFINITE );
54 if( ret != WAIT_OBJECT_0 )
55 {
56 Fail( "ERROR:WaitForSingleObject() returned %lu, "
57 "expected WAIT_OBJECT_0\n",
58 ret );
59 }
60
61 return 0;
62}
63
64
65int __cdecl main( int argc, char **argv )
66
67{
68 /* local variables */
69 HANDLE hThread = NULL;
70 DWORD IDThread;
71 DWORD ret;
72 BOOL bResult = FALSE;
73
74 /* PAL initialization */
75 if( (PAL_Initialize(argc, argv)) != 0 )
76 {
77 return( FAIL );
78 }
79
80 /* create an event for the other thread to wait on */
81 hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
82 if( hEvent == NULL )
83 {
84 Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() );
85 }
86
87 /* run another dummy thread to cause notification of the library */
88 hThread = CreateThread( NULL, /* no security attributes */
89 0, /* use default stack size */
90 (LPTHREAD_START_ROUTINE) ThreadFunc, /* thread function */
91 (LPVOID) NULL, /* pass thread index as */
92 /* function argument */
93 CREATE_SUSPENDED, /* create suspended */
94 &IDThread ); /* returns thread id */
95
96 /* Check the return value for success. */
97 if( hThread == NULL )
98 {
99 /* error creating thread */
100 Trace( "ERROR:%lu:CreateThread call failed\n", GetLastError() );
101 if( ! CloseHandle( hEvent ) )
102 {
103 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
104 }
105 Fail( "test failed\n" );
106 }
107
108 /* queue our APC on the suspended thread */
109 ret = QueueUserAPC( APCFunc, hThread, 0 );
110 if( ret == 0 )
111 {
112 Fail( "ERROR:%lu:QueueUserAPC call failed\n", GetLastError() );
113 }
114
115 /* wait on the suspended thread */
116 ret = WaitForSingleObject( hThread, 2000 );
117 if( ret != WAIT_TIMEOUT )
118 {
119 Trace( "ERROR:WaitForSingleObject() returned %lu, "
120 "expected WAIT_TIMEOUT\n",
121 ret );
122 goto cleanup;
123 }
124
125 /* verify that the APC function was not executed */
126 if( bAPCExecuted == TRUE )
127 {
128 Trace( "ERROR:APC function was executed for a suspended thread\n" );
129 goto cleanup;
130 }
131
132 /* Resume the suspended thread */
133 ResumeThread( hThread );
134
135 /* do another wait on the resumed thread */
136 ret = WaitForSingleObject( hThread, 2000 );
137
138 /* verify that we got a WAIT_TIMEOUT result */
139 if( ret != WAIT_TIMEOUT )
140 {
141 Trace( "ERROR:WaitForSingleObject() returned %lu, "
142 "expected WAIT_TIMEOUT\n",
143 ret );
144 goto cleanup;
145 }
146
147 /* check that the APC function was actually executed */
148 if( bAPCExecuted == FALSE )
149 {
150 Trace( "ERROR:APC function was not executed\n" );
151 goto cleanup;
152 }
153
154 /* set the success flag */
155 bResult = PASS;
156
157cleanup:
158 /* signal the event so the other thread will exit */
159 if( ! SetEvent( hEvent ) )
160 {
161 Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() );
162 bResult = FAIL;
163 }
164
165 /* close the global event handle */
166 if( ! CloseHandle( hEvent ) )
167 {
168 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
169 bResult = FAIL;
170 }
171
172 /* wait on the other thread to complete */
173 ret = WaitForSingleObject( hThread, 2000 );
174 if( ret != WAIT_OBJECT_0 )
175 {
176 Trace( "ERROR:WaitForSingleObject() returned %lu, "
177 "expected WAIT_OBJECT_0\n",
178 ret );
179 bResult = FAIL;
180 }
181
182 /* close the thread handle */
183 if( ! CloseHandle( hThread ) )
184 {
185 Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() );
186 bResult = FAIL;
187 }
188
189 /* output final failure result for failure case */
190 if( bResult == FAIL )
191 {
192 Fail( "test failed\n" );
193 }
194
195 /* PAL termination */
196 PAL_Terminate();
197
198 /* return success */
199 return PASS;
200}
201