- Fix KiDispatchException to unmask KI_EXCEPTION_INTERNAL when setting the exception...
[reactos.git] / reactos / ntoskrnl / ke / gate.c
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 <internal/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(Gate) / 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 APC lock and return */
79 KiReleaseApcLock(&ApcLock);
80 break;
81 }
82
83 /* Setup a Wait Block */
84 GateWaitBlock = &Thread->WaitBlock[0];
85 GateWaitBlock->Object = (PVOID)Gate;
86 GateWaitBlock->Thread = Thread;
87
88 /* Set the Thread Wait Data */
89 Thread->WaitMode = WaitMode;
90 Thread->WaitReason = WaitReason;
91 Thread->WaitIrql = ApcLock.OldIrql;
92 Thread->State = GateWait;
93 Thread->GateObject = Gate;
94
95 /* Insert into the Wait List */
96 InsertTailList(&Gate->Header.WaitListHead,
97 &GateWaitBlock->WaitListEntry);
98
99 /* Release the gate lock */
100 KiReleaseDispatcherObject(&Gate->Header);
101
102 /* Set swap busy */
103 KiSetThreadSwapBusy(Thread);
104
105 /* Release the thread lock */
106 KiReleaseThreadLock(Thread);
107
108 /* Check if we had a queue */
109 if (Queue)
110 {
111 /* Wake it up */
112 KiActivateWaiterQueue(Queue);
113
114 /* Release the dispatcher lock */
115 KiReleaseDispatcherLockFromDpcLevel();
116 }
117
118 /* Release the APC lock but stay at DPC level */
119 KiReleaseApcLockFromDpcLevel(&ApcLock);
120
121 /* Find a new thread to run */
122 Status = KiSwapThread(Thread, KeGetCurrentPrcb());
123
124 /* Make sure we weren't executing an APC */
125 if (Status == STATUS_SUCCESS) return;
126 }
127 } while (TRUE);
128 }
129
130 VOID
131 FASTCALL
132 KeSignalGateBoostPriority(IN PKGATE Gate)
133 {
134 PKTHREAD WaitThread;
135 PKWAIT_BLOCK WaitBlock;
136 KIRQL OldIrql;
137 ASSERT_GATE(Gate);
138 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
139
140 /* Start entry loop */
141 for (;;)
142 {
143 /* Raise to synch level */
144 OldIrql = KeRaiseIrqlToSynchLevel();
145
146 /* Lock the gate */
147 KiAcquireDispatcherObject(&Gate->Header);
148
149 /* Make sure we're not already signaled or that the list is empty */
150 if (Gate->Header.SignalState) break;
151
152 /* Check if our wait list is empty */
153 if (IsListEmpty(&Gate->Header.WaitListHead))
154 {
155 /* It is, so signal the event */
156 Gate->Header.SignalState = 1;
157 break;
158 }
159 else
160 {
161 /* Get WaitBlock */
162 WaitBlock = CONTAINING_RECORD(Gate->Header.WaitListHead.Flink,
163 KWAIT_BLOCK,
164 WaitListEntry);
165
166 /* Get the Associated thread */
167 WaitThread = WaitBlock->Thread;
168
169 /* Check to see if the waiting thread is locked */
170 if (!KiTryThreadLock(WaitThread))
171 {
172 /* Unlock the gate */
173 KiReleaseDispatcherObject(&Gate->Header);
174
175 /* Lower IRQL and loop again */
176 KeLowerIrql(OldIrql);
177 continue;
178 }
179
180 /* Remove it */
181 RemoveEntryList(&WaitBlock->WaitListEntry);
182
183 /* Clear wait status */
184 WaitThread->WaitStatus = STATUS_SUCCESS;
185
186 /* Set state and CPU */
187 WaitThread->State = DeferredReady;
188 WaitThread->DeferredProcessor = KeGetCurrentPrcb()->Number;
189
190 /* Release the gate lock */
191 KiReleaseDispatcherObject(&Gate->Header);
192
193 /* Release the thread lock */
194 KiReleaseThreadLock(WaitThread);
195
196 /* FIXME: Boosting */
197
198 /* Check if we have a queue */
199 if (WaitThread->Queue)
200 {
201 /* Acquire the dispatcher lock */
202 KiAcquireDispatcherLockAtDpcLevel();
203
204 /* Check if we still have one */
205 if (WaitThread->Queue)
206 {
207 /* Increment active threads */
208 WaitThread->Queue->CurrentCount++;
209 }
210
211 /* Release lock */
212 KiReleaseDispatcherLockFromDpcLevel();
213 }
214
215 /* Make the thread ready */
216 KiReadyThread(WaitThread);
217
218 /* Exit the dispatcher */
219 KiExitDispatcher(OldIrql);
220 return;
221 }
222 }
223
224 /* If we got here, then there's no rescheduling. */
225 KiReleaseDispatcherObject(&Gate->Header);
226 KeLowerIrql(OldIrql);
227 }
228
229 /* EOF */