2 * PROJECT: ReactOS kernel-mode tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Kernel-Mode Test Suite Event test
5 * PROGRAMMER: Thomas Faber <thomas.faber@reactos.org>
10 #define CheckEvent(Event, ExpectedType, State, ExpectedWaitNext, \
11 Irql, ThreadList, ThreadCount) do \
14 PLIST_ENTRY TheEntry; \
16 ok_eq_uint((Event)->Header.Type, ExpectedType); \
17 ok_eq_uint((Event)->Header.Hand, sizeof *(Event) / sizeof(ULONG)); \
18 ok_eq_hex((Event)->Header.Lock & 0xFF00FF00L, 0x55005500L); \
19 ok_eq_long((Event)->Header.SignalState, State); \
20 TheEntry = (Event)->Header.WaitListHead.Flink; \
21 for (TheIndex = 0; TheIndex < (ThreadCount); ++TheIndex) \
23 TheThread = CONTAINING_RECORD(TheEntry, KTHREAD, \
24 WaitBlock[0].WaitListEntry); \
25 ok_eq_pointer(TheThread, (ThreadList)[TheIndex]); \
26 ok_eq_pointer(TheEntry->Flink->Blink, TheEntry); \
27 TheEntry = TheEntry->Flink; \
29 ok_eq_pointer(TheEntry, &(Event)->Header.WaitListHead); \
30 ok_eq_pointer(TheEntry->Flink->Blink, TheEntry); \
31 ok_eq_long(KeReadStateEvent(Event), State); \
32 ok_eq_bool(Thread->WaitNext, ExpectedWaitNext); \
41 IN KIRQL OriginalIrql
)
44 PKTHREAD Thread
= KeGetCurrentThread();
46 memset(Event
, 0x55, sizeof *Event
);
47 KeInitializeEvent(Event
, Type
, FALSE
);
48 CheckEvent(Event
, Type
, 0L, FALSE
, OriginalIrql
, (PVOID
*)NULL
, 0);
50 memset(Event
, 0x55, sizeof *Event
);
51 KeInitializeEvent(Event
, Type
, TRUE
);
52 CheckEvent(Event
, Type
, 1L, FALSE
, OriginalIrql
, (PVOID
*)NULL
, 0);
54 Event
->Header
.SignalState
= 0x12345678L
;
55 CheckEvent(Event
, Type
, 0x12345678L
, FALSE
, OriginalIrql
, (PVOID
*)NULL
, 0);
57 State
= KePulseEvent(Event
, 0, FALSE
);
58 CheckEvent(Event
, Type
, 0L, FALSE
, OriginalIrql
, (PVOID
*)NULL
, 0);
59 ok_eq_long(State
, 0x12345678L
);
61 Event
->Header
.SignalState
= 0x12345678L
;
63 CheckEvent(Event
, Type
, 0L, FALSE
, OriginalIrql
, (PVOID
*)NULL
, 0);
65 State
= KeSetEvent(Event
, 0, FALSE
);
66 CheckEvent(Event
, Type
, 1L, FALSE
, OriginalIrql
, (PVOID
*)NULL
, 0);
67 ok_eq_long(State
, 0L);
69 State
= KeResetEvent(Event
);
70 CheckEvent(Event
, Type
, 0L, FALSE
, OriginalIrql
, (PVOID
*)NULL
, 0);
71 ok_eq_long(State
, 1L);
73 Event
->Header
.SignalState
= 0x23456789L
;
74 State
= KeSetEvent(Event
, 0, FALSE
);
75 CheckEvent(Event
, Type
, 1L, FALSE
, OriginalIrql
, (PVOID
*)NULL
, 0);
76 ok_eq_long(State
, 0x23456789L
);
78 Event
->Header
.SignalState
= 0x3456789AL
;
79 State
= KeResetEvent(Event
);
80 CheckEvent(Event
, Type
, 0L, FALSE
, OriginalIrql
, (PVOID
*)NULL
, 0);
81 ok_eq_long(State
, 0x3456789AL
);
83 /* Irql is raised to DISPATCH_LEVEL here, which kills checked build,
84 * a spinlock is acquired and never released, which kills MP build */
85 if ((OriginalIrql
<= DISPATCH_LEVEL
|| !KmtIsCheckedBuild
) &&
86 !KmtIsMultiProcessorBuild
)
88 Event
->Header
.SignalState
= 0x456789ABL
;
89 State
= KeSetEvent(Event
, 0, TRUE
);
90 CheckEvent(Event
, Type
, 1L, TRUE
, DISPATCH_LEVEL
, (PVOID
*)NULL
, 0);
91 ok_eq_long(State
, 0x456789ABL
);
92 ok_eq_uint(Thread
->WaitIrql
, OriginalIrql
);
93 /* repair the "damage" */
94 Thread
->WaitNext
= FALSE
;
95 KmtSetIrql(OriginalIrql
);
97 Event
->Header
.SignalState
= 0x56789ABCL
;
98 State
= KePulseEvent(Event
, 0, TRUE
);
99 CheckEvent(Event
, Type
, 0L, TRUE
, DISPATCH_LEVEL
, (PVOID
*)NULL
, 0);
100 ok_eq_long(State
, 0x56789ABCL
);
101 ok_eq_uint(Thread
->WaitIrql
, OriginalIrql
);
102 /* repair the "damage" */
103 Thread
->WaitNext
= FALSE
;
104 KmtSetIrql(OriginalIrql
);
107 ok_irql(OriginalIrql
);
108 KmtSetIrql(OriginalIrql
);
116 volatile BOOLEAN Signal
;
117 } THREAD_DATA
, *PTHREAD_DATA
;
123 IN OUT PVOID Context
)
126 PTHREAD_DATA ThreadData
= Context
;
128 ok_irql(PASSIVE_LEVEL
);
129 ThreadData
->Signal
= TRUE
;
130 Status
= KeWaitForSingleObject(ThreadData
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
131 ok_eq_hex(Status
, STATUS_SUCCESS
);
132 ok_irql(PASSIVE_LEVEL
);
135 typedef LONG (NTAPI
*PSET_EVENT_FUNCTION
)(PRKEVENT
, KPRIORITY
, BOOLEAN
);
142 IN KIRQL OriginalIrql
,
143 PSET_EVENT_FUNCTION SetEvent
,
144 KPRIORITY PriorityIncrement
,
146 BOOLEAN SatisfiesAll
)
149 THREAD_DATA Threads
[5];
150 const INT ThreadCount
= sizeof Threads
/ sizeof Threads
[0];
152 LARGE_INTEGER LongTimeout
, ShortTimeout
;
154 KWAIT_BLOCK WaitBlock
[RTL_NUMBER_OF(Threads
)];
155 PVOID ThreadObjects
[RTL_NUMBER_OF(Threads
)];
157 PKTHREAD Thread
= KeGetCurrentThread();
159 LongTimeout
.QuadPart
= -100 * MILLISECOND
;
160 ShortTimeout
.QuadPart
= -1 * MILLISECOND
;
162 KeInitializeEvent(Event
, Type
, FALSE
);
164 for (i
= 0; i
< ThreadCount
; ++i
)
166 Threads
[i
].Event
= Event
;
167 Threads
[i
].Signal
= FALSE
;
168 Status
= PsCreateSystemThread(&Threads
[i
].Handle
, GENERIC_ALL
, NULL
, NULL
, NULL
, WaitForEventThread
, &Threads
[i
]);
169 ok_eq_hex(Status
, STATUS_SUCCESS
);
170 Status
= ObReferenceObjectByHandle(Threads
[i
].Handle
, SYNCHRONIZE
, *PsThreadType
, KernelMode
, (PVOID
*)&Threads
[i
].Thread
, NULL
);
171 ok_eq_hex(Status
, STATUS_SUCCESS
);
172 ThreadObjects
[i
] = Threads
[i
].Thread
;
173 Priority
= KeQueryPriorityThread(Threads
[i
].Thread
);
174 ok_eq_long(Priority
, 8L);
175 while (!Threads
[i
].Signal
)
177 Status
= KeDelayExecutionThread(KernelMode
, FALSE
, &ShortTimeout
);
178 if (Status
!= STATUS_SUCCESS
)
180 ok_eq_hex(Status
, STATUS_SUCCESS
);
183 CheckEvent(Event
, Type
, 0L, FALSE
, OriginalIrql
, ThreadObjects
, i
+ 1);
186 /* the threads shouldn't wake up on their own */
187 Status
= KeDelayExecutionThread(KernelMode
, FALSE
, &ShortTimeout
);
188 ok_eq_hex(Status
, STATUS_SUCCESS
);
190 for (i
= 0; i
< ThreadCount
; ++i
)
192 CheckEvent(Event
, Type
, 0L, FALSE
, OriginalIrql
, ThreadObjects
+ i
, ThreadCount
- i
);
193 State
= SetEvent(Event
, PriorityIncrement
+ i
, FALSE
);
195 ok_eq_long(State
, 0L);
196 CheckEvent(Event
, Type
, ExpectedState
, FALSE
, OriginalIrql
, ThreadObjects
+ i
+ 1, SatisfiesAll
? 0 : ThreadCount
- i
- 1);
197 Status
= KeWaitForMultipleObjects(ThreadCount
, ThreadObjects
, SatisfiesAll
? WaitAll
: WaitAny
, Executive
, KernelMode
, FALSE
, &LongTimeout
, WaitBlock
);
198 ok_eq_hex(Status
, STATUS_WAIT_0
+ i
);
201 for (; i
< ThreadCount
; ++i
)
203 Priority
= KeQueryPriorityThread(Threads
[i
].Thread
);
204 ok_eq_long(Priority
, max(min(8L + PriorityIncrement
, 15L), 8L));
208 Priority
= KeQueryPriorityThread(Threads
[i
].Thread
);
209 ok_eq_long(Priority
, max(min(8L + PriorityIncrement
+ i
, 15L), 8L));
210 /* replace the thread with the current thread - which will never signal */
211 if (!skip((Status
& 0x3F) < ThreadCount
, "Index out of bounds"))
212 ThreadObjects
[Status
& 0x3F] = Thread
;
213 Status
= KeWaitForMultipleObjects(ThreadCount
, ThreadObjects
, WaitAny
, Executive
, KernelMode
, FALSE
, &ShortTimeout
, WaitBlock
);
214 ok_eq_hex(Status
, STATUS_TIMEOUT
);
217 for (i
= 0; i
< ThreadCount
; ++i
)
219 ObDereferenceObject(Threads
[i
].Thread
);
220 Status
= ZwClose(Threads
[i
].Handle
);
221 ok_eq_hex(Status
, STATUS_SUCCESS
);
225 #define NUM_SCHED_TESTS 1000
232 KPRIORITY PriorityIncrement
;
233 ULONG CounterValues
[NUM_SCHED_TESTS
];
234 } COUNT_THREAD_DATA
, *PCOUNT_THREAD_DATA
;
240 IN OUT PVOID Context
)
242 PCOUNT_THREAD_DATA ThreadData
= Context
;
243 PKEVENT Event
= &ThreadData
->Event
;
244 volatile ULONG
*Counter
= &ThreadData
->Counter
;
245 ULONG
*CounterValue
= ThreadData
->CounterValues
;
248 Priority
= KeQueryPriorityThread(KeGetCurrentThread());
249 ok_eq_long(Priority
, 8L);
251 while (CounterValue
< &ThreadData
->CounterValues
[NUM_SCHED_TESTS
])
253 KeSetEvent(&ThreadData
->WaitEvent
, IO_NO_INCREMENT
, TRUE
);
254 KeWaitForSingleObject(Event
, Executive
, KernelMode
, FALSE
, NULL
);
255 *CounterValue
++ = *Counter
;
258 Priority
= KeQueryPriorityThread(KeGetCurrentThread());
259 ok_eq_long(Priority
, 8L + min(ThreadData
->PriorityIncrement
, 7));
268 PCOUNT_THREAD_DATA ThreadData
;
273 volatile ULONG
*Counter
;
274 KPRIORITY PriorityIncrement
;
277 UNREFERENCED_PARAMETER(Context
);
279 ThreadData
= ExAllocatePoolWithTag(PagedPool
, sizeof(*ThreadData
), 'CEmK');
280 if (skip(ThreadData
!= NULL
, "Out of memory\n"))
284 KeInitializeEvent(&ThreadData
->Event
, SynchronizationEvent
, FALSE
);
285 KeInitializeEvent(&ThreadData
->WaitEvent
, SynchronizationEvent
, FALSE
);
286 Counter
= &ThreadData
->Counter
;
288 for (PriorityIncrement
= 0; PriorityIncrement
<= 8; PriorityIncrement
++)
290 ThreadData
->PriorityIncrement
= PriorityIncrement
;
291 ThreadData
->Counter
= 0;
292 RtlFillMemory(ThreadData
->CounterValues
,
293 sizeof(ThreadData
->CounterValues
),
295 Thread
= KmtStartThread(CountThread
, ThreadData
);
296 Priority
= KeQueryPriorityThread(KeGetCurrentThread());
297 ok(Priority
== 8, "[%lu] Priority = %lu\n", PriorityIncrement
, Priority
);
298 for (i
= 1; i
<= NUM_SCHED_TESTS
; i
++)
300 Status
= KeWaitForSingleObject(&ThreadData
->WaitEvent
, Executive
, KernelMode
, FALSE
, NULL
);
301 ok_eq_hex(Status
, STATUS_SUCCESS
);
302 PreviousState
= KeSetEvent(&ThreadData
->Event
, PriorityIncrement
, FALSE
);
304 ok_eq_long(PreviousState
, 0L);
306 Priority
= KeQueryPriorityThread(KeGetCurrentThread());
307 ok(Priority
== 8, "[%lu] Priority = %lu\n", PriorityIncrement
, Priority
);
308 KmtFinishThread(Thread
, NULL
);
310 if (PriorityIncrement
== 0)
312 /* Both threads have the same priority, so either can win the race */
313 ok(ThreadData
->CounterValues
[0] == 0 || ThreadData
->CounterValues
[0] == 1,
314 "[%lu] Counter 0 = %lu\n",
315 PriorityIncrement
, ThreadData
->CounterValues
[0]);
319 /* CountThread has the higher priority, it will always win */
320 ok(ThreadData
->CounterValues
[0] == 0,
321 "[%lu] Counter 0 = %lu\n",
322 PriorityIncrement
, ThreadData
->CounterValues
[0]);
324 for (i
= 1; i
< NUM_SCHED_TESTS
; i
++)
326 if (PriorityIncrement
== 0)
328 ok(ThreadData
->CounterValues
[i
] == i
||
329 ThreadData
->CounterValues
[i
] == i
+ 1,
330 "[%lu] Counter %lu = %lu, expected %lu or %lu\n",
331 PriorityIncrement
, i
,
332 ThreadData
->CounterValues
[i
], i
, i
+ 1);
336 ok(ThreadData
->CounterValues
[i
] == ThreadData
->CounterValues
[i
- 1] + 1,
337 "[%lu] Counter %lu = %lu, expected %lu\n",
338 PriorityIncrement
, i
,
339 ThreadData
->CounterValues
[i
], ThreadData
->CounterValues
[i
- 1] + 1);
344 ExFreePoolWithTag(ThreadData
, 'CEmK');
352 KIRQL Irqls
[] = { PASSIVE_LEVEL
, APC_LEVEL
, DISPATCH_LEVEL
};
354 KPRIORITY PriorityIncrement
;
356 for (i
= 0; i
< RTL_NUMBER_OF(Irqls
); ++i
)
358 KeRaiseIrql(Irqls
[i
], &Irql
);
359 TestEventFunctional(&Event
, NotificationEvent
, Irqls
[i
]);
360 TestEventFunctional(&Event
, SynchronizationEvent
, Irqls
[i
]);
364 for (i
= 0; i
< RTL_NUMBER_OF(Irqls
); ++i
)
366 /* creating threads above DISPATCH_LEVEL... nope */
367 if (Irqls
[i
] >= DISPATCH_LEVEL
)
369 KeRaiseIrql(Irqls
[i
], &Irql
);
370 trace("IRQL: %u\n", Irqls
[i
]);
371 for (PriorityIncrement
= -1; PriorityIncrement
<= 8; ++PriorityIncrement
)
373 if (PriorityIncrement
< 0 && KmtIsCheckedBuild
)
375 trace("PriorityIncrement: %ld\n", PriorityIncrement
);
376 trace("-> Checking KeSetEvent, NotificationEvent\n");
377 TestEventConcurrent(&Event
, NotificationEvent
, Irqls
[i
], KeSetEvent
, PriorityIncrement
, 1, TRUE
);
378 trace("-> Checking KeSetEvent, SynchronizationEvent\n");
379 TestEventConcurrent(&Event
, SynchronizationEvent
, Irqls
[i
], KeSetEvent
, PriorityIncrement
, 0, FALSE
);
380 trace("-> Checking KePulseEvent, NotificationEvent\n");
381 TestEventConcurrent(&Event
, NotificationEvent
, Irqls
[i
], KePulseEvent
, PriorityIncrement
, 0, TRUE
);
382 trace("-> Checking KePulseEvent, SynchronizationEvent\n");
383 TestEventConcurrent(&Event
, SynchronizationEvent
, Irqls
[i
], KePulseEvent
, PriorityIncrement
, 0, FALSE
);
388 ok_irql(PASSIVE_LEVEL
);
389 KmtSetIrql(PASSIVE_LEVEL
);
391 Thread
= KmtStartThread(TestEventScheduling
, NULL
);
392 KmtFinishThread(Thread
, NULL
);