[KMTESTS:KE]
[reactos.git] / 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 /* TODO: why does GCC have 3 tests less than MSVC?! */
11
12 #define CheckEvent(Event, ExpectedType, State, ExpectedWaitNext, \
13 Irql, ThreadList, ThreadCount) do \
14 { \
15 INT TheIndex; \
16 PLIST_ENTRY TheEntry; \
17 PKTHREAD TheThread; \
18 ok_eq_uint((Event)->Header.Type, ExpectedType); \
19 ok_eq_uint((Event)->Header.Hand, sizeof *(Event) / sizeof(ULONG)); \
20 ok_eq_hex((Event)->Header.Lock & 0xFF00FF00L, 0x55005500L); \
21 ok_eq_long((Event)->Header.SignalState, State); \
22 TheEntry = (Event)->Header.WaitListHead.Flink; \
23 for (TheIndex = 0; TheIndex < (ThreadCount); ++TheIndex) \
24 { \
25 TheThread = CONTAINING_RECORD(TheEntry, KTHREAD, \
26 WaitBlock[0].WaitListEntry); \
27 ok_eq_pointer(TheThread, (ThreadList)[TheIndex]); \
28 ok_eq_pointer(TheEntry->Flink->Blink, TheEntry); \
29 TheEntry = TheEntry->Flink; \
30 } \
31 ok_eq_pointer(TheEntry, &(Event)->Header.WaitListHead); \
32 ok_eq_pointer(TheEntry->Flink->Blink, TheEntry); \
33 ok_eq_long(KeReadStateEvent(Event), State); \
34 ok_eq_bool(Thread->WaitNext, ExpectedWaitNext); \
35 ok_irql(Irql); \
36 } while (0)
37
38 static
39 VOID
40 TestEventFunctional(
41 IN PKEVENT Event,
42 IN EVENT_TYPE Type,
43 IN KIRQL OriginalIrql)
44 {
45 LONG State;
46 PKTHREAD Thread = KeGetCurrentThread();
47
48 memset(Event, 0x55, sizeof *Event);
49 KeInitializeEvent(Event, Type, FALSE);
50 CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
51
52 memset(Event, 0x55, sizeof *Event);
53 KeInitializeEvent(Event, Type, TRUE);
54 CheckEvent(Event, Type, 1L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
55
56 Event->Header.SignalState = 0x12345678L;
57 CheckEvent(Event, Type, 0x12345678L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
58
59 State = KePulseEvent(Event, 0, FALSE);
60 CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
61 ok_eq_long(State, 0x12345678L);
62
63 Event->Header.SignalState = 0x12345678L;
64 KeClearEvent(Event);
65 CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
66
67 State = KeSetEvent(Event, 0, FALSE);
68 CheckEvent(Event, Type, 1L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
69 ok_eq_long(State, 0L);
70
71 State = KeResetEvent(Event);
72 CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
73 ok_eq_long(State, 1L);
74
75 Event->Header.SignalState = 0x23456789L;
76 State = KeSetEvent(Event, 0, FALSE);
77 CheckEvent(Event, Type, 1L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
78 ok_eq_long(State, 0x23456789L);
79
80 Event->Header.SignalState = 0x3456789AL;
81 State = KeResetEvent(Event);
82 CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, (PVOID *)NULL, 0);
83 ok_eq_long(State, 0x3456789AL);
84
85 /* Irql is raised to DISPATCH_LEVEL here, which kills checked build,
86 * a spinlock is acquired and never released, which kills MP build */
87 if ((OriginalIrql <= DISPATCH_LEVEL || !KmtIsCheckedBuild) &&
88 !KmtIsMultiProcessorBuild)
89 {
90 Event->Header.SignalState = 0x456789ABL;
91 State = KeSetEvent(Event, 0, TRUE);
92 CheckEvent(Event, Type, 1L, TRUE, DISPATCH_LEVEL, (PVOID *)NULL, 0);
93 ok_eq_long(State, 0x456789ABL);
94 ok_eq_uint(Thread->WaitIrql, OriginalIrql);
95 /* repair the "damage" */
96 Thread->WaitNext = FALSE;
97 KmtSetIrql(OriginalIrql);
98
99 Event->Header.SignalState = 0x56789ABCL;
100 State = KePulseEvent(Event, 0, TRUE);
101 CheckEvent(Event, Type, 0L, TRUE, DISPATCH_LEVEL, (PVOID *)NULL, 0);
102 ok_eq_long(State, 0x56789ABCL);
103 ok_eq_uint(Thread->WaitIrql, OriginalIrql);
104 /* repair the "damage" */
105 Thread->WaitNext = FALSE;
106 KmtSetIrql(OriginalIrql);
107 }
108
109 ok_irql(OriginalIrql);
110 KmtSetIrql(OriginalIrql);
111 }
112
113 typedef struct
114 {
115 HANDLE Handle;
116 PKTHREAD Thread;
117 PKEVENT Event;
118 volatile BOOLEAN Signal;
119 } THREAD_DATA, *PTHREAD_DATA;
120
121 static
122 VOID
123 NTAPI
124 WaitForEventThread(
125 IN OUT PVOID Context)
126 {
127 NTSTATUS Status;
128 PTHREAD_DATA ThreadData = Context;
129
130 ok_irql(PASSIVE_LEVEL);
131 ThreadData->Signal = TRUE;
132 Status = KeWaitForSingleObject(ThreadData->Event, Executive, KernelMode, FALSE, NULL);
133 ok_eq_hex(Status, STATUS_SUCCESS);
134 ok_irql(PASSIVE_LEVEL);
135 }
136
137 typedef LONG (NTAPI *PSET_EVENT_FUNCTION)(PRKEVENT, KPRIORITY, BOOLEAN);
138
139 static
140 VOID
141 TestEventConcurrent(
142 IN PKEVENT Event,
143 IN EVENT_TYPE Type,
144 IN KIRQL OriginalIrql,
145 PSET_EVENT_FUNCTION SetEvent,
146 KPRIORITY PriorityIncrement,
147 LONG ExpectedState,
148 BOOLEAN SatisfiesAll)
149 {
150 NTSTATUS Status;
151 THREAD_DATA Threads[5];
152 const INT ThreadCount = sizeof Threads / sizeof Threads[0];
153 KPRIORITY Priority;
154 LARGE_INTEGER LongTimeout, ShortTimeout;
155 INT i;
156 KWAIT_BLOCK WaitBlock[MAXIMUM_WAIT_OBJECTS];
157 PVOID ThreadObjects[MAXIMUM_WAIT_OBJECTS];
158 LONG State;
159 PKTHREAD Thread = KeGetCurrentThread();
160
161 LongTimeout.QuadPart = -100 * MILLISECOND;
162 ShortTimeout.QuadPart = -1 * MILLISECOND;
163
164 KeInitializeEvent(Event, Type, FALSE);
165
166 for (i = 0; i < ThreadCount; ++i)
167 {
168 Threads[i].Event = Event;
169 Threads[i].Signal = FALSE;
170 Status = PsCreateSystemThread(&Threads[i].Handle, GENERIC_ALL, NULL, NULL, NULL, WaitForEventThread, &Threads[i]);
171 ok_eq_hex(Status, STATUS_SUCCESS);
172 Status = ObReferenceObjectByHandle(Threads[i].Handle, SYNCHRONIZE, *PsThreadType, KernelMode, (PVOID *)&Threads[i].Thread, NULL);
173 ok_eq_hex(Status, STATUS_SUCCESS);
174 ThreadObjects[i] = Threads[i].Thread;
175 Priority = KeQueryPriorityThread(Threads[i].Thread);
176 ok_eq_long(Priority, 8L);
177 while (!Threads[i].Signal)
178 {
179 Status = KeDelayExecutionThread(KernelMode, FALSE, &ShortTimeout);
180 ok_eq_hex(Status, STATUS_SUCCESS);
181 }
182 CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, ThreadObjects, i + 1);
183 }
184
185 /* the threads shouldn't wake up on their own */
186 Status = KeDelayExecutionThread(KernelMode, FALSE, &ShortTimeout);
187 ok_eq_hex(Status, STATUS_SUCCESS);
188
189 for (i = 0; i < ThreadCount; ++i)
190 {
191 CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, ThreadObjects + i, ThreadCount - i);
192 State = SetEvent(Event, PriorityIncrement + i, FALSE);
193
194 ok_eq_long(State, 0L);
195 CheckEvent(Event, Type, ExpectedState, FALSE, OriginalIrql, ThreadObjects + i + 1, SatisfiesAll ? 0 : ThreadCount - i - 1);
196 Status = KeWaitForMultipleObjects(ThreadCount, ThreadObjects, SatisfiesAll ? WaitAll : WaitAny, Executive, KernelMode, FALSE, &LongTimeout, WaitBlock);
197 ok_eq_hex(Status, STATUS_WAIT_0 + i);
198 if (SatisfiesAll)
199 {
200 for (; i < ThreadCount; ++i)
201 {
202 Priority = KeQueryPriorityThread(Threads[i].Thread);
203 ok_eq_long(Priority, max(min(8L + PriorityIncrement, 15L), 8L));
204 }
205 break;
206 }
207 Priority = KeQueryPriorityThread(Threads[i].Thread);
208 ok_eq_long(Priority, max(min(8L + PriorityIncrement + i, 15L), 8L));
209 /* replace the thread with the current thread - which will never signal */
210 if (!skip((Status & 0x3F) < ThreadCount, "Index out of bounds"))
211 ThreadObjects[Status & 0x3F] = Thread;
212 Status = KeWaitForMultipleObjects(ThreadCount, ThreadObjects, WaitAny, Executive, KernelMode, FALSE, &ShortTimeout, WaitBlock);
213 ok_eq_hex(Status, STATUS_TIMEOUT);
214 }
215
216 for (i = 0; i < ThreadCount; ++i)
217 {
218 ObDereferenceObject(Threads[i].Thread);
219 Status = ZwClose(Threads[i].Handle);
220 ok_eq_hex(Status, STATUS_SUCCESS);
221 }
222 }
223
224 START_TEST(KeEvent)
225 {
226 KEVENT Event;
227 KIRQL Irql;
228 KIRQL Irqls[] = { PASSIVE_LEVEL, APC_LEVEL, DISPATCH_LEVEL };
229 INT i;
230 KPRIORITY PriorityIncrement;
231
232 for (i = 0; i < sizeof Irqls / sizeof Irqls[0]; ++i)
233 {
234 KeRaiseIrql(Irqls[i], &Irql);
235 TestEventFunctional(&Event, NotificationEvent, Irqls[i]);
236 TestEventFunctional(&Event, SynchronizationEvent, Irqls[i]);
237 KeLowerIrql(Irql);
238 }
239
240 for (i = 0; i < sizeof Irqls / sizeof Irqls[0]; ++i)
241 {
242 /* creating threads above DISPATCH_LEVEL... nope */
243 if (Irqls[i] >= DISPATCH_LEVEL)
244 continue;
245 KeRaiseIrql(Irqls[i], &Irql);
246 trace("IRQL: %u\n", Irqls[i]);
247 for (PriorityIncrement = -1; PriorityIncrement <= 8; ++PriorityIncrement)
248 {
249 trace("PriorityIncrement: %ld\n", PriorityIncrement);
250 trace("-> Checking KeSetEvent, NotificationEvent\n");
251 TestEventConcurrent(&Event, NotificationEvent, Irqls[i], KeSetEvent, PriorityIncrement, 1, TRUE);
252 trace("-> Checking KeSetEvent, SynchronizationEvent\n");
253 TestEventConcurrent(&Event, SynchronizationEvent, Irqls[i], KeSetEvent, PriorityIncrement, 0, FALSE);
254 trace("-> Checking KePulseEvent, NotificationEvent\n");
255 TestEventConcurrent(&Event, NotificationEvent, Irqls[i], KePulseEvent, PriorityIncrement, 0, TRUE);
256 trace("-> Checking KePulseEvent, SynchronizationEvent\n");
257 TestEventConcurrent(&Event, SynchronizationEvent, Irqls[i], KePulseEvent, PriorityIncrement, 0, FALSE);
258 }
259 KeLowerIrql(Irql);
260 }
261
262 ok_irql(PASSIVE_LEVEL);
263 KmtSetIrql(PASSIVE_LEVEL);
264 }