[KMTESTS:KE]
[reactos.git] / rostests / kmtests / ntos_ke / KeGuardedMutex.c
1 /*
2 * PROJECT: ReactOS kernel-mode tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Kernel-Mode Test Suite Guarded Mutex test
5 * PROGRAMMER: Thomas Faber <thomas.faber@reactos.org>
6 */
7
8 #include <kmt_test.h>
9
10 #define NDEBUG
11 #include <debug.h>
12
13 #define CheckMutex(Mutex, ExpectedCount, ExpectedOwner, ExpectedContention, \
14 ExpectedKernelApcDisable, ExpectedSpecialApcDisable, \
15 KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, \
16 ExpectedIrql) do \
17 { \
18 ok_eq_long((Mutex)->Count, ExpectedCount); \
19 ok_eq_pointer((Mutex)->Owner, ExpectedOwner); \
20 ok_eq_ulong((Mutex)->Contention, ExpectedContention); \
21 ok_eq_int((Mutex)->KernelApcDisable, ExpectedKernelApcDisable); \
22 if (KmtIsCheckedBuild) \
23 ok_eq_int((Mutex)->SpecialApcDisable, ExpectedSpecialApcDisable); \
24 else \
25 ok_eq_int((Mutex)->SpecialApcDisable, 0x5555); \
26 ok_eq_bool(KeAreApcsDisabled(), KernelApcsDisabled || SpecialApcsDisabled); \
27 ok_eq_int(Thread->KernelApcDisable, KernelApcsDisabled); \
28 ok_eq_bool(KeAreAllApcsDisabled(), AllApcsDisabled); \
29 ok_eq_int(Thread->SpecialApcDisable, SpecialApcsDisabled); \
30 ok_irql(ExpectedIrql); \
31 } while (0)
32
33 static
34 VOID
35 TestGuardedMutex(
36 PKGUARDED_MUTEX Mutex,
37 SHORT KernelApcsDisabled,
38 SHORT SpecialApcsDisabled,
39 SHORT AllApcsDisabled,
40 KIRQL OriginalIrql)
41 {
42 PKTHREAD Thread = KeGetCurrentThread();
43
44 ok_irql(OriginalIrql);
45 CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, 0x5555, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
46
47 /* these ASSERT */
48 if (!KmtIsCheckedBuild || OriginalIrql <= APC_LEVEL)
49 {
50 /* acquire/release normally */
51 KeAcquireGuardedMutex(Mutex);
52 CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
53 ok_bool_false(KeTryToAcquireGuardedMutex(Mutex), "KeTryToAcquireGuardedMutex returned");
54 CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
55 KeReleaseGuardedMutex(Mutex);
56 CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
57
58 /* try to acquire */
59 ok_bool_true(KeTryToAcquireGuardedMutex(Mutex), "KeTryToAcquireGuardedMutex returned");
60 CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
61 KeReleaseGuardedMutex(Mutex);
62 CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
63 }
64 else
65 /* Make the following test happy */
66 Mutex->SpecialApcDisable = SpecialApcsDisabled - 1;
67
68 /* ASSERT */
69 if (!KmtIsCheckedBuild || OriginalIrql == APC_LEVEL || SpecialApcsDisabled < 0)
70 {
71 /* acquire/release unsafe */
72 KeAcquireGuardedMutexUnsafe(Mutex);
73 CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
74 KeReleaseGuardedMutexUnsafe(Mutex);
75 CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
76 }
77
78 /* Bugchecks >= DISPATCH_LEVEL */
79 if (!KmtIsCheckedBuild)
80 {
81 /* mismatched acquire/release */
82 KeAcquireGuardedMutex(Mutex);
83 CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
84 KeReleaseGuardedMutexUnsafe(Mutex);
85 CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
86 KeLeaveGuardedRegion();
87 CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
88
89 KeAcquireGuardedMutexUnsafe(Mutex);
90 CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
91 KeReleaseGuardedMutex(Mutex);
92 CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled + 1, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -1, OriginalIrql);
93 KeEnterGuardedRegion();
94 CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
95
96 /* release without acquire */
97 KeReleaseGuardedMutexUnsafe(Mutex);
98 CheckMutex(Mutex, 0L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
99 KeReleaseGuardedMutex(Mutex);
100 CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled, KernelApcsDisabled, SpecialApcsDisabled + 1, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -1, OriginalIrql);
101 KeReleaseGuardedMutex(Mutex);
102 /* TODO: here we see that Mutex->Count isn't actually just a count. Test the bits correctly! */
103 CheckMutex(Mutex, 0L, NULL, 0LU, 0x5555, SpecialApcsDisabled, KernelApcsDisabled, SpecialApcsDisabled + 2, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -2, OriginalIrql);
104 KeReleaseGuardedMutex(Mutex);
105 CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled, KernelApcsDisabled, SpecialApcsDisabled + 3, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -3, OriginalIrql);
106 Thread->SpecialApcDisable -= 3;
107 }
108
109 /* make sure we survive this in case of error */
110 ok_eq_long(Mutex->Count, 1L);
111 Mutex->Count = 1;
112 ok_eq_int(Thread->KernelApcDisable, KernelApcsDisabled);
113 Thread->KernelApcDisable = KernelApcsDisabled;
114 ok_eq_int(Thread->SpecialApcDisable, SpecialApcsDisabled);
115 Thread->SpecialApcDisable = SpecialApcsDisabled;
116 ok_irql(OriginalIrql);
117 }
118
119 typedef VOID (FASTCALL *PMUTEX_FUNCTION)(PKGUARDED_MUTEX);
120 typedef BOOLEAN (FASTCALL *PMUTEX_TRY_FUNCTION)(PKGUARDED_MUTEX);
121
122 typedef struct
123 {
124 HANDLE Handle;
125 PKTHREAD Thread;
126 KIRQL Irql;
127 PKGUARDED_MUTEX Mutex;
128 PMUTEX_FUNCTION Acquire;
129 PMUTEX_TRY_FUNCTION TryAcquire;
130 PMUTEX_FUNCTION Release;
131 BOOLEAN Try;
132 BOOLEAN RetExpected;
133 KEVENT InEvent;
134 KEVENT OutEvent;
135 } THREAD_DATA, *PTHREAD_DATA;
136
137 static
138 VOID
139 NTAPI
140 AcquireMutexThread(
141 PVOID Parameter)
142 {
143 PTHREAD_DATA ThreadData = Parameter;
144 KIRQL Irql;
145 BOOLEAN Ret = FALSE;
146 NTSTATUS Status;
147
148 DPRINT("Thread starting\n");
149 KeRaiseIrql(ThreadData->Irql, &Irql);
150
151 if (ThreadData->Try)
152 {
153 Ret = ThreadData->TryAcquire(ThreadData->Mutex);
154 ok_eq_bool(Ret, ThreadData->RetExpected);
155 }
156 else
157 ThreadData->Acquire(ThreadData->Mutex);
158
159 ok_bool_false(KeSetEvent(&ThreadData->OutEvent, 0, TRUE), "KeSetEvent returned");
160 DPRINT("Thread now waiting\n");
161 Status = KeWaitForSingleObject(&ThreadData->InEvent, Executive, KernelMode, FALSE, NULL);
162 DPRINT("Thread done waiting\n");
163 ok_eq_hex(Status, STATUS_SUCCESS);
164
165 if (!ThreadData->Try || Ret)
166 ThreadData->Release(ThreadData->Mutex);
167
168 KeLowerIrql(Irql);
169 DPRINT("Thread exiting\n");
170 }
171
172 static
173 VOID
174 InitThreadData(
175 PTHREAD_DATA ThreadData,
176 PKGUARDED_MUTEX Mutex,
177 PMUTEX_FUNCTION Acquire,
178 PMUTEX_TRY_FUNCTION TryAcquire,
179 PMUTEX_FUNCTION Release)
180 {
181 ThreadData->Mutex = Mutex;
182 KeInitializeEvent(&ThreadData->InEvent, NotificationEvent, FALSE);
183 KeInitializeEvent(&ThreadData->OutEvent, NotificationEvent, FALSE);
184 ThreadData->Acquire = Acquire;
185 ThreadData->TryAcquire = TryAcquire;
186 ThreadData->Release = Release;
187 }
188
189 static
190 NTSTATUS
191 StartThread(
192 PTHREAD_DATA ThreadData,
193 PLARGE_INTEGER Timeout,
194 KIRQL Irql,
195 BOOLEAN Try,
196 BOOLEAN RetExpected)
197 {
198 NTSTATUS Status = STATUS_SUCCESS;
199 OBJECT_ATTRIBUTES Attributes;
200
201 ThreadData->Try = Try;
202 ThreadData->Irql = Irql;
203 ThreadData->RetExpected = RetExpected;
204 InitializeObjectAttributes(&Attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
205 Status = PsCreateSystemThread(&ThreadData->Handle, GENERIC_ALL, &Attributes, NULL, NULL, AcquireMutexThread, ThreadData);
206 ok_eq_hex(Status, STATUS_SUCCESS);
207 Status = ObReferenceObjectByHandle(ThreadData->Handle, SYNCHRONIZE, *PsThreadType, KernelMode, (PVOID *)&ThreadData->Thread, NULL);
208 ok_eq_hex(Status, STATUS_SUCCESS);
209
210 return KeWaitForSingleObject(&ThreadData->OutEvent, Executive, KernelMode, FALSE, Timeout);
211 }
212
213 static
214 VOID
215 FinishThread(
216 PTHREAD_DATA ThreadData)
217 {
218 NTSTATUS Status = STATUS_SUCCESS;
219
220 KeSetEvent(&ThreadData->InEvent, 0, TRUE);
221 Status = KeWaitForSingleObject(ThreadData->Thread, Executive, KernelMode, FALSE, NULL);
222 ok_eq_hex(Status, STATUS_SUCCESS);
223
224 ObDereferenceObject(ThreadData->Thread);
225 Status = ZwClose(ThreadData->Handle);
226 ok_eq_hex(Status, STATUS_SUCCESS);
227 KeClearEvent(&ThreadData->InEvent);
228 KeClearEvent(&ThreadData->OutEvent);
229 }
230
231 static
232 VOID
233 TestGuardedMutexConcurrent(
234 PKGUARDED_MUTEX Mutex)
235 {
236 NTSTATUS Status;
237 THREAD_DATA ThreadData;
238 THREAD_DATA ThreadData2;
239 THREAD_DATA ThreadDataUnsafe;
240 THREAD_DATA ThreadDataTry;
241 PKTHREAD Thread = KeGetCurrentThread();
242 LARGE_INTEGER Timeout;
243 Timeout.QuadPart = -50 * 1000 * 10; /* 50 ms */
244
245 InitThreadData(&ThreadData, Mutex, KeAcquireGuardedMutex, NULL, KeReleaseGuardedMutex);
246 InitThreadData(&ThreadData2, Mutex, KeAcquireGuardedMutex, NULL, KeReleaseGuardedMutex);
247 InitThreadData(&ThreadDataUnsafe, Mutex, KeAcquireGuardedMutexUnsafe, NULL, KeReleaseGuardedMutexUnsafe);
248 InitThreadData(&ThreadDataTry, Mutex, NULL, KeTryToAcquireGuardedMutex, KeReleaseGuardedMutex);
249
250 /* have a thread acquire the mutex */
251 Status = StartThread(&ThreadData, NULL, PASSIVE_LEVEL, FALSE, FALSE);
252 ok_eq_hex(Status, STATUS_SUCCESS);
253 CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
254 /* have a second thread try to acquire it -- should fail */
255 Status = StartThread(&ThreadDataTry, NULL, PASSIVE_LEVEL, TRUE, FALSE);
256 ok_eq_hex(Status, STATUS_SUCCESS);
257 CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
258 FinishThread(&ThreadDataTry);
259
260 /* have another thread acquire it -- should block */
261 Status = StartThread(&ThreadData2, &Timeout, APC_LEVEL, FALSE, FALSE);
262 ok_eq_hex(Status, STATUS_TIMEOUT);
263 CheckMutex(Mutex, 4L, ThreadData.Thread, 1LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
264
265 /* finish the first thread -- now the second should become available */
266 FinishThread(&ThreadData);
267 Status = KeWaitForSingleObject(&ThreadData2.OutEvent, Executive, KernelMode, FALSE, NULL);
268 ok_eq_hex(Status, STATUS_SUCCESS);
269 CheckMutex(Mutex, 0L, ThreadData2.Thread, 1LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
270
271 /* block two more threads */
272 Status = StartThread(&ThreadDataUnsafe, &Timeout, APC_LEVEL, FALSE, FALSE);
273 ok_eq_hex(Status, STATUS_TIMEOUT);
274 CheckMutex(Mutex, 4L, ThreadData2.Thread, 2LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
275
276 Status = StartThread(&ThreadData, &Timeout, PASSIVE_LEVEL, FALSE, FALSE);
277 ok_eq_hex(Status, STATUS_TIMEOUT);
278 CheckMutex(Mutex, 8L, ThreadData2.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
279
280 /* finish 1 */
281 FinishThread(&ThreadData2);
282 Status = KeWaitForSingleObject(&ThreadDataUnsafe.OutEvent, Executive, KernelMode, FALSE, NULL);
283 ok_eq_hex(Status, STATUS_SUCCESS);
284 CheckMutex(Mutex, 4L, ThreadDataUnsafe.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
285
286 /* finish 2 */
287 FinishThread(&ThreadDataUnsafe);
288 Status = KeWaitForSingleObject(&ThreadData.OutEvent, Executive, KernelMode, FALSE, NULL);
289 ok_eq_hex(Status, STATUS_SUCCESS);
290 CheckMutex(Mutex, 0L, ThreadData.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
291
292 /* finish 3 */
293 FinishThread(&ThreadData);
294
295 CheckMutex(Mutex, 1L, NULL, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
296 }
297
298 START_TEST(KeGuardedMutex)
299 {
300 KGUARDED_MUTEX Mutex;
301 KIRQL OldIrql;
302 PKTHREAD Thread = KeGetCurrentThread();
303 struct {
304 KIRQL Irql;
305 SHORT KernelApcsDisabled;
306 SHORT SpecialApcsDisabled;
307 BOOLEAN AllApcsDisabled;
308 } TestIterations[] =
309 {
310 { PASSIVE_LEVEL, 0, 0, FALSE },
311 { PASSIVE_LEVEL, -1, 0, FALSE },
312 { PASSIVE_LEVEL, -3, 0, FALSE },
313 { PASSIVE_LEVEL, 0, -1, TRUE },
314 { PASSIVE_LEVEL, -1, -1, TRUE },
315 { PASSIVE_LEVEL, -3, -2, TRUE },
316 // 6
317 { APC_LEVEL, 0, 0, TRUE },
318 { APC_LEVEL, -1, 0, TRUE },
319 { APC_LEVEL, -3, 0, TRUE },
320 { APC_LEVEL, 0, -1, TRUE },
321 { APC_LEVEL, -1, -1, TRUE },
322 { APC_LEVEL, -3, -2, TRUE },
323 // 12
324 { DISPATCH_LEVEL, 0, 0, TRUE },
325 { DISPATCH_LEVEL, -1, 0, TRUE },
326 { DISPATCH_LEVEL, -3, 0, TRUE },
327 { DISPATCH_LEVEL, 0, -1, TRUE },
328 { DISPATCH_LEVEL, -1, -1, TRUE },
329 { DISPATCH_LEVEL, -3, -2, TRUE },
330 // 18
331 { HIGH_LEVEL, 0, 0, TRUE },
332 { HIGH_LEVEL, -1, 0, TRUE },
333 { HIGH_LEVEL, -3, 0, TRUE },
334 { HIGH_LEVEL, 0, -1, TRUE },
335 { HIGH_LEVEL, -1, -1, TRUE },
336 { HIGH_LEVEL, -3, -2, TRUE },
337 };
338 int i;
339
340 for (i = 0; i < sizeof TestIterations / sizeof TestIterations[0]; ++i)
341 {
342 trace("Run %d\n", i);
343 KeRaiseIrql(TestIterations[i].Irql, &OldIrql);
344 Thread->KernelApcDisable = TestIterations[i].KernelApcsDisabled;
345 Thread->SpecialApcDisable = TestIterations[i].SpecialApcsDisabled;
346
347 RtlFillMemory(&Mutex, sizeof Mutex, 0x55);
348 KeInitializeGuardedMutex(&Mutex);
349 CheckMutex(&Mutex, 1L, NULL, 0LU, 0x5555, 0x5555, TestIterations[i].KernelApcsDisabled, TestIterations[i].SpecialApcsDisabled, TestIterations[i].AllApcsDisabled, TestIterations[i].Irql);
350 TestGuardedMutex(&Mutex, TestIterations[i].KernelApcsDisabled, TestIterations[i].SpecialApcsDisabled, TestIterations[i].AllApcsDisabled, TestIterations[i].Irql);
351
352 Thread->SpecialApcDisable = 0;
353 Thread->KernelApcDisable = 0;
354 KeLowerIrql(OldIrql);
355 }
356
357 trace("Concurrent test\n");
358 RtlFillMemory(&Mutex, sizeof Mutex, 0x55);
359 KeInitializeGuardedMutex(&Mutex);
360 TestGuardedMutexConcurrent(&Mutex);
361 }