b6457ce950783e30f9e43f339d09132a18f480f7
[reactos.git] / reactos / ntoskrnl / ke / eventobj.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/event.c
5 * PURPOSE: Implements the Event Dispatcher Object
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* FUNCTIONS *****************************************************************/
16
17 /*
18 * @implemented
19 */
20 VOID
21 NTAPI
22 KeClearEvent(IN PKEVENT Event)
23 {
24 ASSERT_EVENT(Event);
25
26 /* Reset Signal State */
27 Event->Header.SignalState = FALSE;
28 }
29
30 /*
31 * @implemented
32 */
33 VOID
34 NTAPI
35 KeInitializeEvent(OUT PKEVENT Event,
36 IN EVENT_TYPE Type,
37 IN BOOLEAN State)
38 {
39 /* Initialize the Dispatcher Header */
40 KeInitializeDispatcherHeader(&Event->Header,
41 Type,
42 sizeof(*Event) / sizeof(ULONG),
43 State);
44 }
45
46 /*
47 * @implemented
48 */
49 VOID
50 NTAPI
51 KeInitializeEventPair(IN PKEVENT_PAIR EventPair)
52 {
53 /* Initialize the Event Pair Type and Size */
54 EventPair->Type = EventPairObject;
55 EventPair->Size = sizeof(KEVENT_PAIR);
56
57 /* Initialize the two Events */
58 KeInitializeEvent(&EventPair->LowEvent, SynchronizationEvent, FALSE);
59 KeInitializeEvent(&EventPair->HighEvent, SynchronizationEvent, FALSE);
60 }
61
62 /*
63 * @implemented
64 */
65 LONG
66 NTAPI
67 KePulseEvent(IN PKEVENT Event,
68 IN KPRIORITY Increment,
69 IN BOOLEAN Wait)
70 {
71 KIRQL OldIrql;
72 LONG PreviousState;
73 PKTHREAD Thread;
74 ASSERT_EVENT(Event);
75 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
76
77 /* Lock the Dispatcher Database */
78 OldIrql = KiAcquireDispatcherLock();
79
80 /* Save the Old State */
81 PreviousState = Event->Header.SignalState;
82
83 /* Check if we are non-signaled and we have stuff in the Wait Queue */
84 if (!PreviousState && !IsListEmpty(&Event->Header.WaitListHead))
85 {
86 /* Set the Event to Signaled */
87 Event->Header.SignalState = 1;
88
89 /* Wake the Event */
90 KiWaitTest(&Event->Header, Increment);
91 }
92
93 /* Unsignal it */
94 Event->Header.SignalState = 0;
95
96 /* Check what wait state was requested */
97 if (Wait == FALSE)
98 {
99 /* Wait not requested, release Dispatcher Database and return */
100 KiReleaseDispatcherLock(OldIrql);
101 }
102 else
103 {
104 /* Return Locked and with a Wait */
105 Thread = KeGetCurrentThread();
106 Thread->WaitNext = TRUE;
107 Thread->WaitIrql = OldIrql;
108 }
109
110 /* Return the previous State */
111 return PreviousState;
112 }
113
114 /*
115 * @implemented
116 */
117 LONG
118 NTAPI
119 KeReadStateEvent(IN PKEVENT Event)
120 {
121 ASSERT_EVENT(Event);
122
123 /* Return the Signal State */
124 return Event->Header.SignalState;
125 }
126
127 /*
128 * @implemented
129 */
130 LONG
131 NTAPI
132 KeResetEvent(IN PKEVENT Event)
133 {
134 KIRQL OldIrql;
135 LONG PreviousState;
136 ASSERT_EVENT(Event);
137 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
138
139 /* Lock the Dispatcher Database */
140 OldIrql = KiAcquireDispatcherLock();
141
142 /* Save the Previous State */
143 PreviousState = Event->Header.SignalState;
144
145 /* Set it to zero */
146 Event->Header.SignalState = 0;
147
148 /* Release Dispatcher Database and return previous state */
149 KiReleaseDispatcherLock(OldIrql);
150 return PreviousState;
151 }
152
153 /*
154 * @implemented
155 */
156 LONG
157 NTAPI
158 KeSetEvent(IN PKEVENT Event,
159 IN KPRIORITY Increment,
160 IN BOOLEAN Wait)
161 {
162 KIRQL OldIrql;
163 LONG PreviousState;
164 PKWAIT_BLOCK WaitBlock;
165 PKTHREAD Thread;
166 ASSERT_EVENT(Event);
167 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
168
169 /*
170 * Check if this is an signaled notification event without an upcoming wait.
171 * In this case, we can immediately return TRUE, without locking.
172 */
173 if ((Event->Header.Type == NotificationEvent) &&
174 (Event->Header.SignalState == 1) &&
175 !(Wait))
176 {
177 /* Return the signal state (TRUE/Signalled) */
178 return TRUE;
179 }
180
181 /* Lock the Dispathcer Database */
182 OldIrql = KiAcquireDispatcherLock();
183
184 /* Save the Previous State */
185 PreviousState = Event->Header.SignalState;
186
187 /* Set the Event to Signaled */
188 Event->Header.SignalState = 1;
189
190 /* Check if the event just became signaled now, and it has waiters */
191 if (!(PreviousState) && !(IsListEmpty(&Event->Header.WaitListHead)))
192 {
193 /* Get the Wait Block */
194 WaitBlock = CONTAINING_RECORD(Event->Header.WaitListHead.Flink,
195 KWAIT_BLOCK,
196 WaitListEntry);
197
198 /* Check the type of event */
199 if (Event->Header.Type == NotificationEvent)
200 {
201 /* Unwait the thread */
202 KxUnwaitThread(&Event->Header, Increment);
203 }
204 else
205 {
206 /* Otherwise unwait the thread and unsignal the event */
207 KxUnwaitThreadForEvent(Event, Increment);
208 }
209 }
210
211 /* Check what wait state was requested */
212 if (!Wait)
213 {
214 /* Wait not requested, release Dispatcher Database and return */
215 KiReleaseDispatcherLock(OldIrql);
216 }
217 else
218 {
219 /* Return Locked and with a Wait */
220 Thread = KeGetCurrentThread();
221 Thread->WaitNext = TRUE;
222 Thread->WaitIrql = OldIrql;
223 }
224
225 /* Return the previous State */
226 return PreviousState;
227 }
228
229 /*
230 * @implemented
231 */
232 VOID
233 NTAPI
234 KeSetEventBoostPriority(IN PKEVENT Event,
235 IN PKTHREAD *WaitingThread OPTIONAL)
236 {
237 KIRQL OldIrql;
238 PKWAIT_BLOCK WaitBlock;
239 PKTHREAD Thread = KeGetCurrentThread(), WaitThread;
240 ASSERT(Event->Header.Type == SynchronizationEvent);
241 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
242
243 /* Acquire Dispatcher Database Lock */
244 OldIrql = KiAcquireDispatcherLock();
245
246 /* Check if the list is empty */
247 if (IsListEmpty(&Event->Header.WaitListHead))
248 {
249 /* Set the Event to Signaled */
250 Event->Header.SignalState = 1;
251
252 /* Return */
253 KiReleaseDispatcherLock(OldIrql);
254 return;
255 }
256
257 /* Get the Wait Block */
258 WaitBlock = CONTAINING_RECORD(Event->Header.WaitListHead.Flink,
259 KWAIT_BLOCK,
260 WaitListEntry);
261
262 /* Check if this is a WaitAll */
263 if (WaitBlock->WaitType == WaitAll)
264 {
265 /* Set the Event to Signaled */
266 Event->Header.SignalState = 1;
267
268 /* Unwait the thread and unsignal the event */
269 KxUnwaitThreadForEvent(Event, EVENT_INCREMENT);
270 }
271 else
272 {
273 /* Return waiting thread to caller */
274 WaitThread = WaitBlock->Thread;
275 if (WaitingThread) *WaitingThread = WaitThread;
276
277 /* Calculate new priority */
278 Thread->Priority = KiComputeNewPriority(Thread, 0);
279
280 /* Unlink the waiting thread */
281 KiUnlinkThread(WaitThread, STATUS_SUCCESS);
282
283 /* Request priority boosting */
284 WaitThread->AdjustIncrement = Thread->Priority;
285 WaitThread->AdjustReason = AdjustBoost;
286
287 /* Ready the thread */
288 KiReadyThread(WaitThread);
289 }
290
291 /* Release the Dispatcher Database Lock */
292 KiReleaseDispatcherLock(OldIrql);
293 }
294
295 /* EOF */