39dfa89770ebb6175e8f34734fb1b76bd31eae96
[reactos.git] / reactos / ntoskrnl / ex / rundown.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998 - 2004 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /*
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/ex/rundown.c
22 * PURPOSE: Rundown Protection Functions
23 * PORTABILITY: Checked
24 */
25
26 /* INCLUDES *****************************************************************/
27
28 #include <ntoskrnl.h>
29 #define NDEBUG
30 #include <internal/debug.h>
31
32 /* FUNCTIONS *****************************************************************/
33
34 /*
35 * @implemented
36 */
37 BOOLEAN
38 FASTCALL
39 ExAcquireRundownProtection (
40 IN PEX_RUNDOWN_REF RunRef
41 )
42 {
43 /* Call the general function with only one Reference add */
44 return ExAcquireRundownProtectionEx(RunRef, 1);
45 }
46
47 /*
48 * @implemented
49 */
50 BOOLEAN
51 FASTCALL
52 ExAcquireRundownProtectionEx (
53 IN PEX_RUNDOWN_REF RunRef,
54 IN ULONG Count
55 )
56 {
57 ULONG_PTR PrevCount, Current;
58
59 Count <<= EX_RUNDOWN_COUNT_SHIFT;
60
61 /* Loop until successfully incremented the counter */
62 do
63 {
64 Current = RunRef->Count;
65
66 /* Make sure a rundown is not active */
67 if (Current & EX_RUNDOWN_ACTIVE)
68 {
69 return FALSE;
70 }
71
72 #ifdef _WIN64
73 PrevCount = (ULONG_PTR)InterlockedExchangeAdd64((LONGLONG*)&RunRef->Count, (LONGLONG)Count);
74 #else
75 PrevCount = (ULONG_PTR)InterlockedExchangeAdd((LONG*)&RunRef->Count, (LONG)Count);
76 #endif
77 } while (PrevCount != Current);
78
79 /* Return Success */
80 return TRUE;
81 }
82
83 /*
84 * @implemented
85 */
86 VOID
87 FASTCALL
88 ExInitializeRundownProtection (
89 IN PEX_RUNDOWN_REF RunRef
90 )
91 {
92 /* Set the count to zero */
93 RunRef->Count = 0;
94 }
95
96 /*
97 * @implemented
98 */
99 VOID
100 FASTCALL
101 ExReInitializeRundownProtection (
102 IN PEX_RUNDOWN_REF RunRef
103 )
104 {
105 /* Reset the count */
106 #ifdef _WIN64
107 InterlockedExchangeAdd64((LONGLONG*)&RunRef->Count, 0LL);
108 #else
109 InterlockedExchangeAdd((LONG*)&RunRef->Count, 0);
110 #endif
111 }
112
113
114 /*
115 * @implemented
116 */
117 VOID
118 FASTCALL
119 ExReleaseRundownProtectionEx (
120 IN PEX_RUNDOWN_REF RunRef,
121 IN ULONG Count
122 )
123 {
124 Count <<= EX_RUNDOWN_COUNT_SHIFT;
125
126 for (;;)
127 {
128 ULONG_PTR Current = RunRef->Count;
129
130 /* Check if Rundown is active */
131 if (Current & EX_RUNDOWN_ACTIVE)
132 {
133 /* Get Pointer */
134 PRUNDOWN_DESCRIPTOR RundownDescriptor = (PRUNDOWN_DESCRIPTOR)((ULONG_PTR)RunRef->Ptr & ~EX_RUNDOWN_ACTIVE);
135
136 /* Decrease Reference Count by RundownDescriptor->References */
137 for (;;)
138 {
139 ULONG_PTR PrevCount, NewCount;
140
141 if ((Current >> EX_RUNDOWN_COUNT_SHIFT) == RundownDescriptor->References)
142 {
143 NewCount = 0;
144 }
145 else
146 {
147 NewCount = (((Current >> EX_RUNDOWN_COUNT_SHIFT) - RundownDescriptor->References) << EX_RUNDOWN_COUNT_SHIFT) | EX_RUNDOWN_ACTIVE;
148 }
149 #ifdef _WIN64
150 PrevCount = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Count, (LONGLONG)NewCount, (LONGLONG)Current);
151 #else
152 PrevCount = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Count, (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 /* mark the */
211 #ifdef _WIN64
212 InterlockedExchange64((LONGLONG*)&RunRef->Count, (LONGLONG)EX_RUNDOWN_ACTIVE);
213 #else
214 InterlockedExchange((LONG*)&RunRef->Count, EX_RUNDOWN_ACTIVE);
215 #endif
216 }
217
218 /*
219 * @implemented
220 */
221 VOID
222 FASTCALL
223 ExWaitForRundownProtectionRelease (
224 IN PEX_RUNDOWN_REF RunRef
225 )
226 {
227 ULONG_PTR PrevCount, NewPtr, PrevPtr;
228 RUNDOWN_DESCRIPTOR RundownDescriptor;
229
230 #ifdef _WIN64
231 PrevCount = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Ptr, (LONGLONG)EX_RUNDOWN_ACTIVE, 0LL);
232 #else
233 PrevCount = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Ptr, EX_RUNDOWN_ACTIVE, 0);
234 #endif
235
236 if (PrevCount == 0 ||
237 PrevCount & EX_RUNDOWN_ACTIVE)
238 {
239 return;
240 }
241
242 /* save the reference counter */
243 RundownDescriptor.References = PrevCount >> EX_RUNDOWN_COUNT_SHIFT;
244
245 /* Pending references... wait on them to be closed with an event */
246 KeInitializeEvent(&RundownDescriptor.RundownEvent, NotificationEvent, FALSE);
247
248 NewPtr = (ULONG_PTR)&RundownDescriptor | EX_RUNDOWN_ACTIVE;
249 PrevCount = EX_RUNDOWN_ACTIVE;
250
251 do
252 {
253 #ifdef _WIN64
254 PrevPtr = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Ptr, (LONGLONG)NewPtr, (LONGLONG)PrevCount);
255 #else
256 PrevPtr = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Ptr, (LONG)NewPtr, (LONG)PrevCount);
257 #endif
258
259 PrevCount = PrevPtr;
260 } while (PrevPtr != PrevCount);
261
262 /* Wait for whoever needs to release to notify us */
263 KeWaitForSingleObject(&RundownDescriptor.RundownEvent, Executive, KernelMode, FALSE, NULL);
264 }
265
266 /* EOF */