[KMTEST]
[reactos.git] / rostests / kmtests / ntos_ex / ExFastMutex.c
1 /*
2 * PROJECT: ReactOS kernel-mode tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Kernel-Mode Test Suite Fast 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 NTKERNELAPI VOID FASTCALL ExiAcquireFastMutex(IN OUT PFAST_MUTEX FastMutex);
14 NTKERNELAPI VOID FASTCALL ExiReleaseFastMutex(IN OUT PFAST_MUTEX FastMutex);
15 NTKERNELAPI BOOLEAN FASTCALL ExiTryToAcquireFastMutex(IN OUT PFAST_MUTEX FastMutex);
16
17 #define CheckMutex(Mutex, ExpectedCount, ExpectedOwner, \
18 ExpectedContention, ExpectedOldIrql, \
19 ExpectedIrql) do \
20 { \
21 ok_eq_long((Mutex)->Count, ExpectedCount); \
22 ok_eq_pointer((Mutex)->Owner, ExpectedOwner); \
23 ok_eq_ulong((Mutex)->Contention, ExpectedContention); \
24 ok_eq_ulong((Mutex)->OldIrql, (ULONG)ExpectedOldIrql); \
25 ok_bool_false(KeAreApcsDisabled(), "KeAreApcsDisabled returned"); \
26 ok_irql(ExpectedIrql); \
27 } while (0)
28
29 static
30 VOID
31 TestFastMutex(
32 PFAST_MUTEX Mutex,
33 KIRQL OriginalIrql)
34 {
35 PKTHREAD Thread = KeGetCurrentThread();
36
37 ok_irql(OriginalIrql);
38
39 /* acquire/release normally */
40 ExAcquireFastMutex(Mutex);
41 CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
42 ok_bool_false(ExTryToAcquireFastMutex(Mutex), "ExTryToAcquireFastMutex returned");
43 CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
44 ExReleaseFastMutex(Mutex);
45 CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
46
47 #ifdef _M_IX86
48 /* ntoskrnl's fastcall version */
49 ExiAcquireFastMutex(Mutex);
50 CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
51 ok_bool_false(ExiTryToAcquireFastMutex(Mutex), "ExiTryToAcquireFastMutex returned");
52 CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
53 ExiReleaseFastMutex(Mutex);
54 CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
55 #endif
56
57 /* try to acquire */
58 ok_bool_true(ExTryToAcquireFastMutex(Mutex), "ExTryToAcquireFastMutex returned");
59 CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
60 ExReleaseFastMutex(Mutex);
61 CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
62
63 /* shortcut functions with critical region */
64 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(Mutex);
65 ok_bool_true(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
66 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(Mutex);
67
68 /* acquire/release unsafe */
69 if (!KmtIsCheckedBuild || OriginalIrql == APC_LEVEL)
70 {
71 ExAcquireFastMutexUnsafe(Mutex);
72 CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, OriginalIrql);
73 ExReleaseFastMutexUnsafe(Mutex);
74 CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
75
76 /* mismatched acquire/release */
77 ExAcquireFastMutex(Mutex);
78 CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
79 ExReleaseFastMutexUnsafe(Mutex);
80 CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, APC_LEVEL);
81 KmtSetIrql(OriginalIrql);
82 CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
83
84 Mutex->OldIrql = 0x55555555LU;
85 ExAcquireFastMutexUnsafe(Mutex);
86 CheckMutex(Mutex, 0L, Thread, 0LU, 0x55555555LU, OriginalIrql);
87 Mutex->OldIrql = PASSIVE_LEVEL;
88 ExReleaseFastMutex(Mutex);
89 CheckMutex(Mutex, 1L, NULL, 0LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
90 KmtSetIrql(OriginalIrql);
91 CheckMutex(Mutex, 1L, NULL, 0LU, PASSIVE_LEVEL, OriginalIrql);
92 }
93
94 if (!KmtIsCheckedBuild)
95 {
96 /* release without acquire */
97 ExReleaseFastMutexUnsafe(Mutex);
98 CheckMutex(Mutex, 2L, NULL, 0LU, PASSIVE_LEVEL, OriginalIrql);
99 --Mutex->Count;
100 Mutex->OldIrql = OriginalIrql;
101 ExReleaseFastMutex(Mutex);
102 CheckMutex(Mutex, 2L, NULL, 0LU, OriginalIrql, OriginalIrql);
103 ExReleaseFastMutex(Mutex);
104 CheckMutex(Mutex, 3L, NULL, 0LU, OriginalIrql, OriginalIrql);
105 Mutex->Count -= 2;
106 }
107
108 /* make sure we survive this in case of error */
109 ok_eq_long(Mutex->Count, 1L);
110 Mutex->Count = 1;
111 ok_irql(OriginalIrql);
112 KmtSetIrql(OriginalIrql);
113 }
114
115 typedef VOID (FASTCALL *PMUTEX_FUNCTION)(PFAST_MUTEX);
116 typedef BOOLEAN (FASTCALL *PMUTEX_TRY_FUNCTION)(PFAST_MUTEX);
117
118 typedef struct
119 {
120 HANDLE Handle;
121 PKTHREAD Thread;
122 KIRQL Irql;
123 PFAST_MUTEX Mutex;
124 PMUTEX_FUNCTION Acquire;
125 PMUTEX_TRY_FUNCTION TryAcquire;
126 PMUTEX_FUNCTION Release;
127 BOOLEAN Try;
128 BOOLEAN RetExpected;
129 KEVENT InEvent;
130 KEVENT OutEvent;
131 } THREAD_DATA, *PTHREAD_DATA;
132
133 static
134 VOID
135 NTAPI
136 AcquireMutexThread(
137 PVOID Parameter)
138 {
139 PTHREAD_DATA ThreadData = Parameter;
140 KIRQL Irql;
141 BOOLEAN Ret = FALSE;
142 NTSTATUS Status;
143
144 KeRaiseIrql(ThreadData->Irql, &Irql);
145
146 if (ThreadData->Try)
147 {
148 Ret = ThreadData->TryAcquire(ThreadData->Mutex);
149 ok_eq_bool(Ret, ThreadData->RetExpected);
150 }
151 else
152 ThreadData->Acquire(ThreadData->Mutex);
153
154 ok_bool_false(KeSetEvent(&ThreadData->OutEvent, 0, TRUE), "KeSetEvent returned");
155 Status = KeWaitForSingleObject(&ThreadData->InEvent, Executive, KernelMode, FALSE, NULL);
156 ok_eq_hex(Status, STATUS_SUCCESS);
157
158 if (!ThreadData->Try || Ret)
159 ThreadData->Release(ThreadData->Mutex);
160
161 KeLowerIrql(Irql);
162 }
163
164 static
165 VOID
166 InitThreadData(
167 PTHREAD_DATA ThreadData,
168 PFAST_MUTEX Mutex,
169 PMUTEX_FUNCTION Acquire,
170 PMUTEX_TRY_FUNCTION TryAcquire,
171 PMUTEX_FUNCTION Release)
172 {
173 ThreadData->Mutex = Mutex;
174 KeInitializeEvent(&ThreadData->InEvent, NotificationEvent, FALSE);
175 KeInitializeEvent(&ThreadData->OutEvent, NotificationEvent, FALSE);
176 ThreadData->Acquire = Acquire;
177 ThreadData->TryAcquire = TryAcquire;
178 ThreadData->Release = Release;
179 }
180
181 static
182 NTSTATUS
183 StartThread(
184 PTHREAD_DATA ThreadData,
185 PLARGE_INTEGER Timeout,
186 KIRQL Irql,
187 BOOLEAN Try,
188 BOOLEAN RetExpected)
189 {
190 NTSTATUS Status = STATUS_SUCCESS;
191 OBJECT_ATTRIBUTES Attributes;
192
193 ThreadData->Try = Try;
194 ThreadData->Irql = Irql;
195 ThreadData->RetExpected = RetExpected;
196 InitializeObjectAttributes(&Attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
197 Status = PsCreateSystemThread(&ThreadData->Handle, GENERIC_ALL, &Attributes, NULL, NULL, AcquireMutexThread, ThreadData);
198 ok_eq_hex(Status, STATUS_SUCCESS);
199 Status = ObReferenceObjectByHandle(ThreadData->Handle, SYNCHRONIZE,
200 #ifdef _PROPER_NT_EXPORTS
201 *PsThreadType,
202 #else
203 PsThreadType,
204 #endif
205 KernelMode, (PVOID *)&ThreadData->Thread, NULL);
206 ok_eq_hex(Status, STATUS_SUCCESS);
207
208 return KeWaitForSingleObject(&ThreadData->OutEvent, Executive, KernelMode, FALSE, Timeout);
209 }
210
211 static
212 VOID
213 FinishThread(
214 PTHREAD_DATA ThreadData)
215 {
216 NTSTATUS Status = STATUS_SUCCESS;
217
218 KeSetEvent(&ThreadData->InEvent, 0, TRUE);
219 Status = KeWaitForSingleObject(ThreadData->Thread, Executive, KernelMode, FALSE, NULL);
220 ok_eq_hex(Status, STATUS_SUCCESS);
221
222 ObDereferenceObject(ThreadData->Thread);
223 Status = ZwClose(ThreadData->Handle);
224 ok_eq_hex(Status, STATUS_SUCCESS);
225 KeClearEvent(&ThreadData->InEvent);
226 KeClearEvent(&ThreadData->OutEvent);
227 }
228
229 static
230 VOID
231 TestFastMutexConcurrent(
232 PFAST_MUTEX Mutex)
233 {
234 NTSTATUS Status;
235 THREAD_DATA ThreadData;
236 THREAD_DATA ThreadData2;
237 THREAD_DATA ThreadDataUnsafe;
238 THREAD_DATA ThreadDataTry;
239 LARGE_INTEGER Timeout;
240 Timeout.QuadPart = -10 * 1000 * 10; /* 10 ms */
241
242 InitThreadData(&ThreadData, Mutex, ExAcquireFastMutex, NULL, ExReleaseFastMutex);
243 InitThreadData(&ThreadData2, Mutex, ExAcquireFastMutex, NULL, ExReleaseFastMutex);
244 InitThreadData(&ThreadDataUnsafe, Mutex, ExAcquireFastMutexUnsafe, NULL, ExReleaseFastMutexUnsafe);
245 InitThreadData(&ThreadDataTry, Mutex, NULL, ExTryToAcquireFastMutex, ExReleaseFastMutex);
246
247 /* have a thread acquire the mutex */
248 Status = StartThread(&ThreadData, NULL, PASSIVE_LEVEL, FALSE, FALSE);
249 ok_eq_hex(Status, STATUS_SUCCESS);
250 CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
251 /* have a second thread try to acquire it -- should fail */
252 Status = StartThread(&ThreadDataTry, NULL, PASSIVE_LEVEL, TRUE, FALSE);
253 ok_eq_hex(Status, STATUS_SUCCESS);
254 CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
255 FinishThread(&ThreadDataTry);
256
257 /* have another thread acquire it -- should block */
258 Status = StartThread(&ThreadData2, &Timeout, APC_LEVEL, FALSE, FALSE);
259 ok_eq_hex(Status, STATUS_TIMEOUT);
260 CheckMutex(Mutex, -1L, ThreadData.Thread, 1LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
261
262 /* finish the first thread -- now the second should become available */
263 FinishThread(&ThreadData);
264 Status = KeWaitForSingleObject(&ThreadData2.OutEvent, Executive, KernelMode, FALSE, NULL);
265 ok_eq_hex(Status, STATUS_SUCCESS);
266 CheckMutex(Mutex, 0L, ThreadData2.Thread, 1LU, APC_LEVEL, PASSIVE_LEVEL);
267
268 /* block two more threads */
269 Status = StartThread(&ThreadDataUnsafe, &Timeout, APC_LEVEL, FALSE, FALSE);
270 ok_eq_hex(Status, STATUS_TIMEOUT);
271 CheckMutex(Mutex, -1L, ThreadData2.Thread, 2LU, APC_LEVEL, PASSIVE_LEVEL);
272
273 Status = StartThread(&ThreadData, &Timeout, PASSIVE_LEVEL, FALSE, FALSE);
274 ok_eq_hex(Status, STATUS_TIMEOUT);
275 CheckMutex(Mutex, -2L, ThreadData2.Thread, 3LU, APC_LEVEL, PASSIVE_LEVEL);
276
277 /* finish 1 */
278 FinishThread(&ThreadData2);
279 Status = KeWaitForSingleObject(&ThreadDataUnsafe.OutEvent, Executive, KernelMode, FALSE, NULL);
280 ok_eq_hex(Status, STATUS_SUCCESS);
281 CheckMutex(Mutex, -1L, ThreadDataUnsafe.Thread, 3LU, APC_LEVEL, PASSIVE_LEVEL);
282
283 /* finish 2 */
284 FinishThread(&ThreadDataUnsafe);
285 Status = KeWaitForSingleObject(&ThreadData.OutEvent, Executive, KernelMode, FALSE, NULL);
286 ok_eq_hex(Status, STATUS_SUCCESS);
287 CheckMutex(Mutex, 0L, ThreadData.Thread, 3LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
288
289 /* finish 3 */
290 FinishThread(&ThreadData);
291
292 CheckMutex(Mutex, 1L, NULL, 3LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
293 }
294
295 START_TEST(ExFastMutex)
296 {
297 FAST_MUTEX Mutex;
298 KIRQL Irql;
299
300 memset(&Mutex, 0x55, sizeof Mutex);
301 ExInitializeFastMutex(&Mutex);
302 CheckMutex(&Mutex, 1L, NULL, 0LU, 0x55555555LU, PASSIVE_LEVEL);
303
304 TestFastMutex(&Mutex, PASSIVE_LEVEL);
305 KeRaiseIrql(APC_LEVEL, &Irql);
306 TestFastMutex(&Mutex, APC_LEVEL);
307 if (!KmtIsCheckedBuild)
308 {
309 KeRaiseIrql(DISPATCH_LEVEL, &Irql);
310 TestFastMutex(&Mutex, DISPATCH_LEVEL);
311 KeRaiseIrql(HIGH_LEVEL, &Irql);
312 TestFastMutex(&Mutex, HIGH_LEVEL);
313 }
314 KeLowerIrql(PASSIVE_LEVEL);
315
316 TestFastMutexConcurrent(&Mutex);
317 }