3 * Copyright (C) 1998 - 2004 ReactOS Team
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.
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.
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.
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/ex/rundown.c
22 * PURPOSE: Rundown Protection Functions
23 * PORTABILITY: Checked
26 /* INCLUDES *****************************************************************/
30 #include <internal/debug.h>
32 /* FUNCTIONS *****************************************************************/
39 ExAcquireRundownProtection (
40 IN PEX_RUNDOWN_REF RunRef
43 /* Call the general function with only one Reference add */
44 return ExAcquireRundownProtectionEx(RunRef
, 1);
52 ExAcquireRundownProtectionEx (
53 IN PEX_RUNDOWN_REF RunRef
,
57 ULONG_PTR PrevCount
, Current
;
59 Count
<<= EX_RUNDOWN_COUNT_SHIFT
;
61 /* Loop until successfully incremented the counter */
64 Current
= RunRef
->Count
;
66 /* Make sure a rundown is not active */
67 if (Current
& EX_RUNDOWN_ACTIVE
)
73 PrevCount
= (ULONG_PTR
)InterlockedExchangeAdd64((LONGLONG
*)&RunRef
->Count
, (LONGLONG
)Count
);
75 PrevCount
= (ULONG_PTR
)InterlockedExchangeAdd((LONG
*)&RunRef
->Count
, (LONG
)Count
);
77 } while (PrevCount
!= Current
);
88 ExInitializeRundownProtection (
89 IN PEX_RUNDOWN_REF RunRef
92 /* Set the count to zero */
101 ExReInitializeRundownProtection (
102 IN PEX_RUNDOWN_REF RunRef
105 /* Reset the count */
107 InterlockedExchangeAdd64((LONGLONG
*)&RunRef
->Count
, 0LL);
109 InterlockedExchangeAdd((LONG
*)&RunRef
->Count
, 0);
119 ExReleaseRundownProtectionEx (
120 IN PEX_RUNDOWN_REF RunRef
,
124 Count
<<= EX_RUNDOWN_COUNT_SHIFT
;
128 ULONG_PTR Current
= RunRef
->Count
;
130 /* Check if Rundown is active */
131 if (Current
& EX_RUNDOWN_ACTIVE
)
134 PRUNDOWN_DESCRIPTOR RundownDescriptor
= (PRUNDOWN_DESCRIPTOR
)((ULONG_PTR
)RunRef
->Ptr
& ~EX_RUNDOWN_ACTIVE
);
136 /* Decrease Reference Count by RundownDescriptor->References */
139 ULONG_PTR PrevCount
, NewCount
;
141 if ((Current
>> EX_RUNDOWN_COUNT_SHIFT
) == RundownDescriptor
->References
)
147 NewCount
= (((Current
>> EX_RUNDOWN_COUNT_SHIFT
) - RundownDescriptor
->References
) << EX_RUNDOWN_COUNT_SHIFT
) | EX_RUNDOWN_ACTIVE
;
150 PrevCount
= (ULONG_PTR
)InterlockedCompareExchange64((LONGLONG
*)&RunRef
->Count
, (LONGLONG
)NewCount
, (LONGLONG
)Current
);
152 PrevCount
= (ULONG_PTR
)InterlockedCompareExchange((LONG
*)&RunRef
->Count
, (LONG
)NewCount
, (LONG
)Current
);
154 if (PrevCount
== Current
)
158 /* Signal the event so anyone waiting on it can now kill it */
159 KeSetEvent(&RundownDescriptor
->RundownEvent
, 0, FALSE
);
162 /* Successfully decremented the counter, so bail! */
173 ULONG_PTR PrevCount
, NewCount
= Current
- (ULONG_PTR
)Count
;
175 PrevCount
= (ULONG_PTR
)InterlockedCompareExchange64((LONGLONG
*)&RunRef
->Count
, (LONGLONG
)NewCount
, (LONGLONG
)Current
);
177 PrevCount
= (ULONG_PTR
)InterlockedCompareExchange((LONG
*)&RunRef
->Count
, (LONG
)NewCount
, (LONG
)Current
);
179 if (PrevCount
== Current
)
181 /* Successfully decremented the counter, so bail! */
193 ExReleaseRundownProtection (
194 IN PEX_RUNDOWN_REF RunRef
197 /* Call the general function with only 1 reference removal */
198 ExReleaseRundownProtectionEx(RunRef
, 1);
207 IN PEX_RUNDOWN_REF RunRef
212 InterlockedExchange64((LONGLONG
*)&RunRef
->Count
, (LONGLONG
)EX_RUNDOWN_ACTIVE
);
214 InterlockedExchange((LONG
*)&RunRef
->Count
, EX_RUNDOWN_ACTIVE
);
223 ExWaitForRundownProtectionRelease (
224 IN PEX_RUNDOWN_REF RunRef
227 ULONG_PTR PrevCount
, NewPtr
, PrevPtr
;
228 RUNDOWN_DESCRIPTOR RundownDescriptor
;
231 PrevCount
= (ULONG_PTR
)InterlockedCompareExchange64((LONGLONG
*)&RunRef
->Ptr
, (LONGLONG
)EX_RUNDOWN_ACTIVE
, 0LL);
233 PrevCount
= (ULONG_PTR
)InterlockedCompareExchange((LONG
*)&RunRef
->Ptr
, EX_RUNDOWN_ACTIVE
, 0);
236 if (PrevCount
== 0 ||
237 PrevCount
& EX_RUNDOWN_ACTIVE
)
242 /* save the reference counter */
243 RundownDescriptor
.References
= PrevCount
>> EX_RUNDOWN_COUNT_SHIFT
;
245 /* Pending references... wait on them to be closed with an event */
246 KeInitializeEvent(&RundownDescriptor
.RundownEvent
, NotificationEvent
, FALSE
);
248 NewPtr
= (ULONG_PTR
)&RundownDescriptor
| EX_RUNDOWN_ACTIVE
;
249 PrevCount
= EX_RUNDOWN_ACTIVE
;
254 PrevPtr
= (ULONG_PTR
)InterlockedCompareExchange64((LONGLONG
*)&RunRef
->Ptr
, (LONGLONG
)NewPtr
, (LONGLONG
)PrevCount
);
256 PrevPtr
= (ULONG_PTR
)InterlockedCompareExchange((LONG
*)&RunRef
->Ptr
, (LONG
)NewPtr
, (LONG
)PrevCount
);
260 } while (PrevPtr
!= PrevCount
);
262 /* Wait for whoever needs to release to notify us */
263 KeWaitForSingleObject(&RundownDescriptor
.RundownEvent
, Executive
, KernelMode
, FALSE
, NULL
);