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