xref: /reactos/ntoskrnl/ke/gate.c (revision 9ea495ba)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ke/gate.c
5  * PURPOSE:         Implements the Gate 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 VOID
18 FASTCALL
19 KeInitializeGate(IN PKGATE Gate)
20 {
21     /* Initialize the Dispatcher Header */
22     KeInitializeDispatcherHeader(&Gate->Header,
23                                  GateObject,
24                                  sizeof(KGATE) / sizeof(ULONG),
25                                  0);
26 }
27 
28 VOID
29 FASTCALL
30 KeWaitForGate(IN PKGATE Gate,
31               IN KWAIT_REASON WaitReason,
32               IN KPROCESSOR_MODE WaitMode)
33 {
34     KLOCK_QUEUE_HANDLE ApcLock;
35     PKTHREAD Thread = KeGetCurrentThread();
36     PKWAIT_BLOCK GateWaitBlock;
37     NTSTATUS Status;
38     PKQUEUE Queue;
39     ASSERT_GATE(Gate);
40     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
41 
42     /* Start wait loop */
43     do
44     {
45         /* Acquire the APC lock */
46         KiAcquireApcLock(Thread, &ApcLock);
47 
48         /* Check if a kernel APC is pending and we're below APC_LEVEL */
49         if ((Thread->ApcState.KernelApcPending) &&
50             !(Thread->SpecialApcDisable) &&
51             (ApcLock.OldIrql < APC_LEVEL))
52         {
53             /* Release the lock, this will fire the APC */
54             KiReleaseApcLock(&ApcLock);
55         }
56         else
57         {
58             /* Check if we have a queue and lock the dispatcher if so */
59             Queue = Thread->Queue;
60             if (Queue) KiAcquireDispatcherLockAtDpcLevel();
61 
62             /* Lock the thread */
63             KiAcquireThreadLock(Thread);
64 
65             /* Lock the gate */
66             KiAcquireDispatcherObject(&Gate->Header);
67 
68             /* Check if it's already signaled */
69             if (Gate->Header.SignalState)
70             {
71                 /* Unsignal it */
72                 Gate->Header.SignalState = 0;
73 
74                 /* Release the gate and thread locks */
75                 KiReleaseDispatcherObject(&Gate->Header);
76                 KiReleaseThreadLock(Thread);
77 
78                 /* Release the gate lock */
79                 if (Queue) KiReleaseDispatcherLockFromDpcLevel();
80 
81                 /* Release the APC lock and return */
82                 KiReleaseApcLock(&ApcLock);
83                 break;
84             }
85 
86             /* Setup a Wait Block */
87             GateWaitBlock = &Thread->WaitBlock[0];
88             GateWaitBlock->Object = (PVOID)Gate;
89             GateWaitBlock->Thread = Thread;
90 
91             /* Set the Thread Wait Data */
92             Thread->WaitMode = WaitMode;
93             Thread->WaitReason = WaitReason;
94             Thread->WaitIrql = ApcLock.OldIrql;
95             Thread->State = GateWait;
96             Thread->GateObject = Gate;
97 
98             /* Insert into the Wait List */
99             InsertTailList(&Gate->Header.WaitListHead,
100                            &GateWaitBlock->WaitListEntry);
101 
102             /* Release the gate lock */
103             KiReleaseDispatcherObject(&Gate->Header);
104 
105             /* Set swap busy */
106             KiSetThreadSwapBusy(Thread);
107 
108             /* Release the thread lock */
109             KiReleaseThreadLock(Thread);
110 
111             /* Check if we had a queue */
112             if (Queue)
113             {
114                 /* Wake it up */
115                 KiActivateWaiterQueue(Queue);
116 
117                 /* Release the dispatcher lock */
118                 KiReleaseDispatcherLockFromDpcLevel();
119             }
120 
121             /* Release the APC lock but stay at DPC level */
122             KiReleaseApcLockFromDpcLevel(&ApcLock);
123 
124             /* Find a new thread to run */
125             Status = KiSwapThread(Thread, KeGetCurrentPrcb());
126 
127             /* Make sure we weren't executing an APC */
128             if (Status == STATUS_SUCCESS) return;
129         }
130     } while (TRUE);
131 }
132 
133 VOID
134 FASTCALL
135 KeSignalGateBoostPriority(IN PKGATE Gate)
136 {
137     PKTHREAD WaitThread;
138     PKWAIT_BLOCK WaitBlock;
139     KIRQL OldIrql;
140     ASSERT_GATE(Gate);
141     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
142 
143     /* Start entry loop */
144     for (;;)
145     {
146         /* Raise to synch level */
147         OldIrql = KeRaiseIrqlToSynchLevel();
148 
149         /* Lock the gate */
150         KiAcquireDispatcherObject(&Gate->Header);
151 
152         /* Make sure we're not already signaled or that the list is empty */
153         if (Gate->Header.SignalState) break;
154 
155         /* Check if our wait list is empty */
156         if (IsListEmpty(&Gate->Header.WaitListHead))
157         {
158             /* It is, so signal the event */
159             Gate->Header.SignalState = 1;
160             break;
161         }
162         else
163         {
164             /* Get WaitBlock */
165             WaitBlock = CONTAINING_RECORD(Gate->Header.WaitListHead.Flink,
166                                           KWAIT_BLOCK,
167                                           WaitListEntry);
168 
169             /* Get the Associated thread */
170             WaitThread = WaitBlock->Thread;
171 
172             /* Check to see if the waiting thread is locked */
173             if (KiTryThreadLock(WaitThread))
174             {
175                 /* Unlock the gate */
176                 KiReleaseDispatcherObject(&Gate->Header);
177 
178                 /* Lower IRQL and loop again */
179                 KeLowerIrql(OldIrql);
180                 continue;
181             }
182 
183             /* Remove it */
184             RemoveEntryList(&WaitBlock->WaitListEntry);
185 
186             /* Clear wait status */
187             WaitThread->WaitStatus = STATUS_SUCCESS;
188 
189             /* Set state and CPU */
190             WaitThread->State = DeferredReady;
191             WaitThread->DeferredProcessor = KeGetCurrentPrcb()->Number;
192 
193             /* Release the gate lock */
194             KiReleaseDispatcherObject(&Gate->Header);
195 
196             /* Release the thread lock */
197             KiReleaseThreadLock(WaitThread);
198 
199             /* FIXME: Boosting */
200 
201             /* Check if we have a queue */
202             if (WaitThread->Queue)
203             {
204                 /* Acquire the dispatcher lock */
205                 KiAcquireDispatcherLockAtDpcLevel();
206 
207                 /* Check if we still have one */
208                 if (WaitThread->Queue)
209                 {
210                     /* Increment active threads */
211                     WaitThread->Queue->CurrentCount++;
212                 }
213 
214                 /* Release lock */
215                 KiReleaseDispatcherLockFromDpcLevel();
216             }
217 
218             /* Make the thread ready */
219             KiReadyThread(WaitThread);
220 
221             /* Exit the dispatcher */
222             KiExitDispatcher(OldIrql);
223             return;
224         }
225     }
226 
227     /* If we got here, then there's no rescheduling. */
228     KiReleaseDispatcherObject(&Gate->Header);
229     KeLowerIrql(OldIrql);
230 }
231 
232 /* EOF */
233