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