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