[KMTESTS]
[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, *PsThreadType, KernelMode, (PVOID *)&ThreadData->Thread, NULL);
200 ok_eq_hex(Status, STATUS_SUCCESS);
201
202 return KeWaitForSingleObject(&ThreadData->OutEvent, Executive, KernelMode, FALSE, Timeout);
203 }
204
205 static
206 VOID
207 FinishThread(
208 PTHREAD_DATA ThreadData)
209 {
210 NTSTATUS Status = STATUS_SUCCESS;
211
212 KeSetEvent(&ThreadData->InEvent, 0, TRUE);
213 Status = KeWaitForSingleObject(ThreadData->Thread, Executive, KernelMode, FALSE, NULL);
214 ok_eq_hex(Status, STATUS_SUCCESS);
215
216 ObDereferenceObject(ThreadData->Thread);
217 Status = ZwClose(ThreadData->Handle);
218 ok_eq_hex(Status, STATUS_SUCCESS);
219 KeClearEvent(&ThreadData->InEvent);
220 KeClearEvent(&ThreadData->OutEvent);
221 }
222
223 static
224 VOID
225 TestFastMutexConcurrent(
226 PFAST_MUTEX Mutex)
227 {
228 NTSTATUS Status;
229 THREAD_DATA ThreadData;
230 THREAD_DATA ThreadData2;
231 THREAD_DATA ThreadDataUnsafe;
232 THREAD_DATA ThreadDataTry;
233 LARGE_INTEGER Timeout;
234 Timeout.QuadPart = -10 * 1000 * 10; /* 10 ms */
235
236 InitThreadData(&ThreadData, Mutex, ExAcquireFastMutex, NULL, ExReleaseFastMutex);
237 InitThreadData(&ThreadData2, Mutex, ExAcquireFastMutex, NULL, ExReleaseFastMutex);
238 InitThreadData(&ThreadDataUnsafe, Mutex, ExAcquireFastMutexUnsafe, NULL, ExReleaseFastMutexUnsafe);
239 InitThreadData(&ThreadDataTry, Mutex, NULL, ExTryToAcquireFastMutex, ExReleaseFastMutex);
240
241 /* have a thread acquire the mutex */
242 Status = StartThread(&ThreadData, NULL, PASSIVE_LEVEL, FALSE, FALSE);
243 ok_eq_hex(Status, STATUS_SUCCESS);
244 CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
245 /* have a second thread try to acquire it -- should fail */
246 Status = StartThread(&ThreadDataTry, NULL, PASSIVE_LEVEL, TRUE, FALSE);
247 ok_eq_hex(Status, STATUS_SUCCESS);
248 CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
249 FinishThread(&ThreadDataTry);
250
251 /* have another thread acquire it -- should block */
252 Status = StartThread(&ThreadData2, &Timeout, APC_LEVEL, FALSE, FALSE);
253 ok_eq_hex(Status, STATUS_TIMEOUT);
254 CheckMutex(Mutex, -1L, ThreadData.Thread, 1LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
255
256 /* finish the first thread -- now the second should become available */
257 FinishThread(&ThreadData);
258 Status = KeWaitForSingleObject(&ThreadData2.OutEvent, Executive, KernelMode, FALSE, NULL);
259 ok_eq_hex(Status, STATUS_SUCCESS);
260 CheckMutex(Mutex, 0L, ThreadData2.Thread, 1LU, APC_LEVEL, PASSIVE_LEVEL);
261
262 /* block two more threads */
263 Status = StartThread(&ThreadDataUnsafe, &Timeout, APC_LEVEL, FALSE, FALSE);
264 ok_eq_hex(Status, STATUS_TIMEOUT);
265 CheckMutex(Mutex, -1L, ThreadData2.Thread, 2LU, APC_LEVEL, PASSIVE_LEVEL);
266
267 Status = StartThread(&ThreadData, &Timeout, PASSIVE_LEVEL, FALSE, FALSE);
268 ok_eq_hex(Status, STATUS_TIMEOUT);
269 CheckMutex(Mutex, -2L, ThreadData2.Thread, 3LU, APC_LEVEL, PASSIVE_LEVEL);
270
271 /* finish 1 */
272 FinishThread(&ThreadData2);
273 Status = KeWaitForSingleObject(&ThreadDataUnsafe.OutEvent, Executive, KernelMode, FALSE, NULL);
274 ok_eq_hex(Status, STATUS_SUCCESS);
275 CheckMutex(Mutex, -1L, ThreadDataUnsafe.Thread, 3LU, APC_LEVEL, PASSIVE_LEVEL);
276
277 /* finish 2 */
278 FinishThread(&ThreadDataUnsafe);
279 Status = KeWaitForSingleObject(&ThreadData.OutEvent, Executive, KernelMode, FALSE, NULL);
280 ok_eq_hex(Status, STATUS_SUCCESS);
281 CheckMutex(Mutex, 0L, ThreadData.Thread, 3LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
282
283 /* finish 3 */
284 FinishThread(&ThreadData);
285
286 CheckMutex(Mutex, 1L, NULL, 3LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
287 }
288
289 START_TEST(ExFastMutex)
290 {
291 FAST_MUTEX Mutex;
292 KIRQL Irql;
293
294 memset(&Mutex, 0x55, sizeof Mutex);
295 ExInitializeFastMutex(&Mutex);
296 CheckMutex(&Mutex, 1L, NULL, 0LU, 0x55555555LU, PASSIVE_LEVEL);
297
298 TestFastMutex(&Mutex, PASSIVE_LEVEL);
299 KeRaiseIrql(APC_LEVEL, &Irql);
300 TestFastMutex(&Mutex, APC_LEVEL);
301 if (!KmtIsCheckedBuild)
302 {
303 KeRaiseIrql(DISPATCH_LEVEL, &Irql);
304 TestFastMutex(&Mutex, DISPATCH_LEVEL);
305 KeRaiseIrql(HIGH_LEVEL, &Irql);
306 TestFastMutex(&Mutex, HIGH_LEVEL);
307 }
308 KeLowerIrql(PASSIVE_LEVEL);
309
310 TestFastMutexConcurrent(&Mutex);
311 }