2cf19026c8894db645ae5617d56c0e6c149fb365
[reactos.git] / modules / rostests / kmtests / ntos_ke / KeEvent.c
1 /*
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>
6 */
7
8 #include <kmt_test.h>
9
10 #define CheckEvent(Event, ExpectedType, State, ExpectedWaitNext, \
11 Irql, ThreadList, ThreadCount) do \
12 { \
13 INT TheIndex; \
14 PLIST_ENTRY TheEntry; \
15 PKTHREAD TheThread; \
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) \
22 { \
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; \
28 } \
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); \
33 ok_irql(Irql); \
34 } while (0)
35
36 static
37 VOID
38 TestEventFunctional(
39 IN PKEVENT Event,
40 IN EVENT_TYPE Type,
41 IN KIRQL OriginalIrql)
42 {
43 LONG State;
44 PKTHREAD Thread = KeGetCurrentThread();
45
46 memset(Event, 0x55, sizeof *Event);
47 KeInitializeEvent(Event, Type, FALSE);
48 CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
49
50 memset(Event, 0x55, sizeof *Event);
51 KeInitializeEvent(Event, Type, TRUE);
52 CheckEvent(Event, Type, 1L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
53
54 Event->Header.SignalState = 0x12345678L;
55 CheckEvent(Event, Type, 0x12345678L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
56
57 State = KePulseEvent(Event, 0, FALSE);
58 CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
59 ok_eq_long(State, 0x12345678L);
60
61 Event->Header.SignalState = 0x12345678L;
62 KeClearEvent(Event);
63 CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
64
65 State = KeSetEvent(Event, 0, FALSE);
66 CheckEvent(Event, Type, 1L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
67 ok_eq_long(State, 0L);
68
69 State = KeResetEvent(Event);
70 CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
71 ok_eq_long(State, 1L);
72
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);
77
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);
82
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)
87 {
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);
96
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);
105 }
106
107 ok_irql(OriginalIrql);
108 KmtSetIrql(OriginalIrql);
109 }
110
111 typedef struct
112 {
113 HANDLE Handle;
114 PKTHREAD Thread;
115 PKEVENT Event;
116 volatile BOOLEAN Signal;
117 } THREAD_DATA, *PTHREAD_DATA;
118
119 static
120 VOID
121 NTAPI
122 WaitForEventThread(
123 IN OUT PVOID Context)
124 {
125 NTSTATUS Status;
126 PTHREAD_DATA ThreadData = Context;
127
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);
133 }
134
135 typedef LONG (NTAPI *PSET_EVENT_FUNCTION)(PRKEVENT, KPRIORITY, BOOLEAN);
136
137 static
138 VOID
139 TestEventConcurrent(
140 IN PKEVENT Event,
141 IN EVENT_TYPE Type,
142 IN KIRQL OriginalIrql,
143 PSET_EVENT_FUNCTION SetEvent,
144 KPRIORITY PriorityIncrement,
145 LONG ExpectedState,
146 BOOLEAN SatisfiesAll)
147 {
148 NTSTATUS Status;
149 THREAD_DATA Threads[5];
150 const INT ThreadCount = sizeof Threads / sizeof Threads[0];
151 KPRIORITY Priority;
152 LARGE_INTEGER LongTimeout, ShortTimeout;
153 INT i;
154 KWAIT_BLOCK WaitBlock[RTL_NUMBER_OF(Threads)];
155 PVOID ThreadObjects[RTL_NUMBER_OF(Threads)];
156 LONG State;
157 PKTHREAD Thread = KeGetCurrentThread();
158 OBJECT_ATTRIBUTES ObjectAttributes;
159
160 LongTimeout.QuadPart = -100 * MILLISECOND;
161 ShortTimeout.QuadPart = -1 * MILLISECOND;
162
163 KeInitializeEvent(Event, Type, FALSE);
164
165 for (i = 0; i < ThreadCount; ++i)
166 {
167 Threads[i].Event = Event;
168 Threads[i].Signal = FALSE;
169 InitializeObjectAttributes(&ObjectAttributes,
170 NULL,
171 OBJ_KERNEL_HANDLE,
172 NULL,
173 NULL);
174 Status = PsCreateSystemThread(&Threads[i].Handle, GENERIC_ALL, &ObjectAttributes, NULL, NULL, WaitForEventThread, &Threads[i]);
175 ok_eq_hex(Status, STATUS_SUCCESS);
176 Status = ObReferenceObjectByHandle(Threads[i].Handle, SYNCHRONIZE, *PsThreadType, KernelMode, (PVOID *)&Threads[i].Thread, NULL);
177 ok_eq_hex(Status, STATUS_SUCCESS);
178 ThreadObjects[i] = Threads[i].Thread;
179 Priority = KeQueryPriorityThread(Threads[i].Thread);
180 ok_eq_long(Priority, 8L);
181 while (!Threads[i].Signal)
182 {
183 Status = KeDelayExecutionThread(KernelMode, FALSE, &ShortTimeout);
184 if (Status != STATUS_SUCCESS)
185 {
186 ok_eq_hex(Status, STATUS_SUCCESS);
187 }
188 }
189 CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, ThreadObjects, i + 1);
190 }
191
192 /* the threads shouldn't wake up on their own */
193 Status = KeDelayExecutionThread(KernelMode, FALSE, &ShortTimeout);
194 ok_eq_hex(Status, STATUS_SUCCESS);
195
196 for (i = 0; i < ThreadCount; ++i)
197 {
198 CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, ThreadObjects + i, ThreadCount - i);
199 State = SetEvent(Event, PriorityIncrement + i, FALSE);
200
201 ok_eq_long(State, 0L);
202 CheckEvent(Event, Type, ExpectedState, FALSE, OriginalIrql, ThreadObjects + i + 1, SatisfiesAll ? 0 : ThreadCount - i - 1);
203 Status = KeWaitForMultipleObjects(ThreadCount, ThreadObjects, SatisfiesAll ? WaitAll : WaitAny, Executive, KernelMode, FALSE, &LongTimeout, WaitBlock);
204 ok_eq_hex(Status, STATUS_WAIT_0 + i);
205 if (SatisfiesAll)
206 {
207 for (; i < ThreadCount; ++i)
208 {
209 Priority = KeQueryPriorityThread(Threads[i].Thread);
210 ok_eq_long(Priority, max(min(8L + PriorityIncrement, 15L), 8L));
211 }
212 break;
213 }
214 Priority = KeQueryPriorityThread(Threads[i].Thread);
215 ok_eq_long(Priority, max(min(8L + PriorityIncrement + i, 15L), 8L));
216 /* replace the thread with the current thread - which will never signal */
217 if (!skip((Status & 0x3F) < ThreadCount, "Index out of bounds"))
218 ThreadObjects[Status & 0x3F] = Thread;
219 Status = KeWaitForMultipleObjects(ThreadCount, ThreadObjects, WaitAny, Executive, KernelMode, FALSE, &ShortTimeout, WaitBlock);
220 ok_eq_hex(Status, STATUS_TIMEOUT);
221 }
222
223 for (i = 0; i < ThreadCount; ++i)
224 {
225 ObDereferenceObject(Threads[i].Thread);
226 Status = ZwClose(Threads[i].Handle);
227 ok_eq_hex(Status, STATUS_SUCCESS);
228 }
229 }
230
231 #define NUM_SCHED_TESTS 1000
232
233 typedef struct
234 {
235 KEVENT Event;
236 KEVENT WaitEvent;
237 ULONG Counter;
238 KPRIORITY PriorityIncrement;
239 ULONG CounterValues[NUM_SCHED_TESTS];
240 } COUNT_THREAD_DATA, *PCOUNT_THREAD_DATA;
241
242 static
243 VOID
244 NTAPI
245 CountThread(
246 IN OUT PVOID Context)
247 {
248 PCOUNT_THREAD_DATA ThreadData = Context;
249 PKEVENT Event = &ThreadData->Event;
250 volatile ULONG *Counter = &ThreadData->Counter;
251 ULONG *CounterValue = ThreadData->CounterValues;
252 KPRIORITY Priority;
253
254 Priority = KeQueryPriorityThread(KeGetCurrentThread());
255 ok_eq_long(Priority, 8L);
256
257 while (CounterValue < &ThreadData->CounterValues[NUM_SCHED_TESTS])
258 {
259 KeSetEvent(&ThreadData->WaitEvent, IO_NO_INCREMENT, TRUE);
260 KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
261 *CounterValue++ = *Counter;
262 }
263
264 Priority = KeQueryPriorityThread(KeGetCurrentThread());
265 ok_eq_long(Priority, 8L + min(ThreadData->PriorityIncrement, 7));
266 }
267
268 static
269 VOID
270 NTAPI
271 TestEventScheduling(
272 _In_ PVOID Context)
273 {
274 PCOUNT_THREAD_DATA ThreadData;
275 PKTHREAD Thread;
276 NTSTATUS Status;
277 LONG PreviousState;
278 ULONG i;
279 volatile ULONG *Counter;
280 KPRIORITY PriorityIncrement;
281 KPRIORITY Priority;
282
283 UNREFERENCED_PARAMETER(Context);
284
285 ThreadData = ExAllocatePoolWithTag(PagedPool, sizeof(*ThreadData), 'CEmK');
286 if (skip(ThreadData != NULL, "Out of memory\n"))
287 {
288 return;
289 }
290 KeInitializeEvent(&ThreadData->Event, SynchronizationEvent, FALSE);
291 KeInitializeEvent(&ThreadData->WaitEvent, SynchronizationEvent, FALSE);
292 Counter = &ThreadData->Counter;
293
294 for (PriorityIncrement = 0; PriorityIncrement <= 8; PriorityIncrement++)
295 {
296 ThreadData->PriorityIncrement = PriorityIncrement;
297 ThreadData->Counter = 0;
298 RtlFillMemory(ThreadData->CounterValues,
299 sizeof(ThreadData->CounterValues),
300 0xFE);
301 Thread = KmtStartThread(CountThread, ThreadData);
302 Priority = KeQueryPriorityThread(KeGetCurrentThread());
303 ok(Priority == 8, "[%lu] Priority = %lu\n", PriorityIncrement, Priority);
304 for (i = 1; i <= NUM_SCHED_TESTS; i++)
305 {
306 Status = KeWaitForSingleObject(&ThreadData->WaitEvent, Executive, KernelMode, FALSE, NULL);
307 ok_eq_hex(Status, STATUS_SUCCESS);
308 PreviousState = KeSetEvent(&ThreadData->Event, PriorityIncrement, FALSE);
309 *Counter = i;
310 ok_eq_long(PreviousState, 0L);
311 }
312 Priority = KeQueryPriorityThread(KeGetCurrentThread());
313 ok(Priority == 8, "[%lu] Priority = %lu\n", PriorityIncrement, Priority);
314 KmtFinishThread(Thread, NULL);
315
316 if (PriorityIncrement == 0)
317 {
318 /* Both threads have the same priority, so either can win the race */
319 ok(ThreadData->CounterValues[0] == 0 || ThreadData->CounterValues[0] == 1,
320 "[%lu] Counter 0 = %lu\n",
321 PriorityIncrement, ThreadData->CounterValues[0]);
322 }
323 else
324 {
325 /* CountThread has the higher priority, it will always win */
326 ok(ThreadData->CounterValues[0] == 0,
327 "[%lu] Counter 0 = %lu\n",
328 PriorityIncrement, ThreadData->CounterValues[0]);
329 }
330 for (i = 1; i < NUM_SCHED_TESTS; i++)
331 {
332 if (PriorityIncrement == 0)
333 {
334 ok(ThreadData->CounterValues[i] == i ||
335 ThreadData->CounterValues[i] == i + 1,
336 "[%lu] Counter %lu = %lu, expected %lu or %lu\n",
337 PriorityIncrement, i,
338 ThreadData->CounterValues[i], i, i + 1);
339 }
340 else
341 {
342 ok(ThreadData->CounterValues[i] == ThreadData->CounterValues[i - 1] + 1,
343 "[%lu] Counter %lu = %lu, expected %lu\n",
344 PriorityIncrement, i,
345 ThreadData->CounterValues[i], ThreadData->CounterValues[i - 1] + 1);
346 }
347 }
348 }
349
350 ExFreePoolWithTag(ThreadData, 'CEmK');
351 }
352
353 START_TEST(KeEvent)
354 {
355 PKTHREAD Thread;
356 KEVENT Event;
357 KIRQL Irql;
358 KIRQL Irqls[] = { PASSIVE_LEVEL, APC_LEVEL, DISPATCH_LEVEL };
359 ULONG i;
360 KPRIORITY PriorityIncrement;
361
362 for (i = 0; i < RTL_NUMBER_OF(Irqls); ++i)
363 {
364 KeRaiseIrql(Irqls[i], &Irql);
365 TestEventFunctional(&Event, NotificationEvent, Irqls[i]);
366 TestEventFunctional(&Event, SynchronizationEvent, Irqls[i]);
367 KeLowerIrql(Irql);
368 }
369
370 for (i = 0; i < RTL_NUMBER_OF(Irqls); ++i)
371 {
372 /* creating threads above DISPATCH_LEVEL... nope */
373 if (Irqls[i] >= DISPATCH_LEVEL)
374 continue;
375 KeRaiseIrql(Irqls[i], &Irql);
376 trace("IRQL: %u\n", Irqls[i]);
377 for (PriorityIncrement = -1; PriorityIncrement <= 8; ++PriorityIncrement)
378 {
379 if (PriorityIncrement < 0 && KmtIsCheckedBuild)
380 continue;
381 trace("PriorityIncrement: %ld\n", PriorityIncrement);
382 trace("-> Checking KeSetEvent, NotificationEvent\n");
383 TestEventConcurrent(&Event, NotificationEvent, Irqls[i], KeSetEvent, PriorityIncrement, 1, TRUE);
384 trace("-> Checking KeSetEvent, SynchronizationEvent\n");
385 TestEventConcurrent(&Event, SynchronizationEvent, Irqls[i], KeSetEvent, PriorityIncrement, 0, FALSE);
386 trace("-> Checking KePulseEvent, NotificationEvent\n");
387 TestEventConcurrent(&Event, NotificationEvent, Irqls[i], KePulseEvent, PriorityIncrement, 0, TRUE);
388 trace("-> Checking KePulseEvent, SynchronizationEvent\n");
389 TestEventConcurrent(&Event, SynchronizationEvent, Irqls[i], KePulseEvent, PriorityIncrement, 0, FALSE);
390 }
391 KeLowerIrql(Irql);
392 }
393
394 ok_irql(PASSIVE_LEVEL);
395 KmtSetIrql(PASSIVE_LEVEL);
396
397 Thread = KmtStartThread(TestEventScheduling, NULL);
398 KmtFinishThread(Thread, NULL);
399 }