* Sync up to trunk head (r65147).
[reactos.git] / 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 Event->Header.Type = Type;
41 //Event->Header.Signalling = FALSE; // fails in kmtest
42 Event->Header.Size = sizeof(KEVENT) / sizeof(ULONG);
43 Event->Header.SignalState = State;
44 InitializeListHead(&(Event->Header.WaitListHead));
45 }
46
47 /*
48 * @implemented
49 */
50 VOID
51 NTAPI
52 KeInitializeEventPair(IN PKEVENT_PAIR EventPair)
53 {
54 /* Initialize the Event Pair Type and Size */
55 EventPair->Type = EventPairObject;
56 EventPair->Size = sizeof(KEVENT_PAIR);
57
58 /* Initialize the two Events */
59 KeInitializeEvent(&EventPair->LowEvent, SynchronizationEvent, FALSE);
60 KeInitializeEvent(&EventPair->HighEvent, SynchronizationEvent, FALSE);
61 }
62
63 /*
64 * @implemented
65 */
66 LONG
67 NTAPI
68 KePulseEvent(IN PKEVENT Event,
69 IN KPRIORITY Increment,
70 IN BOOLEAN Wait)
71 {
72 KIRQL OldIrql;
73 LONG PreviousState;
74 PKTHREAD Thread;
75 ASSERT_EVENT(Event);
76 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
77
78 /* Lock the Dispatcher Database */
79 OldIrql = KiAcquireDispatcherLock();
80
81 /* Save the Old State */
82 PreviousState = Event->Header.SignalState;
83
84 /* Check if we are non-signaled and we have stuff in the Wait Queue */
85 if (!PreviousState && !IsListEmpty(&Event->Header.WaitListHead))
86 {
87 /* Set the Event to Signaled */
88 Event->Header.SignalState = 1;
89
90 /* Wake the Event */
91 KiWaitTest(&Event->Header, Increment);
92 }
93
94 /* Unsignal it */
95 Event->Header.SignalState = 0;
96
97 /* Check what wait state was requested */
98 if (Wait == FALSE)
99 {
100 /* Wait not requested, release Dispatcher Database and return */
101 KiReleaseDispatcherLock(OldIrql);
102 }
103 else
104 {
105 /* Return Locked and with a Wait */
106 Thread = KeGetCurrentThread();
107 Thread->WaitNext = TRUE;
108 Thread->WaitIrql = OldIrql;
109 }
110
111 /* Return the previous State */
112 return PreviousState;
113 }
114
115 /*
116 * @implemented
117 */
118 LONG
119 NTAPI
120 KeReadStateEvent(IN PKEVENT Event)
121 {
122 ASSERT_EVENT(Event);
123
124 /* Return the Signal State */
125 return Event->Header.SignalState;
126 }
127
128 /*
129 * @implemented
130 */
131 LONG
132 NTAPI
133 KeResetEvent(IN PKEVENT Event)
134 {
135 KIRQL OldIrql;
136 LONG PreviousState;
137 ASSERT_EVENT(Event);
138 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
139
140 /* Lock the Dispatcher Database */
141 OldIrql = KiAcquireDispatcherLock();
142
143 /* Save the Previous State */
144 PreviousState = Event->Header.SignalState;
145
146 /* Set it to zero */
147 Event->Header.SignalState = 0;
148
149 /* Release Dispatcher Database and return previous state */
150 KiReleaseDispatcherLock(OldIrql);
151 return PreviousState;
152 }
153
154 /*
155 * @implemented
156 */
157 LONG
158 NTAPI
159 KeSetEvent(IN PKEVENT Event,
160 IN KPRIORITY Increment,
161 IN BOOLEAN Wait)
162 {
163 KIRQL OldIrql;
164 LONG PreviousState;
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 == EventNotificationObject) &&
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 /* Check the type of event */
194 if (Event->Header.Type == EventNotificationObject)
195 {
196 /* Unwait the thread */
197 KxUnwaitThread(&Event->Header, Increment);
198 }
199 else
200 {
201 /* Otherwise unwait the thread and unsignal the event */
202 KxUnwaitThreadForEvent(Event, Increment);
203 }
204 }
205
206 /* Check what wait state was requested */
207 if (!Wait)
208 {
209 /* Wait not requested, release Dispatcher Database and return */
210 KiReleaseDispatcherLock(OldIrql);
211 }
212 else
213 {
214 /* Return Locked and with a Wait */
215 Thread = KeGetCurrentThread();
216 Thread->WaitNext = TRUE;
217 Thread->WaitIrql = OldIrql;
218 }
219
220 /* Return the previous State */
221 return PreviousState;
222 }
223
224 /*
225 * @implemented
226 */
227 VOID
228 NTAPI
229 KeSetEventBoostPriority(IN PKEVENT Event,
230 IN PKTHREAD *WaitingThread OPTIONAL)
231 {
232 KIRQL OldIrql;
233 PKWAIT_BLOCK WaitBlock;
234 PKTHREAD Thread = KeGetCurrentThread(), WaitThread;
235 ASSERT(Event->Header.Type == EventSynchronizationObject);
236 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
237
238 /* Acquire Dispatcher Database Lock */
239 OldIrql = KiAcquireDispatcherLock();
240
241 /* Check if the list is empty */
242 if (IsListEmpty(&Event->Header.WaitListHead))
243 {
244 /* Set the Event to Signaled */
245 Event->Header.SignalState = 1;
246
247 /* Return */
248 KiReleaseDispatcherLock(OldIrql);
249 return;
250 }
251
252 /* Get the Wait Block */
253 WaitBlock = CONTAINING_RECORD(Event->Header.WaitListHead.Flink,
254 KWAIT_BLOCK,
255 WaitListEntry);
256
257 /* Check if this is a WaitAll */
258 if (WaitBlock->WaitType == WaitAll)
259 {
260 /* Set the Event to Signaled */
261 Event->Header.SignalState = 1;
262
263 /* Unwait the thread and unsignal the event */
264 KxUnwaitThreadForEvent(Event, EVENT_INCREMENT);
265 }
266 else
267 {
268 /* Return waiting thread to caller */
269 WaitThread = WaitBlock->Thread;
270 if (WaitingThread) *WaitingThread = WaitThread;
271
272 /* Calculate new priority */
273 Thread->Priority = KiComputeNewPriority(Thread, 0);
274
275 /* Unlink the waiting thread */
276 KiUnlinkThread(WaitThread, STATUS_SUCCESS);
277
278 /* Request priority boosting */
279 WaitThread->AdjustIncrement = Thread->Priority;
280 WaitThread->AdjustReason = AdjustBoost;
281
282 /* Ready the thread */
283 KiReadyThread(WaitThread);
284 }
285
286 /* Release the Dispatcher Database Lock */
287 KiReleaseDispatcherLock(OldIrql);
288 }
289
290 /* EOF */