KD System Rewrite:
[reactos.git] / reactos / ntoskrnl / ex / rundown.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ex/rundown.c
5 * PURPOSE: Rundown Protection Functions
6 *
7 * PROGRAMMERS: Alex Ionescu & Thomas Weidenmueller - Implementation
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <internal/debug.h>
15
16 /* FUNCTIONS *****************************************************************/
17
18 /*
19 * @implemented
20 */
21 BOOLEAN
22 FASTCALL
23 ExAcquireRundownProtection (
24 IN PEX_RUNDOWN_REF RunRef
25 )
26 {
27 /* Call the general function with only one Reference add */
28 return ExAcquireRundownProtectionEx(RunRef, 1);
29 }
30
31 /*
32 * @implemented
33 */
34 BOOLEAN
35 FASTCALL
36 ExAcquireRundownProtectionEx (
37 IN PEX_RUNDOWN_REF RunRef,
38 IN ULONG Count
39 )
40 {
41 ULONG_PTR PrevCount, Current;
42
43 PAGED_CODE();
44
45 Count <<= EX_RUNDOWN_COUNT_SHIFT;
46
47 /* Loop until successfully incremented the counter */
48 do
49 {
50 Current = RunRef->Count;
51
52 /* Make sure a rundown is not active */
53 if (Current & EX_RUNDOWN_ACTIVE)
54 {
55 return FALSE;
56 }
57
58 #ifdef _WIN64
59 PrevCount = (ULONG_PTR)InterlockedExchangeAdd64((LONGLONG*)&RunRef->Count, (LONGLONG)Count);
60 #else
61 PrevCount = (ULONG_PTR)InterlockedExchangeAdd((LONG*)&RunRef->Count, (LONG)Count);
62 #endif
63 } while (PrevCount != Current);
64
65 /* Return Success */
66 return TRUE;
67 }
68
69 /*
70 * @implemented
71 */
72 VOID
73 FASTCALL
74 ExInitializeRundownProtection (
75 IN PEX_RUNDOWN_REF RunRef
76 )
77 {
78 PAGED_CODE();
79
80 /* Set the count to zero */
81 RunRef->Count = 0;
82 }
83
84 /*
85 * @implemented
86 */
87 VOID
88 FASTCALL
89 ExReInitializeRundownProtection (
90 IN PEX_RUNDOWN_REF RunRef
91 )
92 {
93 PAGED_CODE();
94
95 /* Reset the count */
96 #ifdef _WIN64
97 InterlockedExchangeAdd64((LONGLONG*)&RunRef->Count, 0LL);
98 #else
99 InterlockedExchangeAdd((LONG*)&RunRef->Count, 0);
100 #endif
101 }
102
103
104 /*
105 * @implemented
106 */
107 VOID
108 FASTCALL
109 ExReleaseRundownProtectionEx (
110 IN PEX_RUNDOWN_REF RunRef,
111 IN ULONG Count
112 )
113 {
114 PAGED_CODE();
115
116 Count <<= EX_RUNDOWN_COUNT_SHIFT;
117
118 for (;;)
119 {
120 ULONG_PTR Current = RunRef->Count;
121
122 /* Check if Rundown is active */
123 if (Current & EX_RUNDOWN_ACTIVE)
124 {
125 /* Get Pointer */
126 PRUNDOWN_DESCRIPTOR RundownDescriptor = (PRUNDOWN_DESCRIPTOR)(Current & ~EX_RUNDOWN_ACTIVE);
127
128 if (RundownDescriptor == NULL)
129 {
130 /* the rundown was completed and there's no one to notify */
131 break;
132 }
133
134 Current = RundownDescriptor->References;
135
136 /* Decrease RundownDescriptor->References by Count references */
137 for (;;)
138 {
139 ULONG_PTR PrevCount, NewCount;
140
141 if ((Count >> EX_RUNDOWN_COUNT_SHIFT) == Current)
142 {
143 NewCount = 0;
144 }
145 else
146 {
147 NewCount = ((RundownDescriptor->References - (Count >> EX_RUNDOWN_COUNT_SHIFT)) << EX_RUNDOWN_COUNT_SHIFT) | EX_RUNDOWN_ACTIVE;
148 }
149 #ifdef _WIN64
150 PrevCount = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RundownDescriptor->References, (LONGLONG)NewCount, (LONGLONG)Current);
151 #else
152 PrevCount = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RundownDescriptor->References, (LONG)NewCount, (LONG)Current);
153 #endif
154 if (PrevCount == Current)
155 {
156 if (NewCount == 0)
157 {
158 /* Signal the event so anyone waiting on it can now kill it */
159 KeSetEvent(&RundownDescriptor->RundownEvent, 0, FALSE);
160 }
161
162 /* Successfully decremented the counter, so bail! */
163 break;
164 }
165
166 Current = PrevCount;
167 }
168
169 break;
170 }
171 else
172 {
173 ULONG_PTR PrevCount, NewCount = Current - (ULONG_PTR)Count;
174 #ifdef _WIN64
175 PrevCount = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Count, (LONGLONG)NewCount, (LONGLONG)Current);
176 #else
177 PrevCount = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Count, (LONG)NewCount, (LONG)Current);
178 #endif
179 if (PrevCount == Current)
180 {
181 /* Successfully decremented the counter, so bail! */
182 break;
183 }
184 }
185 }
186 }
187
188 /*
189 * @implemented
190 */
191 VOID
192 FASTCALL
193 ExReleaseRundownProtection (
194 IN PEX_RUNDOWN_REF RunRef
195 )
196 {
197 /* Call the general function with only 1 reference removal */
198 ExReleaseRundownProtectionEx(RunRef, 1);
199 }
200
201 /*
202 * @implemented
203 */
204 VOID
205 FASTCALL
206 ExRundownCompleted (
207 IN PEX_RUNDOWN_REF RunRef
208 )
209 {
210 PAGED_CODE();
211
212 /* mark the counter as active */
213 #ifdef _WIN64
214 InterlockedExchange64((LONGLONG*)&RunRef->Count, (LONGLONG)EX_RUNDOWN_ACTIVE);
215 #else
216 InterlockedExchange((LONG*)&RunRef->Count, EX_RUNDOWN_ACTIVE);
217 #endif
218 }
219
220 /*
221 * @implemented
222 */
223 VOID
224 FASTCALL
225 ExWaitForRundownProtectionRelease (
226 IN PEX_RUNDOWN_REF RunRef
227 )
228 {
229 ULONG_PTR PrevCount, NewPtr, PrevPtr;
230 RUNDOWN_DESCRIPTOR RundownDescriptor;
231
232 PAGED_CODE();
233
234 PrevCount = RunRef->Count;
235
236 if (PrevCount != 0 && !(PrevCount & EX_RUNDOWN_ACTIVE))
237 {
238 /* save the reference counter */
239 RundownDescriptor.References = PrevCount >> EX_RUNDOWN_COUNT_SHIFT;
240
241 /* Pending references... wait on them to be closed with an event */
242 KeInitializeEvent(&RundownDescriptor.RundownEvent, NotificationEvent, FALSE);
243
244 ASSERT(!((ULONG_PTR)&RundownDescriptor & EX_RUNDOWN_ACTIVE));
245
246 NewPtr = (ULONG_PTR)&RundownDescriptor | EX_RUNDOWN_ACTIVE;
247
248 for (;;)
249 {
250 #ifdef _WIN64
251 PrevPtr = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Ptr, (LONGLONG)NewPtr, (LONGLONG)PrevCount);
252 #else
253 PrevPtr = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Ptr, (LONG)NewPtr, (LONG)PrevCount);
254 #endif
255 if (PrevPtr == PrevCount)
256 {
257 /* Wait for whoever needs to release to notify us */
258 KeWaitForSingleObject(&RundownDescriptor.RundownEvent, Executive, KernelMode, FALSE, NULL);
259 break;
260 }
261 else if (PrevPtr == 0 || (PrevPtr & EX_RUNDOWN_ACTIVE))
262 {
263 /* some one else was faster, let's just bail */
264 break;
265 }
266
267 PrevCount = PrevPtr;
268
269 /* save the changed reference counter and try again */
270 RundownDescriptor.References = PrevCount >> EX_RUNDOWN_COUNT_SHIFT;
271 }
272 }
273 }
274
275 /* EOF */