- Implemented Queued and In-Stack Queued Spinlocks (at DPC-Level). See "Windows Inter...
[reactos.git] / reactos / ntoskrnl / ke / spinlock.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ke/spinlock.c
5 * PURPOSE: Implements spinlocks
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * David Welch (welch@cwcom.net)
8 */
9
10 /* INCLUDES ****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <internal/debug.h>
15
16 #undef KefAcquireSpinLockAtDpcLevel
17 #undef KeAcquireSpinLockAtDpcLevel
18 #undef KefReleaseSpinLockFromDpcLevel
19 #undef KeReleaseSpinLockFromDpcLevel
20
21 #define LQ_WAIT 1
22 #define LQ_OWN 2
23
24 /* FUNCTIONS ***************************************************************/
25
26 /*
27 * @implemented
28 *
29 * FUNCTION: Synchronizes the execution of a given routine with the ISR
30 * of a given interrupt object
31 * ARGUMENTS:
32 * Interrupt = Interrupt object to synchronize with
33 * SynchronizeRoutine = Routine to call whose execution is
34 * synchronized with the ISR
35 * SynchronizeContext = Parameter to pass to the synchronized routine
36 * RETURNS: TRUE if the operation succeeded
37 */
38 BOOLEAN
39 STDCALL
40 KeSynchronizeExecution(PKINTERRUPT Interrupt,
41 PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
42 PVOID SynchronizeContext)
43 {
44 KIRQL OldIrql;
45 BOOLEAN Status;
46
47 /* Raise IRQL and acquire lock on MP */
48 OldIrql = KeAcquireInterruptSpinLock(Interrupt);
49
50 /* Call the routine */
51 Status = SynchronizeRoutine(SynchronizeContext);
52
53 /* Release lock and lower IRQL */
54 KeReleaseInterruptSpinLock(Interrupt, OldIrql);
55
56 /* Return routine status */
57 return Status;
58 }
59
60 /*
61 * @implemented
62 */
63 KIRQL
64 STDCALL
65 KeAcquireInterruptSpinLock(IN PKINTERRUPT Interrupt)
66 {
67 KIRQL OldIrql;
68
69 /* Raise IRQL */
70 KeRaiseIrql(Interrupt->SynchronizeIrql, &OldIrql);
71
72 /* Acquire spinlock on MP */
73 KiAcquireSpinLock(Interrupt->ActualLock);
74 return OldIrql;
75 }
76
77 /*
78 * @implemented
79 *
80 * FUNCTION: Initalizes a spinlock
81 * ARGUMENTS:
82 * SpinLock = Caller supplied storage for the spinlock
83 */
84 VOID
85 STDCALL
86 KeInitializeSpinLock(PKSPIN_LOCK SpinLock)
87 {
88 *SpinLock = 0;
89 }
90
91 /*
92 * @implemented
93 */
94 VOID
95 FASTCALL
96 KefAcquireSpinLockAtDpcLevel(PKSPIN_LOCK SpinLock)
97 {
98 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
99 KiAcquireSpinLock(SpinLock);
100 }
101
102 /*
103 * @implemented
104 *
105 * FUNCTION: Acquires a spinlock when the caller is already running at
106 * dispatch level
107 * ARGUMENTS:
108 * SpinLock = Spinlock to acquire
109 */
110 VOID
111 STDCALL
112 KeAcquireSpinLockAtDpcLevel (PKSPIN_LOCK SpinLock)
113 {
114 KefAcquireSpinLockAtDpcLevel(SpinLock);
115 }
116
117 /*
118 * @implemented
119 */
120 VOID
121 FASTCALL
122 KefReleaseSpinLockFromDpcLevel(PKSPIN_LOCK SpinLock)
123 {
124 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
125 KiReleaseSpinLock(SpinLock);
126 }
127
128 /*
129 * @implemented
130 *
131 * FUNCTION: Releases a spinlock when the caller was running at dispatch
132 * level before acquiring it
133 * ARGUMENTS:
134 * SpinLock = Spinlock to release
135 */
136 VOID
137 STDCALL
138 KeReleaseSpinLockFromDpcLevel (PKSPIN_LOCK SpinLock)
139 {
140 KefReleaseSpinLockFromDpcLevel(SpinLock);
141 }
142
143 /*
144 * @implemented
145 */
146 VOID
147 FASTCALL
148 KiAcquireSpinLock(PKSPIN_LOCK SpinLock)
149 {
150 #ifdef CONFIG_SMP
151 for (;;)
152 {
153 /* Try to acquire it */
154 if (InterlockedBitTestAndSet((PLONG)SpinLock, 0))
155 {
156 /* Value changed... wait until it's locked */
157 while (*(volatile KSPIN_LOCK *)SpinLock == 1) YieldProcessor();
158 }
159 else
160 {
161 /* All is well, break out */
162 break;
163 }
164 }
165 #endif /* CONFIG_SMP */
166 }
167
168 /*
169 * @implemented
170 */
171 VOID
172 STDCALL
173 KeReleaseInterruptSpinLock(IN PKINTERRUPT Interrupt,
174 IN KIRQL OldIrql)
175 {
176 /* Release lock on MP */
177 KiReleaseSpinLock(Interrupt->ActualLock);
178
179 /* Lower IRQL */
180 KeLowerIrql(OldIrql);
181 }
182
183 /*
184 * @implemented
185 */
186 VOID
187 FASTCALL
188 KiReleaseSpinLock(PKSPIN_LOCK SpinLock)
189 {
190 #ifdef CONFIG_SMP
191 /* Simply clear it */
192 *SpinLock = 0;
193 #endif
194 }
195
196 VOID
197 FASTCALL
198 KeAcquireQueuedSpinLockAtDpcLevel(IN PKLOCK_QUEUE_HANDLE LockHandle)
199 {
200 #ifdef CONFIG_SMP
201 PKSPIN_LOCK_QUEUE Prev;
202
203 /* Set the new lock */
204 Prev = (PKSPIN_LOCK_QUEUE)
205 InterlockedExchange((PLONG)LockHandle->LockQueue.Lock,
206 (LONG)LockHandle);
207 if (!Prev)
208 {
209 /* There was nothing there before. We now own it */
210 *(ULONG_PTR*)&LockHandle->LockQueue.Lock |= LQ_OWN;
211 return;
212 }
213
214 /* Set the wait flag */
215 *(ULONG_PTR*)&LockHandle->LockQueue.Lock |= LQ_WAIT;
216
217 /* Link us */
218 Prev->Next = (PKSPIN_LOCK_QUEUE)LockHandle;
219
220 /* Loop and wait */
221 while ( *(ULONG_PTR*)&LockHandle->LockQueue.Lock & LQ_WAIT) YieldProcessor();
222 return;
223 #endif
224 }
225
226 VOID
227 FASTCALL
228 KeReleaseQueuedSpinLockFromDpcLevel(IN PKLOCK_QUEUE_HANDLE LockHandle)
229 {
230 #ifdef CONFIG_SMP
231 KSPIN_LOCK LockVal;
232 PKSPIN_LOCK_QUEUE Waiter;
233
234 /* Remove own and wait flags */
235 *(ULONG_PTR*)&LockHandle->LockQueue.Lock &= ~(LQ_OWN | LQ_WAIT);
236 LockVal = *LockHandle->LockQueue.Lock;
237
238 /* Check if we already own it */
239 if (LockVal == (KSPIN_LOCK)LockHandle)
240 {
241 /* Disown it */
242 LockVal = (KSPIN_LOCK)
243 InterlockedCompareExchangePointer(LockHandle->LockQueue.Lock,
244 NULL,
245 LockHandle);
246 }
247 if (LockVal == (KSPIN_LOCK)LockHandle) return;
248
249 /* Need to wait for it */
250 Waiter = LockHandle->LockQueue.Next;
251 while (!Waiter)
252 {
253 YieldProcessor();
254 Waiter = LockHandle->LockQueue.Next;
255 }
256
257 /* It's gone */
258 *(ULONG_PTR*)&Waiter->Lock ^= (LQ_OWN | LQ_WAIT);
259 LockHandle->LockQueue.Next = NULL;
260 #endif
261 }
262
263 /*
264 * @implemented
265 */
266 VOID
267 FASTCALL
268 KeAcquireInStackQueuedSpinLockAtDpcLevel(IN PKSPIN_LOCK SpinLock,
269 IN PKLOCK_QUEUE_HANDLE LockHandle)
270 {
271 /* Set it up properly */
272 LockHandle->LockQueue.Next = NULL;
273 LockHandle->LockQueue.Lock = SpinLock;
274 KeAcquireQueuedSpinLockAtDpcLevel((PKLOCK_QUEUE_HANDLE)
275 &LockHandle->LockQueue.Next);
276 }
277
278 /*
279 * @implemented
280 */
281 VOID
282 FASTCALL
283 KeReleaseInStackQueuedSpinLockFromDpcLevel(IN PKLOCK_QUEUE_HANDLE LockHandle)
284 {
285 /* Call the internal function */
286 KeReleaseQueuedSpinLockFromDpcLevel((PKLOCK_QUEUE_HANDLE)
287 &LockHandle->LockQueue.Next);
288 }
289
290 /* EOF */