2 * PROJECT: ReactOS kernel-mode tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Kernel-Mode Test Suite Spin lock test
5 * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
9 __declspec(dllimport
) void __stdcall
KeAcquireSpinLock(unsigned long *, unsigned char *);
10 __declspec(dllimport
) void __stdcall
KeReleaseSpinLock(unsigned long *, unsigned char);
11 __declspec(dllimport
) void __stdcall
KeAcquireSpinLockAtDpcLevel(unsigned long *);
12 __declspec(dllimport
) void __stdcall
KeReleaseSpinLockFromDpcLevel(unsigned long *);
15 /* this define makes KeInitializeSpinLock not use the inlined version */
16 #define WIN9X_COMPAT_SPINLOCK
19 #include <ndk/kefuncs.h>
21 #include <pseh/pseh2.h>
27 /* TODO: these are documented for Vista+ */
31 KeAcquireInStackQueuedSpinLockForDpc(
32 IN OUT PKSPIN_LOCK SpinLock
,
33 OUT PKLOCK_QUEUE_HANDLE LockHandle
);
38 KeReleaseInStackQueuedSpinLockForDpc(
39 IN PKLOCK_QUEUE_HANDLE LockHandle
);
41 /* TODO: multiprocessor testing */
44 typedef struct _CHECK_DATA CHECK_DATA
, *PCHECK_DATA
;
46 typedef VOID (*PACQUIRE_FUNCTION
)(PKSPIN_LOCK
, PCHECK_DATA
);
47 typedef VOID (*PRELEASE_FUNCTION
)(PKSPIN_LOCK
, PCHECK_DATA
);
48 typedef BOOLEAN (*PTRY_FUNCTION
)(PKSPIN_LOCK
, PCHECK_DATA
);
58 KIRQL IrqlWhenAcquired
;
59 PACQUIRE_FUNCTION Acquire
;
60 PRELEASE_FUNCTION Release
;
61 PTRY_FUNCTION TryAcquire
;
62 PACQUIRE_FUNCTION AcquireNoRaise
;
63 PRELEASE_FUNCTION ReleaseNoLower
;
64 PTRY_FUNCTION TryAcquireNoRaise
;
65 KSPIN_LOCK_QUEUE_NUMBER QueueNumber
;
66 BOOLEAN TryRetOnFailure
;
69 _ANONYMOUS_UNION
union
71 KLOCK_QUEUE_HANDLE QueueHandle
;
72 PKSPIN_LOCK_QUEUE Queue
;
78 #define DEFINE_ACQUIRE(LocalName, SetIsAcquired, DoCall) \
79 static VOID LocalName(PKSPIN_LOCK SpinLock, PCHECK_DATA CheckData) \
81 ASSERT(!CheckData->IsAcquired); \
83 if (SetIsAcquired) CheckData->IsAcquired = TRUE; \
86 #define DEFINE_RELEASE(LocalName, SetIsAcquired, DoCall) \
87 static VOID LocalName(PKSPIN_LOCK SpinLock, PCHECK_DATA CheckData) \
90 if (SetIsAcquired) CheckData->IsAcquired = FALSE; \
93 DEFINE_ACQUIRE(AcquireNormal
, TRUE
, KeAcquireSpinLock(SpinLock
, &CheckData
->Irql
))
94 DEFINE_RELEASE(ReleaseNormal
, TRUE
, KeReleaseSpinLock(SpinLock
, CheckData
->Irql
))
95 DEFINE_ACQUIRE(AcquireExp
, TRUE
, (KeAcquireSpinLock
)(SpinLock
, &CheckData
->Irql
))
96 DEFINE_RELEASE(ReleaseExp
, TRUE
, (KeReleaseSpinLock
)(SpinLock
, CheckData
->Irql
))
97 DEFINE_ACQUIRE(AcquireSynch
, TRUE
, CheckData
->Irql
= KeAcquireSpinLockRaiseToSynch(SpinLock
))
99 DEFINE_ACQUIRE(AcquireInStackQueued
, TRUE
, KeAcquireInStackQueuedSpinLock(SpinLock
, &CheckData
->QueueHandle
))
100 DEFINE_ACQUIRE(AcquireInStackSynch
, TRUE
, KeAcquireInStackQueuedSpinLockRaiseToSynch(SpinLock
, &CheckData
->QueueHandle
))
101 DEFINE_RELEASE(ReleaseInStackQueued
, TRUE
, KeReleaseInStackQueuedSpinLock(&CheckData
->QueueHandle
))
103 DEFINE_ACQUIRE(AcquireQueued
, TRUE
, CheckData
->Irql
= KeAcquireQueuedSpinLock(CheckData
->QueueNumber
))
104 DEFINE_ACQUIRE(AcquireQueuedSynch
, TRUE
, CheckData
->Irql
= KeAcquireQueuedSpinLockRaiseToSynch(CheckData
->QueueNumber
))
105 DEFINE_RELEASE(ReleaseQueued
, TRUE
, KeReleaseQueuedSpinLock(CheckData
->QueueNumber
, CheckData
->Irql
))
107 DEFINE_ACQUIRE(AcquireNoRaise
, FALSE
, KeAcquireSpinLockAtDpcLevel(SpinLock
))
108 DEFINE_RELEASE(ReleaseNoLower
, FALSE
, KeReleaseSpinLockFromDpcLevel(SpinLock
))
109 DEFINE_ACQUIRE(AcquireExpNoRaise
, FALSE
, (KeAcquireSpinLockAtDpcLevel
)(SpinLock
))
110 DEFINE_RELEASE(ReleaseExpNoLower
, FALSE
, (KeReleaseSpinLockFromDpcLevel
)(SpinLock
))
112 DEFINE_ACQUIRE(AcquireInStackNoRaise
, FALSE
, KeAcquireInStackQueuedSpinLockAtDpcLevel(SpinLock
, &CheckData
->QueueHandle
))
113 DEFINE_RELEASE(ReleaseInStackNoRaise
, FALSE
, KeReleaseInStackQueuedSpinLockFromDpcLevel(&CheckData
->QueueHandle
))
115 DEFINE_ACQUIRE(AcquireForDpc
, TRUE
, CheckData
->Irql
= KeAcquireSpinLockForDpc(SpinLock
))
116 DEFINE_RELEASE(ReleaseForDpc
, TRUE
, KeReleaseSpinLockForDpc(SpinLock
, CheckData
->Irql
))
118 DEFINE_ACQUIRE(AcquireInStackForDpc
, FALSE
, KeAcquireInStackQueuedSpinLockForDpc(SpinLock
, &CheckData
->QueueHandle
))
119 DEFINE_RELEASE(ReleaseInStackForDpc
, FALSE
, KeReleaseInStackQueuedSpinLockForDpc(&CheckData
->QueueHandle
))
121 DEFINE_ACQUIRE(AcquireInt
, FALSE
, KiAcquireSpinLock(SpinLock
))
122 DEFINE_RELEASE(ReleaseInt
, FALSE
, KiReleaseSpinLock(SpinLock
))
124 BOOLEAN
TryQueued(PKSPIN_LOCK SpinLock
, PCHECK_DATA CheckData
) {
125 LOGICAL Ret
= KeTryToAcquireQueuedSpinLock(CheckData
->QueueNumber
, &CheckData
->Irql
);
126 CheckData
->IsAcquired
= TRUE
;
127 ASSERT(Ret
== FALSE
|| Ret
== TRUE
);
130 BOOLEAN
TryQueuedSynch(PKSPIN_LOCK SpinLock
, PCHECK_DATA CheckData
) {
131 BOOLEAN Ret
= KeTryToAcquireQueuedSpinLockRaiseToSynch(CheckData
->QueueNumber
, &CheckData
->Irql
);
132 CheckData
->IsAcquired
= TRUE
;
135 BOOLEAN
TryNoRaise(PKSPIN_LOCK SpinLock
, PCHECK_DATA CheckData
) {
136 BOOLEAN Ret
= KeTryToAcquireSpinLockAtDpcLevel(SpinLock
);
140 #define CheckSpinLockLock(SpinLock, CheckData, Value) do \
142 PKTHREAD Thread = KeGetCurrentThread(); \
143 if (KmtIsMultiProcessorBuild) \
145 ok_eq_bool(Ret, (Value) == 0); \
147 ok_eq_pointer((PVOID)*(SpinLock), \
148 (Value) ? (PVOID)((ULONG_PTR)Thread | 1) : 0); \
152 ok_bool_true(Ret, "KeTestSpinLock returned"); \
154 ok_eq_pointer((PVOID)*(SpinLock), NULL); \
156 ok_eq_uint((CheckData)->Irql, (CheckData)->OriginalIrql); \
159 #define CheckSpinLockQueue(SpinLock, CheckData, Value) do \
161 ok_eq_pointer((CheckData)->Queue->Next, NULL); \
162 ok_eq_pointer((CheckData)->Queue->Lock, NULL); \
163 ok_eq_uint((CheckData)->Irql, (CheckData)->OriginalIrql); \
166 #define CheckSpinLockQueueHandle(SpinLock, CheckData, Value) do \
168 if (KmtIsMultiProcessorBuild) \
170 ok_eq_bool(Ret, (Value) == 0); \
172 ok_eq_pointer((PVOID)*(SpinLock), \
173 (Value) ? &(CheckData)->QueueHandle : 0); \
174 ok_eq_pointer((CheckData)->QueueHandle.LockQueue.Next, NULL); \
175 ok_eq_pointer((CheckData)->QueueHandle.LockQueue.Lock, \
176 (PVOID)((ULONG_PTR)SpinLock | ((Value) ? 2 : 0))); \
180 ok_bool_true(Ret, "KeTestSpinLock returned"); \
182 ok_eq_pointer((PVOID)*(SpinLock), NULL); \
183 ok_eq_pointer((CheckData)->QueueHandle.LockQueue.Next, (CheckData)->UntouchedValue); \
184 ok_eq_pointer((CheckData)->QueueHandle.LockQueue.Lock, (CheckData)->UntouchedValue); \
186 ok_eq_uint((CheckData)->QueueHandle.OldIrql, (CheckData)->OriginalIrql); \
189 #define CheckSpinLock(SpinLock, CheckData, Value) do \
191 BOOLEAN Ret = SpinLock ? KeTestSpinLock(SpinLock) : TRUE; \
192 KIRQL ExpectedIrql = (CheckData)->OriginalIrql; \
194 switch ((CheckData)->Check) \
197 CheckSpinLockLock(SpinLock, CheckData, Value); \
200 CheckSpinLockQueue(SpinLock, CheckData, Value); \
202 case CheckQueueHandle: \
203 CheckSpinLockQueueHandle(SpinLock, CheckData, Value); \
207 if ((CheckData)->IsAcquired) \
208 ExpectedIrql = (CheckData)->IrqlWhenAcquired; \
209 ok_irql(ExpectedIrql); \
210 ok_bool_false(KeAreApcsDisabled(), "KeAreApcsDisabled returned"); \
211 ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:"); \
217 PKSPIN_LOCK SpinLock
,
218 PCHECK_DATA CheckData
)
221 trace("Test SpinLock run %d\n", Run
++);
223 ok_irql(CheckData
->OriginalIrql
);
226 ok_eq_pointer((PVOID
)*SpinLock
, NULL
);
227 CheckData
->Acquire(SpinLock
, CheckData
);
228 CheckSpinLock(SpinLock
, CheckData
, 1);
229 CheckData
->Release(SpinLock
, CheckData
);
230 CheckSpinLock(SpinLock
, CheckData
, 0);
232 if (CheckData
->TryAcquire
)
234 CheckSpinLock(SpinLock
, CheckData
, 0);
235 ok_bool_true(CheckData
->TryAcquire(SpinLock
, CheckData
), "TryAcquire returned");
236 CheckSpinLock(SpinLock
, CheckData
, 1);
237 if (!KmtIsCheckedBuild
)
239 /* SPINLOCK_ALREADY_OWNED on checked build */
240 ok_bool_true(CheckData
->TryAcquire(SpinLock
, CheckData
), "TryAcquire returned");
241 /* even a failing acquire sets irql */
242 ok_eq_uint(CheckData
->Irql
, CheckData
->IrqlWhenAcquired
);
243 CheckData
->Irql
= CheckData
->OriginalIrql
;
244 CheckSpinLock(SpinLock
, CheckData
, 1);
246 CheckData
->Release(SpinLock
, CheckData
);
247 CheckSpinLock(SpinLock
, CheckData
, 0);
250 if (CheckData
->AcquireNoRaise
&&
251 (CheckData
->OriginalIrql
>= DISPATCH_LEVEL
|| !KmtIsCheckedBuild
))
253 /* acquire/release without irql change */
254 CheckData
->AcquireNoRaise(SpinLock
, CheckData
);
255 CheckSpinLock(SpinLock
, CheckData
, 1);
256 CheckData
->ReleaseNoLower(SpinLock
, CheckData
);
257 CheckSpinLock(SpinLock
, CheckData
, 0);
259 /* acquire without raise, but normal release */
260 CheckData
->AcquireNoRaise(SpinLock
, CheckData
);
261 CheckSpinLock(SpinLock
, CheckData
, 1);
262 CheckData
->Release(SpinLock
, CheckData
);
263 CheckSpinLock(SpinLock
, CheckData
, 0);
265 /* acquire normally but release without lower */
266 CheckData
->Acquire(SpinLock
, CheckData
);
267 CheckSpinLock(SpinLock
, CheckData
, 1);
268 CheckData
->ReleaseNoLower(SpinLock
, CheckData
);
269 CheckSpinLock(SpinLock
, CheckData
, 0);
270 CheckData
->IsAcquired
= FALSE
;
271 KmtSetIrql(CheckData
->OriginalIrql
);
273 if (CheckData
->TryAcquireNoRaise
)
275 CheckSpinLock(SpinLock
, CheckData
, 0);
276 ok_bool_true(CheckData
->TryAcquireNoRaise(SpinLock
, CheckData
), "TryAcquireNoRaise returned");
277 CheckSpinLock(SpinLock
, CheckData
, 1);
278 if (!KmtIsCheckedBuild
)
280 ok_bool_true(CheckData
->TryAcquireNoRaise(SpinLock
, CheckData
), "TryAcquireNoRaise returned");
281 CheckSpinLock(SpinLock
, CheckData
, 1);
283 CheckData
->ReleaseNoLower(SpinLock
, CheckData
);
284 CheckSpinLock(SpinLock
, CheckData
, 0);
288 ok_irql(CheckData
->OriginalIrql
);
289 /* make sure we survive this in case of error */
290 KmtSetIrql(CheckData
->OriginalIrql
);
293 START_TEST(KeSpinLock
)
295 KSPIN_LOCK SpinLock
= (KSPIN_LOCK
)0x5555555555555555LL
;
296 PKSPIN_LOCK pSpinLock
= &SpinLock
;
297 KIRQL Irql
, SynchIrql
= KmtIsMultiProcessorBuild
? IPI_LEVEL
- 2 : DISPATCH_LEVEL
;
298 KIRQL OriginalIrqls
[] = { PASSIVE_LEVEL
, APC_LEVEL
, DISPATCH_LEVEL
, HIGH_LEVEL
};
299 CHECK_DATA TestData
[] =
301 { CheckLock
, DISPATCH_LEVEL
, AcquireNormal
, ReleaseNormal
, NULL
, AcquireNoRaise
, ReleaseNoLower
, TryNoRaise
},
302 { CheckLock
, DISPATCH_LEVEL
, AcquireExp
, ReleaseExp
, NULL
, AcquireExpNoRaise
, ReleaseExpNoLower
, NULL
},
303 /* TODO: this one is just weird!
304 { CheckLock, DISPATCH_LEVEL, AcquireNormal, ReleaseNormal, NULL, AcquireForDpc, ReleaseForDpc, NULL },*/
305 { CheckLock
, DISPATCH_LEVEL
, AcquireNormal
, ReleaseNormal
, NULL
, AcquireInt
, ReleaseInt
, NULL
},
306 { CheckLock
, SynchIrql
, AcquireSynch
, ReleaseNormal
, NULL
, NULL
, NULL
, NULL
},
307 { CheckQueueHandle
, DISPATCH_LEVEL
, AcquireInStackQueued
, ReleaseInStackQueued
, NULL
, AcquireInStackNoRaise
, ReleaseInStackNoRaise
, NULL
},
308 { CheckQueueHandle
, SynchIrql
, AcquireInStackSynch
, ReleaseInStackQueued
, NULL
, NULL
, NULL
, NULL
},
309 { CheckQueueHandle
, DISPATCH_LEVEL
, AcquireInStackQueued
, ReleaseInStackQueued
, NULL
, AcquireInStackForDpc
, ReleaseInStackForDpc
, NULL
},
310 { CheckQueue
, DISPATCH_LEVEL
, AcquireQueued
, ReleaseQueued
, TryQueued
, NULL
, NULL
, NULL
, LockQueuePfnLock
},
311 { CheckQueue
, SynchIrql
, AcquireQueuedSynch
, ReleaseQueued
, TryQueuedSynch
, NULL
, NULL
, NULL
, LockQueuePfnLock
},
314 PKPRCB Prcb
= KeGetCurrentPrcb();
316 /* KeInitializeSpinLock */
317 memset(&SpinLock
, 0x55, sizeof SpinLock
);
318 KeInitializeSpinLock(&SpinLock
);
319 ok_eq_pointer((PVOID
)SpinLock
, NULL
);
322 ok_bool_true(KeTestSpinLock(&SpinLock
), "KeTestSpinLock returned");
324 ok_bool_false(KeTestSpinLock(&SpinLock
), "KeTestSpinLock returned");
326 ok_bool_false(KeTestSpinLock(&SpinLock
), "KeTestSpinLock returned");
327 SpinLock
= (ULONG_PTR
)-1;
328 ok_bool_false(KeTestSpinLock(&SpinLock
), "KeTestSpinLock returned");
329 SpinLock
= (ULONG_PTR
)1 << (sizeof(ULONG_PTR
) * CHAR_BIT
- 1);
330 ok_bool_false(KeTestSpinLock(&SpinLock
), "KeTestSpinLock returned");
332 ok_bool_true(KeTestSpinLock(&SpinLock
), "KeTestSpinLock returned");
334 /* on UP none of the following functions actually looks at the spinlock! */
335 if (!KmtIsMultiProcessorBuild
&& !KmtIsCheckedBuild
)
338 for (i
= 0; i
< sizeof TestData
/ sizeof TestData
[0]; ++i
)
340 memset(&SpinLock
, 0x55, sizeof SpinLock
);
341 KeInitializeSpinLock(&SpinLock
);
342 if (TestData
[i
].Check
== CheckQueueHandle
)
343 memset(&TestData
[i
].QueueHandle
, 0x55, sizeof TestData
[i
].QueueHandle
);
344 if (TestData
[i
].Check
== CheckQueue
)
346 TestData
[i
].Queue
= &Prcb
->LockQueue
[TestData
[i
].QueueNumber
];
347 TestData
[i
].UntouchedValue
= NULL
;
350 TestData
[i
].UntouchedValue
= (PVOID
)0x5555555555555555LL
;
352 for (iIrql
= 0; iIrql
< sizeof OriginalIrqls
/ sizeof OriginalIrqls
[0]; ++iIrql
)
354 if (KmtIsCheckedBuild
&& OriginalIrqls
[iIrql
] > DISPATCH_LEVEL
)
356 KeRaiseIrql(OriginalIrqls
[iIrql
], &Irql
);
357 TestData
[i
].OriginalIrql
= OriginalIrqls
[iIrql
];
358 TestData
[i
].IsAcquired
= FALSE
;
359 TestSpinLock(pSpinLock
, &TestData
[i
]);
364 KmtSetIrql(PASSIVE_LEVEL
);