2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ex/rundown.c
5 * PURPOSE: Rundown Protection Functions
7 * PROGRAMMERS: Alex Ionescu & Thomas Weidenmueller - Implementation
10 /* INCLUDES *****************************************************************/
14 #include <internal/debug.h>
16 /* FUNCTIONS *****************************************************************/
23 ExAcquireRundownProtection (
24 IN PEX_RUNDOWN_REF RunRef
27 /* Call the general function with only one Reference add */
28 return ExAcquireRundownProtectionEx(RunRef
, 1);
36 ExAcquireRundownProtectionEx (
37 IN PEX_RUNDOWN_REF RunRef
,
41 ULONG_PTR PrevCount
, Current
;
45 Count
<<= EX_RUNDOWN_COUNT_SHIFT
;
47 /* Loop until successfully incremented the counter */
50 Current
= RunRef
->Count
;
52 /* Make sure a rundown is not active */
53 if (Current
& EX_RUNDOWN_ACTIVE
)
59 PrevCount
= (ULONG_PTR
)InterlockedExchangeAdd64((LONGLONG
*)&RunRef
->Count
, (LONGLONG
)Count
);
61 PrevCount
= (ULONG_PTR
)InterlockedExchangeAdd((LONG
*)&RunRef
->Count
, (LONG
)Count
);
63 } while (PrevCount
!= Current
);
74 ExInitializeRundownProtection (
75 IN PEX_RUNDOWN_REF RunRef
80 /* Set the count to zero */
89 ExReInitializeRundownProtection (
90 IN PEX_RUNDOWN_REF RunRef
97 InterlockedExchangeAdd64((LONGLONG
*)&RunRef
->Count
, 0LL);
99 InterlockedExchangeAdd((LONG
*)&RunRef
->Count
, 0);
109 ExReleaseRundownProtectionEx (
110 IN PEX_RUNDOWN_REF RunRef
,
116 Count
<<= EX_RUNDOWN_COUNT_SHIFT
;
120 ULONG_PTR Current
= RunRef
->Count
;
122 /* Check if Rundown is active */
123 if (Current
& EX_RUNDOWN_ACTIVE
)
126 PRUNDOWN_DESCRIPTOR RundownDescriptor
= (PRUNDOWN_DESCRIPTOR
)(Current
& ~EX_RUNDOWN_ACTIVE
);
128 if (RundownDescriptor
== NULL
)
130 /* the rundown was completed and there's no one to notify */
134 Current
= RundownDescriptor
->References
;
136 /* Decrease RundownDescriptor->References by Count references */
139 ULONG_PTR PrevCount
, NewCount
;
141 if ((Count
>> EX_RUNDOWN_COUNT_SHIFT
) == Current
)
147 NewCount
= ((RundownDescriptor
->References
- (Count
>> EX_RUNDOWN_COUNT_SHIFT
)) << EX_RUNDOWN_COUNT_SHIFT
) | EX_RUNDOWN_ACTIVE
;
150 PrevCount
= (ULONG_PTR
)InterlockedCompareExchange64((LONGLONG
*)&RundownDescriptor
->References
, (LONGLONG
)NewCount
, (LONGLONG
)Current
);
152 PrevCount
= (ULONG_PTR
)InterlockedCompareExchange((LONG
*)&RundownDescriptor
->References
, (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 /* mark the counter as active */
214 InterlockedExchange64((LONGLONG
*)&RunRef
->Count
, (LONGLONG
)EX_RUNDOWN_ACTIVE
);
216 InterlockedExchange((LONG
*)&RunRef
->Count
, EX_RUNDOWN_ACTIVE
);
225 ExWaitForRundownProtectionRelease (
226 IN PEX_RUNDOWN_REF RunRef
229 ULONG_PTR PrevCount
, NewPtr
, PrevPtr
;
230 RUNDOWN_DESCRIPTOR RundownDescriptor
;
234 PrevCount
= RunRef
->Count
;
236 if (PrevCount
!= 0 && !(PrevCount
& EX_RUNDOWN_ACTIVE
))
238 /* save the reference counter */
239 RundownDescriptor
.References
= PrevCount
>> EX_RUNDOWN_COUNT_SHIFT
;
241 /* Pending references... wait on them to be closed with an event */
242 KeInitializeEvent(&RundownDescriptor
.RundownEvent
, NotificationEvent
, FALSE
);
244 ASSERT(!((ULONG_PTR
)&RundownDescriptor
& EX_RUNDOWN_ACTIVE
));
246 NewPtr
= (ULONG_PTR
)&RundownDescriptor
| EX_RUNDOWN_ACTIVE
;
251 PrevPtr
= (ULONG_PTR
)InterlockedCompareExchange64((LONGLONG
*)&RunRef
->Ptr
, (LONGLONG
)NewPtr
, (LONGLONG
)PrevCount
);
253 PrevPtr
= (ULONG_PTR
)InterlockedCompareExchange((LONG
*)&RunRef
->Ptr
, (LONG
)NewPtr
, (LONG
)PrevCount
);
255 if (PrevPtr
== PrevCount
)
257 /* Wait for whoever needs to release to notify us */
258 KeWaitForSingleObject(&RundownDescriptor
.RundownEvent
, Executive
, KernelMode
, FALSE
, NULL
);
261 else if (PrevPtr
== 0 || (PrevPtr
& EX_RUNDOWN_ACTIVE
))
263 /* some one else was faster, let's just bail */
269 /* save the changed reference counter and try again */
270 RundownDescriptor
.References
= PrevCount
>> EX_RUNDOWN_COUNT_SHIFT
;