ReactOS  0.4.13-dev-563-g0561610
ExFastMutex.c
Go to the documentation of this file.
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 static
14 VOID
15 (FASTCALL
16 *pExEnterCriticalRegionAndAcquireFastMutexUnsafe)(
17  _Inout_ PFAST_MUTEX FastMutex
18 );
19 
20 static
21 VOID
22 (FASTCALL
23 *pExReleaseFastMutexUnsafeAndLeaveCriticalRegion)(
24  _Inout_ PFAST_MUTEX FastMutex
25 );
26 
27 static VOID (FASTCALL *pExiAcquireFastMutex)(IN OUT PFAST_MUTEX FastMutex);
28 static VOID (FASTCALL *pExiReleaseFastMutex)(IN OUT PFAST_MUTEX FastMutex);
29 static BOOLEAN (FASTCALL *pExiTryToAcquireFastMutex)(IN OUT PFAST_MUTEX FastMutex);
30 
31 #define CheckMutex(Mutex, ExpectedCount, ExpectedOwner, \
32  ExpectedContention, ExpectedOldIrql, \
33  ExpectedIrql) do \
34 { \
35  ok_eq_long((Mutex)->Count, ExpectedCount); \
36  ok_eq_pointer((Mutex)->Owner, ExpectedOwner); \
37  ok_eq_ulong((Mutex)->Contention, ExpectedContention); \
38  ok_eq_ulong((Mutex)->OldIrql, (ULONG)ExpectedOldIrql); \
39  ok_bool_false(KeAreApcsDisabled(), "KeAreApcsDisabled returned"); \
40  ok_irql(ExpectedIrql); \
41 } while (0)
42 
43 static
44 VOID
47  KIRQL OriginalIrql)
48 {
50 
51  ok_irql(OriginalIrql);
52 
53  /* acquire/release normally */
55  CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
56  ok_bool_false(ExTryToAcquireFastMutex(Mutex), "ExTryToAcquireFastMutex returned");
57  CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
59  CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
60 
61  /* ntoskrnl's fastcall version */
62  if (!skip(pExiAcquireFastMutex &&
63  pExiReleaseFastMutex &&
64  pExiTryToAcquireFastMutex, "No fastcall fast mutex functions\n"))
65  {
66  pExiAcquireFastMutex(Mutex);
67  CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
68  ok_bool_false(pExiTryToAcquireFastMutex(Mutex), "ExiTryToAcquireFastMutex returned");
69  CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
70  pExiReleaseFastMutex(Mutex);
71  CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
72  }
73 
74  /* try to acquire */
75  ok_bool_true(ExTryToAcquireFastMutex(Mutex), "ExTryToAcquireFastMutex returned");
76  CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
78  CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
79 
80  /* shortcut functions with critical region */
81  if (!skip(pExEnterCriticalRegionAndAcquireFastMutexUnsafe &&
82  pExReleaseFastMutexUnsafeAndLeaveCriticalRegion,
83  "Shortcut functions not available"))
84  {
85  pExEnterCriticalRegionAndAcquireFastMutexUnsafe(Mutex);
86  ok_bool_true(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
87  pExReleaseFastMutexUnsafeAndLeaveCriticalRegion(Mutex);
88  }
89 
90  /* acquire/release unsafe */
91  if (!KmtIsCheckedBuild || OriginalIrql == APC_LEVEL)
92  {
94  CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, OriginalIrql);
96  CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
97 
98  /* mismatched acquire/release */
100  CheckMutex(Mutex, 0L, Thread, 0LU, OriginalIrql, APC_LEVEL);
102  CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, APC_LEVEL);
103  KmtSetIrql(OriginalIrql);
104  CheckMutex(Mutex, 1L, NULL, 0LU, OriginalIrql, OriginalIrql);
105 
106  Mutex->OldIrql = 0x55555555LU;
108  CheckMutex(Mutex, 0L, Thread, 0LU, 0x55555555LU, OriginalIrql);
109  Mutex->OldIrql = PASSIVE_LEVEL;
112  KmtSetIrql(OriginalIrql);
113  CheckMutex(Mutex, 1L, NULL, 0LU, PASSIVE_LEVEL, OriginalIrql);
114  }
115 
116  if (!KmtIsCheckedBuild)
117  {
118  /* release without acquire */
120  CheckMutex(Mutex, 2L, NULL, 0LU, PASSIVE_LEVEL, OriginalIrql);
121  --Mutex->Count;
122  Mutex->OldIrql = OriginalIrql;
124  CheckMutex(Mutex, 2L, NULL, 0LU, OriginalIrql, OriginalIrql);
126  CheckMutex(Mutex, 3L, NULL, 0LU, OriginalIrql, OriginalIrql);
127  Mutex->Count -= 2;
128  }
129 
130  /* make sure we survive this in case of error */
131  ok_eq_long(Mutex->Count, 1L);
132  Mutex->Count = 1;
133  ok_irql(OriginalIrql);
134  KmtSetIrql(OriginalIrql);
135 }
136 
139 
140 typedef struct
141 {
154 
155 static
156 VOID
157 NTAPI
160 {
162  KIRQL Irql;
163  BOOLEAN Ret = FALSE;
165 
166  KeRaiseIrql(ThreadData->Irql, &Irql);
167 
168  if (ThreadData->Try)
169  {
170  Ret = ThreadData->TryAcquire(ThreadData->Mutex);
171  ok_eq_bool(Ret, ThreadData->RetExpected);
172  }
173  else
174  ThreadData->Acquire(ThreadData->Mutex);
175 
176  ok_bool_false(KeSetEvent(&ThreadData->OutEvent, 0, TRUE), "KeSetEvent returned");
179 
180  if (!ThreadData->Try || Ret)
181  ThreadData->Release(ThreadData->Mutex);
182 
183  KeLowerIrql(Irql);
184 }
185 
186 static
187 VOID
191  PMUTEX_FUNCTION Acquire,
192  PMUTEX_TRY_FUNCTION TryAcquire,
194 {
195  ThreadData->Mutex = Mutex;
198  ThreadData->Acquire = Acquire;
199  ThreadData->TryAcquire = TryAcquire;
200  ThreadData->Release = Release;
201 }
202 
203 static
204 NTSTATUS
208  KIRQL Irql,
209  BOOLEAN Try,
210  BOOLEAN RetExpected)
211 {
214 
215  ThreadData->Try = Try;
216  ThreadData->Irql = Irql;
217  ThreadData->RetExpected = RetExpected;
223 
225 }
226 
227 static
228 VOID
231 {
233 
234  KeSetEvent(&ThreadData->InEvent, 0, TRUE);
237 
241  KeClearEvent(&ThreadData->InEvent);
242  KeClearEvent(&ThreadData->OutEvent);
243 }
244 
245 static
246 VOID
249 {
252  THREAD_DATA ThreadData2;
253  THREAD_DATA ThreadDataUnsafe;
254  THREAD_DATA ThreadDataTry;
256  Timeout.QuadPart = -10 * 1000 * 10; /* 10 ms */
257 
262 
263  /* have a thread acquire the mutex */
267  /* have a second thread try to acquire it -- should fail */
268  Status = StartThread(&ThreadDataTry, NULL, PASSIVE_LEVEL, TRUE, FALSE);
271  FinishThread(&ThreadDataTry);
272 
273  /* have another thread acquire it -- should block */
274  Status = StartThread(&ThreadData2, &Timeout, APC_LEVEL, FALSE, FALSE);
277 
278  /* finish the first thread -- now the second should become available */
282  CheckMutex(Mutex, 0L, ThreadData2.Thread, 1LU, APC_LEVEL, PASSIVE_LEVEL);
283 
284  /* block two more threads */
285  Status = StartThread(&ThreadDataUnsafe, &Timeout, APC_LEVEL, FALSE, FALSE);
287  CheckMutex(Mutex, -1L, ThreadData2.Thread, 2LU, APC_LEVEL, PASSIVE_LEVEL);
288 
291  CheckMutex(Mutex, -2L, ThreadData2.Thread, 3LU, APC_LEVEL, PASSIVE_LEVEL);
292 
293  /* finish 1 */
294  FinishThread(&ThreadData2);
297  CheckMutex(Mutex, -1L, ThreadDataUnsafe.Thread, 3LU, APC_LEVEL, PASSIVE_LEVEL);
298 
299  /* finish 2 */
300  FinishThread(&ThreadDataUnsafe);
304 
305  /* finish 3 */
307 
309 }
310 
311 START_TEST(ExFastMutex)
312 {
314  KIRQL Irql;
315 
316  pExEnterCriticalRegionAndAcquireFastMutexUnsafe = KmtGetSystemRoutineAddress(L"ExEnterCriticalRegionAndAcquireFastMutexUnsafe");
317  pExReleaseFastMutexUnsafeAndLeaveCriticalRegion = KmtGetSystemRoutineAddress(L"ExReleaseFastMutexUnsafeAndLeaveCriticalRegion");
318 
319  pExiAcquireFastMutex = KmtGetSystemRoutineAddress(L"ExiAcquireFastMutex");
320  pExiReleaseFastMutex = KmtGetSystemRoutineAddress(L"ExiReleaseFastMutex");
321  pExiTryToAcquireFastMutex = KmtGetSystemRoutineAddress(L"ExiTryToAcquireFastMutex");
322 
323  memset(&Mutex, 0x55, sizeof Mutex);
325  CheckMutex(&Mutex, 1L, NULL, 0LU, 0x55555555LU, PASSIVE_LEVEL);
326 
330  if (!KmtIsCheckedBuild)
331  {
336  }
338 
340 }
static VOID TestFastMutexConcurrent(PFAST_MUTEX Mutex)
Definition: ExFastMutex.c:247
static NTSTATUS StartThread(PTHREAD_DATA ThreadData, PLARGE_INTEGER Timeout, KIRQL Irql, BOOLEAN Try, BOOLEAN RetExpected)
Definition: ExFastMutex.c:205
#define IN
Definition: typedefs.h:38
BOOLEAN(FASTCALL * PMUTEX_TRY_FUNCTION)(PFAST_MUTEX)
Definition: ExFastMutex.c:138
#define GENERIC_ALL
Definition: nt_native.h:92
#define KeRaiseIrql(irql, oldIrql)
Definition: env_spec_w32.h:597
#define TRUE
Definition: types.h:120
KEVENT InEvent
Definition: ExFastMutex.c:151
#define KeLowerIrql(oldIrql)
Definition: env_spec_w32.h:602
_In_ BOOLEAN Release
Definition: classpnp.h:929
PMUTEX_FUNCTION Release
Definition: ExFastMutex.c:148
NTSYSAPI NTSTATUS NTAPI ZwClose(_In_ HANDLE Handle)
LONG NTSTATUS
Definition: precomp.h:26
VOID NTAPI ObDereferenceObject(IN PVOID Object)
Definition: obref.c:375
PMUTEX_FUNCTION Acquire
Definition: ExFastMutex.c:146
_In_ PVOID Parameter
Definition: ldrtypes.h:240
START_TEST(ExFastMutex)
Definition: ExFastMutex.c:311
LONG NTAPI KeSetEvent(IN PKEVENT Event, IN KPRIORITY Increment, IN BOOLEAN Wait)
Definition: eventobj.c:159
NTSTATUS NTAPI KeWaitForSingleObject(IN PVOID Object, IN KWAIT_REASON WaitReason, IN KPROCESSOR_MODE WaitMode, IN BOOLEAN Alertable, IN PLARGE_INTEGER Timeout OPTIONAL)
Definition: wait.c:416
#define FASTCALL
Definition: nt_native.h:50
_Out_ PKIRQL Irql
Definition: csq.h:179
VOID FASTCALL ExReleaseFastMutex(IN PFAST_MUTEX FastMutex)
Definition: fmutex.c:31
Definition: Mutex.h:15
VOID FASTCALL ExReleaseFastMutexUnsafe(IN OUT PFAST_MUTEX FastMutex)
Definition: fmutex.c:86
PKTHREAD Thread
Definition: ExFastMutex.c:143
static VOID FinishThread(PTHREAD_DATA ThreadData)
Definition: ExFastMutex.c:229
#define ok_bool_false(value, desc)
Definition: kmt_test.h:257
#define STATUS_TIMEOUT
Definition: ntstatus.h:81
UCHAR KIRQL
Definition: env_spec_w32.h:591
BOOLEAN KmtIsCheckedBuild
#define ok_bool_true(value, desc)
Definition: kmt_test.h:256
BOOLEAN RetExpected
Definition: ExFastMutex.c:150
NTSTATUS NTAPI ObReferenceObjectByHandle(IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL)
Definition: obref.c:496
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
KEVENT OutEvent
Definition: ExFastMutex.c:152
PFAST_MUTEX Mutex
Definition: ExFastMutex.c:145
unsigned char BOOLEAN
static VOID(FASTCALL *pExEnterCriticalRegionAndAcquireFastMutexUnsafe)(_Inout_ PFAST_MUTEX FastMutex)
smooth NULL
Definition: ftsmooth.c:416
VOID KmtSetIrql(IN KIRQL NewIrql)
BOOLEAN Try
Definition: ExFastMutex.c:149
#define ok_eq_bool(value, expected)
Definition: kmt_test.h:258
FAST_MUTEX
Definition: extypes.h:17
struct THREAD_DATA * PTHREAD_DATA
VOID FASTCALL ExAcquireFastMutexUnsafe(IN OUT PFAST_MUTEX FastMutex)
Definition: fmutex.c:75
FORCEINLINE VOID ExInitializeFastMutex(_Out_ PFAST_MUTEX FastMutex)
Definition: exfuncs.h:274
HANDLE Handle
Definition: drwtsn32.h:25
BOOLEAN FASTCALL ExTryToAcquireFastMutex(IN PFAST_MUTEX FastMutex)
Definition: fmutex.c:39
_In_opt_ PFILE_OBJECT _In_opt_ PETHREAD Thread
Definition: fltkernel.h:2653
PVOID KmtGetSystemRoutineAddress(IN PCWSTR RoutineName)
#define _Inout_
Definition: no_sal2.h:244
PMUTEX_TRY_FUNCTION TryAcquire
Definition: ExFastMutex.c:147
POBJECT_TYPE PsThreadType
Definition: thread.c:20
static const WCHAR L[]
Definition: oid.c:1250
#define SYNCHRONIZE
Definition: nt_native.h:61
#define PASSIVE_LEVEL
Definition: env_spec_w32.h:693
Status
Definition: gdiplustypes.h:24
BOOLEAN NTAPI KeAreApcsDisabled(VOID)
Definition: apc.c:958
#define DISPATCH_LEVEL
Definition: env_spec_w32.h:696
static VOID InitThreadData(PTHREAD_DATA ThreadData, PFAST_MUTEX Mutex, PMUTEX_FUNCTION Acquire, PMUTEX_TRY_FUNCTION TryAcquire, PMUTEX_FUNCTION Release)
Definition: ExFastMutex.c:188
#define HIGH_LEVEL
Definition: env_spec_w32.h:703
_Must_inspect_result_ _In_ USHORT _In_ PHIDP_PREPARSED_DATA _Out_writes_to_ LengthAttributes PHIDP_EXTENDED_ATTRIBUTES Attributes
Definition: hidpi.h:348
* PFAST_MUTEX
Definition: extypes.h:17
static ULONG Timeout
Definition: ping.c:61
#define KeInitializeEvent(pEvt, foo, foo2)
Definition: env_spec_w32.h:477
VOID FASTCALL ExAcquireFastMutex(IN PFAST_MUTEX FastMutex)
Definition: fmutex.c:23
static PFAST_MUTEX Mutex
Definition: timer.c:22
NTSTATUS NTAPI PsCreateSystemThread(OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN HANDLE ProcessHandle, IN PCLIENT_ID ClientId, IN PKSTART_ROUTINE StartRoutine, IN PVOID StartContext)
Definition: thread.c:602
HANDLE Handle
Definition: ExFastMutex.c:142
static BOOLEAN(FASTCALL *pExiTryToAcquireFastMutex)(IN OUT PFAST_MUTEX FastMutex)
#define skip(...)
static VOID NTAPI AcquireMutexThread(PVOID Parameter)
Definition: ExFastMutex.c:158
#define OUT
Definition: typedefs.h:39
#define InitializeObjectAttributes(p, n, a, r, s)
Definition: reg.c:106
#define CheckMutex(Mutex, ExpectedCount, ExpectedOwner, ExpectedContention, ExpectedOldIrql, ExpectedIrql)
Definition: ExFastMutex.c:31
#define ok_eq_hex(value, expected)
#define KeGetCurrentThread
Definition: hal.h:44
VOID NTAPI KeClearEvent(IN PKEVENT Event)
Definition: eventobj.c:22
return STATUS_SUCCESS
Definition: btrfs.c:2777
VOID(FASTCALL * PMUTEX_FUNCTION)(PFAST_MUTEX)
Definition: ExFastMutex.c:137
#define memset(x, y, z)
Definition: compat.h:39
static VOID TestFastMutex(PFAST_MUTEX Mutex, KIRQL OriginalIrql)
Definition: ExFastMutex.c:45
#define OBJ_KERNEL_HANDLE
Definition: winternl.h:231
#define APC_LEVEL
Definition: env_spec_w32.h:695
#define ok_eq_long(value, expected)
Definition: kmt_test.h:240