xref: /reactos/ntoskrnl/ke/eventobj.c (revision 1734f297)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ke/eventobj.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 */
291