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 <thomas.faber@reactos.org>
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
25 _IRQL_requires_min_(DISPATCH_LEVEL
)
26 _Post_satisfies_(return == 1 || return == 0)
29 *pKeTryToAcquireSpinLockAtDpcLevel
)(
30 _Inout_
_Requires_lock_not_held_(*_Curr_
)
31 _When_(return!=0, _Acquires_lock_(*_Curr_
))
32 PKSPIN_LOCK SpinLock
);
37 *pKeAcquireInStackQueuedSpinLockForDpc
)(
38 IN OUT PKSPIN_LOCK SpinLock
,
39 OUT PKLOCK_QUEUE_HANDLE LockHandle
);
44 *pKeReleaseInStackQueuedSpinLockForDpc
)(
45 IN PKLOCK_QUEUE_HANDLE LockHandle
);
52 _In_ PKSPIN_LOCK SpinLock
);
54 /* TODO: multiprocessor testing */
57 typedef struct _CHECK_DATA CHECK_DATA
, *PCHECK_DATA
;
59 typedef VOID (*PACQUIRE_FUNCTION
)(PKSPIN_LOCK
, PCHECK_DATA
);
60 typedef VOID (*PRELEASE_FUNCTION
)(PKSPIN_LOCK
, PCHECK_DATA
);
61 typedef BOOLEAN (*PTRY_FUNCTION
)(PKSPIN_LOCK
, PCHECK_DATA
);
71 KIRQL IrqlWhenAcquired
;
72 PACQUIRE_FUNCTION Acquire
;
73 PRELEASE_FUNCTION Release
;
74 PTRY_FUNCTION TryAcquire
;
75 PACQUIRE_FUNCTION AcquireNoRaise
;
76 PRELEASE_FUNCTION ReleaseNoLower
;
77 PTRY_FUNCTION TryAcquireNoRaise
;
78 KSPIN_LOCK_QUEUE_NUMBER QueueNumber
;
79 BOOLEAN TryRetOnFailure
;
82 _ANONYMOUS_UNION
union
84 KLOCK_QUEUE_HANDLE QueueHandle
;
85 PKSPIN_LOCK_QUEUE Queue
;
91 #define DEFINE_ACQUIRE(LocalName, SetIsAcquired, DoCall) \
92 static VOID LocalName(PKSPIN_LOCK SpinLock, PCHECK_DATA CheckData) \
94 ASSERT(!CheckData->IsAcquired); \
96 if (SetIsAcquired) CheckData->IsAcquired = TRUE; \
99 #define DEFINE_RELEASE(LocalName, SetIsAcquired, DoCall) \
100 static VOID LocalName(PKSPIN_LOCK SpinLock, PCHECK_DATA CheckData) \
103 if (SetIsAcquired) CheckData->IsAcquired = FALSE; \
106 DEFINE_ACQUIRE(AcquireNormal
, TRUE
, KeAcquireSpinLock(SpinLock
, &CheckData
->Irql
))
107 DEFINE_RELEASE(ReleaseNormal
, TRUE
, KeReleaseSpinLock(SpinLock
, CheckData
->Irql
))
109 DEFINE_ACQUIRE(AcquireExp
, TRUE
, (KeAcquireSpinLock
)(SpinLock
, &CheckData
->Irql
))
110 DEFINE_RELEASE(ReleaseExp
, TRUE
, (KeReleaseSpinLock
)(SpinLock
, CheckData
->Irql
))
112 DEFINE_ACQUIRE(AcquireExp
, TRUE
, KeAcquireSpinLock(SpinLock
, &CheckData
->Irql
))
113 DEFINE_RELEASE(ReleaseExp
, TRUE
, KeReleaseSpinLock(SpinLock
, CheckData
->Irql
))
115 DEFINE_ACQUIRE(AcquireSynch
, TRUE
, CheckData
->Irql
= KeAcquireSpinLockRaiseToSynch(SpinLock
))
117 DEFINE_ACQUIRE(AcquireInStackQueued
, TRUE
, KeAcquireInStackQueuedSpinLock(SpinLock
, &CheckData
->QueueHandle
))
118 DEFINE_ACQUIRE(AcquireInStackSynch
, TRUE
, KeAcquireInStackQueuedSpinLockRaiseToSynch(SpinLock
, &CheckData
->QueueHandle
))
119 DEFINE_RELEASE(ReleaseInStackQueued
, TRUE
, KeReleaseInStackQueuedSpinLock(&CheckData
->QueueHandle
))
121 DEFINE_ACQUIRE(AcquireQueued
, TRUE
, CheckData
->Irql
= KeAcquireQueuedSpinLock(CheckData
->QueueNumber
))
122 DEFINE_ACQUIRE(AcquireQueuedSynch
, TRUE
, CheckData
->Irql
= KeAcquireQueuedSpinLockRaiseToSynch(CheckData
->QueueNumber
))
123 DEFINE_RELEASE(ReleaseQueued
, TRUE
, KeReleaseQueuedSpinLock(CheckData
->QueueNumber
, CheckData
->Irql
))
125 DEFINE_ACQUIRE(AcquireNoRaise
, FALSE
, KeAcquireSpinLockAtDpcLevel(SpinLock
))
126 DEFINE_RELEASE(ReleaseNoLower
, FALSE
, KeReleaseSpinLockFromDpcLevel(SpinLock
))
127 DEFINE_ACQUIRE(AcquireExpNoRaise
, FALSE
, (KeAcquireSpinLockAtDpcLevel
)(SpinLock
))
128 DEFINE_RELEASE(ReleaseExpNoLower
, FALSE
, (KeReleaseSpinLockFromDpcLevel
)(SpinLock
))
130 DEFINE_ACQUIRE(AcquireInStackNoRaise
, FALSE
, KeAcquireInStackQueuedSpinLockAtDpcLevel(SpinLock
, &CheckData
->QueueHandle
))
131 DEFINE_RELEASE(ReleaseInStackNoRaise
, FALSE
, KeReleaseInStackQueuedSpinLockFromDpcLevel(&CheckData
->QueueHandle
))
133 /* TODO: test these functions. They behave weirdly, though */
135 DEFINE_ACQUIRE(AcquireForDpc
, TRUE
, CheckData
->Irql
= KeAcquireSpinLockForDpc(SpinLock
))
136 DEFINE_RELEASE(ReleaseForDpc
, TRUE
, KeReleaseSpinLockForDpc(SpinLock
, CheckData
->Irql
))
139 DEFINE_ACQUIRE(AcquireInStackForDpc
, FALSE
, pKeAcquireInStackQueuedSpinLockForDpc(SpinLock
, &CheckData
->QueueHandle
))
140 DEFINE_RELEASE(ReleaseInStackForDpc
, FALSE
, pKeReleaseInStackQueuedSpinLockForDpc(&CheckData
->QueueHandle
))
143 DEFINE_ACQUIRE(AcquireInt
, FALSE
, KiAcquireSpinLock(SpinLock
))
144 DEFINE_RELEASE(ReleaseInt
, FALSE
, KiReleaseSpinLock(SpinLock
))
146 DEFINE_ACQUIRE(AcquireInt
, TRUE
, KeAcquireSpinLock(SpinLock
, &CheckData
->Irql
))
147 DEFINE_RELEASE(ReleaseInt
, TRUE
, KeReleaseSpinLock(SpinLock
, CheckData
->Irql
))
150 BOOLEAN
TryQueued(PKSPIN_LOCK SpinLock
, PCHECK_DATA CheckData
) {
151 LOGICAL Ret
= KeTryToAcquireQueuedSpinLock(CheckData
->QueueNumber
, &CheckData
->Irql
);
152 CheckData
->IsAcquired
= TRUE
;
153 ASSERT(Ret
== FALSE
|| Ret
== TRUE
);
156 BOOLEAN
TryQueuedSynch(PKSPIN_LOCK SpinLock
, PCHECK_DATA CheckData
) {
157 BOOLEAN Ret
= KeTryToAcquireQueuedSpinLockRaiseToSynch(CheckData
->QueueNumber
, &CheckData
->Irql
);
158 CheckData
->IsAcquired
= TRUE
;
161 BOOLEAN
TryNoRaise(PKSPIN_LOCK SpinLock
, PCHECK_DATA CheckData
) {
162 BOOLEAN Ret
= pKeTryToAcquireSpinLockAtDpcLevel(SpinLock
);
166 #define CheckSpinLockLock(SpinLock, CheckData, Value) do \
168 PKTHREAD Thread = KeGetCurrentThread(); \
169 if (KmtIsMultiProcessorBuild) \
171 ok_eq_bool(Ret, (Value) == 0); \
173 ok_eq_ulongptr(*(SpinLock), \
174 (Value) ? (ULONG_PTR)Thread | 1 : 0); \
178 ok_bool_true(Ret, "KeTestSpinLock returned"); \
180 ok_eq_ulongptr(*(SpinLock), 0); \
182 ok_eq_uint((CheckData)->Irql, (CheckData)->OriginalIrql); \
185 #define CheckSpinLockQueue(SpinLock, CheckData, Value) do \
187 ok_eq_pointer((CheckData)->Queue->Next, NULL); \
188 ok_eq_pointer((CheckData)->Queue->Lock, NULL); \
189 ok_eq_uint((CheckData)->Irql, (CheckData)->OriginalIrql); \
192 #define CheckSpinLockQueueHandle(SpinLock, CheckData, Value) do \
194 if (KmtIsMultiProcessorBuild) \
196 ok_eq_bool(Ret, (Value) == 0); \
198 ok_eq_ulongptr(*(SpinLock), \
199 (Value) ? &(CheckData)->QueueHandle : 0); \
200 ok_eq_pointer((CheckData)->QueueHandle.LockQueue.Next, NULL); \
201 ok_eq_pointer((CheckData)->QueueHandle.LockQueue.Lock, \
202 (PVOID)((ULONG_PTR)SpinLock | ((Value) ? 2 : 0))); \
206 ok_bool_true(Ret, "KeTestSpinLock returned"); \
208 ok_eq_ulongptr(*(SpinLock), 0); \
209 ok_eq_pointer((CheckData)->QueueHandle.LockQueue.Next, (CheckData)->UntouchedValue); \
210 ok_eq_pointer((CheckData)->QueueHandle.LockQueue.Lock, (CheckData)->UntouchedValue); \
212 ok_eq_uint((CheckData)->QueueHandle.OldIrql, (CheckData)->OriginalIrql); \
215 #define CheckSpinLock(SpinLock, CheckData, Value) do \
217 BOOLEAN Ret = SpinLock && pKeTestSpinLock ? pKeTestSpinLock(SpinLock) : TRUE; \
218 KIRQL ExpectedIrql = (CheckData)->OriginalIrql; \
220 switch ((CheckData)->Check) \
223 CheckSpinLockLock(SpinLock, CheckData, Value); \
226 CheckSpinLockQueue(SpinLock, CheckData, Value); \
228 case CheckQueueHandle: \
229 CheckSpinLockQueueHandle(SpinLock, CheckData, Value); \
233 if ((CheckData)->IsAcquired) \
234 ExpectedIrql = (CheckData)->IrqlWhenAcquired; \
235 ok_irql(ExpectedIrql); \
236 ok_bool_false(KeAreApcsDisabled(), "KeAreApcsDisabled returned"); \
237 ok_bool_true(KmtAreInterruptsEnabled(), "Interrupts enabled:"); \
243 PKSPIN_LOCK SpinLock
,
244 PCHECK_DATA CheckData
)
247 trace("Test SpinLock run %d\n", Run
++);
249 ok_irql(CheckData
->OriginalIrql
);
252 ok_eq_ulongptr(*SpinLock
, 0);
253 CheckData
->Acquire(SpinLock
, CheckData
);
254 CheckSpinLock(SpinLock
, CheckData
, 1);
255 CheckData
->Release(SpinLock
, CheckData
);
256 CheckSpinLock(SpinLock
, CheckData
, 0);
258 if (CheckData
->TryAcquire
)
260 CheckSpinLock(SpinLock
, CheckData
, 0);
261 ok_bool_true(CheckData
->TryAcquire(SpinLock
, CheckData
), "TryAcquire returned");
262 CheckSpinLock(SpinLock
, CheckData
, 1);
263 if (!KmtIsCheckedBuild
)
265 /* SPINLOCK_ALREADY_OWNED on checked build */
266 ok_bool_true(CheckData
->TryAcquire(SpinLock
, CheckData
), "TryAcquire returned");
267 /* even a failing acquire sets irql */
268 ok_eq_uint(CheckData
->Irql
, CheckData
->IrqlWhenAcquired
);
269 CheckData
->Irql
= CheckData
->OriginalIrql
;
270 CheckSpinLock(SpinLock
, CheckData
, 1);
272 CheckData
->Release(SpinLock
, CheckData
);
273 CheckSpinLock(SpinLock
, CheckData
, 0);
276 if (CheckData
->AcquireNoRaise
&&
277 (CheckData
->OriginalIrql
>= DISPATCH_LEVEL
|| !KmtIsCheckedBuild
) &&
278 (CheckData
->AcquireNoRaise
!= AcquireInStackForDpc
||
279 !skip(pKeAcquireInStackQueuedSpinLockForDpc
&&
280 pKeReleaseInStackQueuedSpinLockForDpc
, "No DPC spinlock functions\n")))
282 /* acquire/release without irql change */
283 CheckData
->AcquireNoRaise(SpinLock
, CheckData
);
284 CheckSpinLock(SpinLock
, CheckData
, 1);
285 CheckData
->ReleaseNoLower(SpinLock
, CheckData
);
286 CheckSpinLock(SpinLock
, CheckData
, 0);
288 /* acquire without raise, but normal release */
289 CheckData
->AcquireNoRaise(SpinLock
, CheckData
);
290 CheckSpinLock(SpinLock
, CheckData
, 1);
291 CheckData
->Release(SpinLock
, CheckData
);
292 CheckSpinLock(SpinLock
, CheckData
, 0);
294 /* acquire normally but release without lower */
295 CheckData
->Acquire(SpinLock
, CheckData
);
296 CheckSpinLock(SpinLock
, CheckData
, 1);
297 CheckData
->ReleaseNoLower(SpinLock
, CheckData
);
298 CheckSpinLock(SpinLock
, CheckData
, 0);
299 CheckData
->IsAcquired
= FALSE
;
300 KmtSetIrql(CheckData
->OriginalIrql
);
302 if (CheckData
->TryAcquireNoRaise
&&
303 !skip(pKeTryToAcquireSpinLockAtDpcLevel
!= NULL
, "KeTryToAcquireSpinLockAtDpcLevel unavailable\n"))
305 CheckSpinLock(SpinLock
, CheckData
, 0);
306 ok_bool_true(CheckData
->TryAcquireNoRaise(SpinLock
, CheckData
), "TryAcquireNoRaise returned");
307 CheckSpinLock(SpinLock
, CheckData
, 1);
308 if (!KmtIsCheckedBuild
)
310 ok_bool_true(CheckData
->TryAcquireNoRaise(SpinLock
, CheckData
), "TryAcquireNoRaise returned");
311 CheckSpinLock(SpinLock
, CheckData
, 1);
313 CheckData
->ReleaseNoLower(SpinLock
, CheckData
);
314 CheckSpinLock(SpinLock
, CheckData
, 0);
318 ok_irql(CheckData
->OriginalIrql
);
319 /* make sure we survive this in case of error */
320 KmtSetIrql(CheckData
->OriginalIrql
);
323 START_TEST(KeSpinLock
)
325 KSPIN_LOCK SpinLock
= (KSPIN_LOCK
)0x5555555555555555LL
;
326 PKSPIN_LOCK pSpinLock
= &SpinLock
;
327 KIRQL Irql
, SynchIrql
= KmtIsMultiProcessorBuild
? IPI_LEVEL
- 2 : DISPATCH_LEVEL
;
328 KIRQL OriginalIrqls
[] = { PASSIVE_LEVEL
, APC_LEVEL
, DISPATCH_LEVEL
, HIGH_LEVEL
};
329 CHECK_DATA TestData
[] =
331 { CheckLock
, DISPATCH_LEVEL
, AcquireNormal
, ReleaseNormal
, NULL
, AcquireNoRaise
, ReleaseNoLower
, TryNoRaise
},
332 { CheckLock
, DISPATCH_LEVEL
, AcquireExp
, ReleaseExp
, NULL
, AcquireExpNoRaise
, ReleaseExpNoLower
, NULL
},
333 /* TODO: this one is just weird!
334 { CheckLock, DISPATCH_LEVEL, AcquireNormal, ReleaseNormal, NULL, AcquireForDpc, ReleaseForDpc, NULL },*/
335 { CheckLock
, DISPATCH_LEVEL
, AcquireNormal
, ReleaseNormal
, NULL
, AcquireInt
, ReleaseInt
, NULL
},
336 { CheckLock
, SynchIrql
, AcquireSynch
, ReleaseNormal
, NULL
, NULL
, NULL
, NULL
},
337 { CheckQueueHandle
, DISPATCH_LEVEL
, AcquireInStackQueued
, ReleaseInStackQueued
, NULL
, AcquireInStackNoRaise
, ReleaseInStackNoRaise
, NULL
},
338 { CheckQueueHandle
, SynchIrql
, AcquireInStackSynch
, ReleaseInStackQueued
, NULL
, NULL
, NULL
, NULL
},
339 { CheckQueueHandle
, DISPATCH_LEVEL
, AcquireInStackQueued
, ReleaseInStackQueued
, NULL
, AcquireInStackForDpc
, ReleaseInStackForDpc
, NULL
},
340 { CheckQueue
, DISPATCH_LEVEL
, AcquireQueued
, ReleaseQueued
, TryQueued
, NULL
, NULL
, NULL
, LockQueuePfnLock
},
341 { CheckQueue
, SynchIrql
, AcquireQueuedSynch
, ReleaseQueued
, TryQueuedSynch
, NULL
, NULL
, NULL
, LockQueuePfnLock
},
346 pKeTryToAcquireSpinLockAtDpcLevel
= KmtGetSystemRoutineAddress(L
"KeTryToAcquireSpinLockAtDpcLevel");
347 pKeAcquireInStackQueuedSpinLockForDpc
= KmtGetSystemRoutineAddress(L
"KeAcquireInStackQueuedSpinLockForDpc");
348 pKeReleaseInStackQueuedSpinLockForDpc
= KmtGetSystemRoutineAddress(L
"KeReleaseInStackQueuedSpinLockForDpc");
349 pKeTestSpinLock
= KmtGetSystemRoutineAddress(L
"KeTestSpinLock");
351 Prcb
= KeGetCurrentPrcb();
353 /* KeInitializeSpinLock */
354 memset(&SpinLock
, 0x55, sizeof SpinLock
);
355 KeInitializeSpinLock(&SpinLock
);
356 ok_eq_ulongptr(SpinLock
, 0);
359 if (!skip(pKeTestSpinLock
!= NULL
, "KeTestSpinLock unavailable\n"))
361 ok_bool_true(pKeTestSpinLock(&SpinLock
), "KeTestSpinLock returned");
363 ok_bool_false(pKeTestSpinLock(&SpinLock
), "KeTestSpinLock returned");
365 ok_bool_false(pKeTestSpinLock(&SpinLock
), "KeTestSpinLock returned");
366 SpinLock
= (ULONG_PTR
)-1;
367 ok_bool_false(pKeTestSpinLock(&SpinLock
), "KeTestSpinLock returned");
368 SpinLock
= (ULONG_PTR
)1 << (sizeof(ULONG_PTR
) * CHAR_BIT
- 1);
369 ok_bool_false(pKeTestSpinLock(&SpinLock
), "KeTestSpinLock returned");
371 ok_bool_true(pKeTestSpinLock(&SpinLock
), "KeTestSpinLock returned");
374 /* on UP none of the following functions actually looks at the spinlock! */
375 if (!KmtIsMultiProcessorBuild
&& !KmtIsCheckedBuild
)
378 for (i
= 0; i
< sizeof TestData
/ sizeof TestData
[0]; ++i
)
380 memset(&SpinLock
, 0x55, sizeof SpinLock
);
381 KeInitializeSpinLock(&SpinLock
);
382 if (TestData
[i
].Check
== CheckQueueHandle
)
383 memset(&TestData
[i
].QueueHandle
, 0x55, sizeof TestData
[i
].QueueHandle
);
384 if (TestData
[i
].Check
== CheckQueue
)
386 TestData
[i
].Queue
= &Prcb
->LockQueue
[TestData
[i
].QueueNumber
];
387 TestData
[i
].UntouchedValue
= NULL
;
390 TestData
[i
].UntouchedValue
= (PVOID
)0x5555555555555555LL
;
392 for (iIrql
= 0; iIrql
< sizeof OriginalIrqls
/ sizeof OriginalIrqls
[0]; ++iIrql
)
394 if (KmtIsCheckedBuild
&& OriginalIrqls
[iIrql
] > DISPATCH_LEVEL
)
396 KeRaiseIrql(OriginalIrqls
[iIrql
], &Irql
);
397 TestData
[i
].OriginalIrql
= OriginalIrqls
[iIrql
];
398 TestData
[i
].IsAcquired
= FALSE
;
399 TestSpinLock(pSpinLock
, &TestData
[i
]);
404 KmtSetIrql(PASSIVE_LEVEL
);