- Refactor Fast Reference implementation into a generic module with init/acquire/release/compare/insert interfaces, and update the executive callback & object manager referencing code to use it.

- Always capture fast reference values on the stack since a fast reference is by nature volatile.
- Based off earlier work from Alex to fix callback implementation.

svn path=/trunk/; revision=39859
This commit is contained in:
Aleksey Bragin 2009-03-03 20:24:10 +00:00
parent 516e7fa09c
commit c712fa746c
3 changed files with 318 additions and 270 deletions

View file

@ -45,8 +45,8 @@ VOID
NTAPI
ExInitializeCallBack(IN OUT PEX_CALLBACK Callback)
{
/* Initialize the fast references */
Callback->RoutineBlock.Object = NULL;
/* Initialize the fast reference */
ExInitializeFastReference(&Callback->RoutineBlock, NULL);
}
PEX_CALLBACK_ROUTINE_BLOCK
@ -54,93 +54,66 @@ NTAPI
ExAllocateCallBack(IN PEX_CALLBACK_FUNCTION Function,
IN PVOID Context)
{
PEX_CALLBACK_ROUTINE_BLOCK Callback;
PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock;
/* Allocate a callback */
Callback = ExAllocatePoolWithTag(PagedPool,
sizeof(*Callback),
TAG('C', 'b', 'r', 'b'));
if (Callback)
CallbackBlock = ExAllocatePoolWithTag(PagedPool,
sizeof(EX_CALLBACK_ROUTINE_BLOCK),
'CbRb');
if (CallbackBlock)
{
/* Initialize it */
Callback->Function = Function;
Callback->Context = Context;
ExInitializeRundownProtection(&Callback->RundownProtect);
CallbackBlock->Function = Function;
CallbackBlock->Context = Context;
ExInitializeRundownProtection(&CallbackBlock->RundownProtect);
}
/* Return it */
return Callback;
return CallbackBlock;
}
VOID
NTAPI
ExFreeCallBack(IN PEX_CALLBACK_ROUTINE_BLOCK Callback)
ExFreeCallBack(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
{
/* Just free it from memory */
ExFreePool(Callback);
ExFreePool(CallbackBlock);
}
VOID
NTAPI
ExWaitForCallBacks(IN PEX_CALLBACK_ROUTINE_BLOCK Callback)
ExWaitForCallBacks(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
{
/* Wait on the rundown */
ExWaitForRundownProtectionRelease(&Callback->RundownProtect);
ExWaitForRundownProtectionRelease(&CallbackBlock->RundownProtect);
}
PEX_CALLBACK_FUNCTION
NTAPI
ExGetCallBackBlockRoutine(IN PEX_CALLBACK_ROUTINE_BLOCK Callback)
ExGetCallBackBlockRoutine(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
{
/* Return the function */
return Callback->Function;
return CallbackBlock->Function;
}
PVOID
NTAPI
ExGetCallBackBlockContext(IN PEX_CALLBACK_ROUTINE_BLOCK Callback)
ExGetCallBackBlockContext(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
{
/* Return the context */
return Callback->Context;
return CallbackBlock->Context;
}
VOID
NTAPI
ExDereferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack,
IN PEX_CALLBACK_ROUTINE_BLOCK CallbackRoutineBlock)
IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
{
PEX_FAST_REF FastRef = &CallBack->RoutineBlock;
EX_FAST_REF Value, NewValue;
/* Sanity checks */
ASSERT(CallbackRoutineBlock);
ASSERT(!(((ULONG_PTR)CallbackRoutineBlock) & MAX_FAST_REFS));
/* Start dereference loop */
for (;;)
/* Release a fast reference */
if (!ExReleaseFastReference(&CallBack->RoutineBlock, CallbackBlock))
{
/* Get the current count */
Value = *FastRef;
if ((Value.Value ^ (ULONG_PTR)CallbackRoutineBlock) < MAX_FAST_REFS)
{
/* Decrease the reference count */
NewValue.Value = Value.Value + 1;
NewValue.Object = InterlockedCompareExchangePointer(&FastRef->Object,
NewValue.Object,
Value.Object);
if (NewValue.Object != Value.Object) continue;
/* We're all done */
break;
}
else
{
/* Release rundown protection */
ExReleaseRundownProtection(&CallbackRoutineBlock->RundownProtect);
/* We're all done */
break;
}
/* Take slow path */
ExReleaseRundownProtection(&CallbackBlock->RundownProtect);
}
}
@ -148,89 +121,48 @@ PEX_CALLBACK_ROUTINE_BLOCK
NTAPI
ExReferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack)
{
PEX_FAST_REF FastRef = &CallBack->RoutineBlock;
EX_FAST_REF Value, NewValue;
PEX_CALLBACK_ROUTINE_BLOCK CallbackRoutineBlock;
EX_FAST_REF OldValue;
ULONG_PTR Count;
PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock;
/* Start reference loop */
for (;;)
{
/* Get the current count */
Value = *FastRef;
if (Value.RefCnt != 0)
{
/* Increase the reference count */
NewValue.Value = Value.Value - 1;
NewValue.Object = InterlockedCompareExchangePointer(&FastRef->Object,
NewValue.Object,
Value.Object);
if (NewValue.Object != Value.Object) continue;
}
/* All done */
break;
}
/* Acquire a reference */
OldValue = ExAcquireFastReference(&CallBack->RoutineBlock);
Count = ExGetCountFastReference(OldValue);
/* Fail if there isn't any object */
if (!Value.Value) return NULL;
if (!ExGetObjectFastReference(OldValue)) return NULL;
/* Check if we don't have a reference */
if (!Value.RefCnt)
if (!Count)
{
/* FIXME: Race */
CallbackRoutineBlock = NULL;
DPRINT1("Unhandled callback race condition\n");
ASSERT(FALSE);
return NULL;
}
else
{
/* Get the callback block */
CallbackRoutineBlock = (PVOID)(Value.Value &~ MAX_FAST_REFS);
CallbackBlock = ExGetObjectFastReference(OldValue);
/* Check if this is the last reference */
if (Value.RefCnt == 1)
if (Count == 1)
{
/* Acquire rundown protection */
if (ExfAcquireRundownProtectionEx(&CallbackRoutineBlock->
RundownProtect,
if (ExfAcquireRundownProtectionEx(&CallbackBlock->RundownProtect,
MAX_FAST_REFS))
{
/* Sanity check */
ASSERT(!(((ULONG_PTR)CallbackRoutineBlock) & MAX_FAST_REFS));
/* Start reference loop */
for (;;)
{
/* Check if the current count is too high */
Value = *FastRef;
if (((Value.RefCnt + MAX_FAST_REFS) > MAX_FAST_REFS) ||
((Value.Value &~ MAX_FAST_REFS) !=
(ULONG_PTR)CallbackRoutineBlock))
/* Insert references */
if (!ExInsertFastReference(&CallBack->RoutineBlock, CallbackBlock))
{
/* Backdown the rundown acquire */
ExfReleaseRundownProtectionEx(&CallbackRoutineBlock->
RundownProtect,
ExfReleaseRundownProtectionEx(&CallbackBlock->RundownProtect,
MAX_FAST_REFS);
break;
}
/* Increase the reference count */
NewValue.Value = Value.Value + MAX_FAST_REFS;
NewValue.Object =
InterlockedCompareExchangePointer(&FastRef->Object,
NewValue.Object,
Value.Object);
if (NewValue.Object != Value.Object) continue;
/* Break out if the change was OK */
break;
}
}
}
}
/* Return the callback block */
return CallbackRoutineBlock;
return CallbackBlock;
}
BOOLEAN
@ -239,9 +171,9 @@ ExCompareExchangeCallBack(IN OUT PEX_CALLBACK CallBack,
IN PEX_CALLBACK_ROUTINE_BLOCK NewBlock,
IN PEX_CALLBACK_ROUTINE_BLOCK OldBlock)
{
EX_FAST_REF Value, NewValue;
PEX_CALLBACK_ROUTINE_BLOCK CallbackRoutineBlock;
PEX_FAST_REF FastRef = &CallBack->RoutineBlock;
EX_FAST_REF OldValue;
PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock;
ULONG_PTR Count;
/* Check that we have a new block */
if (NewBlock)
@ -256,46 +188,20 @@ ExCompareExchangeCallBack(IN OUT PEX_CALLBACK CallBack,
}
}
/* Sanity check and start swap loop */
ASSERT(!(((ULONG_PTR)NewBlock) & MAX_FAST_REFS));
for (;;)
{
/* Get the current value */
Value = *FastRef;
/* Make sure there's enough references to swap */
if (!((Value.Value ^ (ULONG_PTR)OldBlock) <= MAX_FAST_REFS)) break;
/* Check if we have an object to swap */
if (NewBlock)
{
/* Set up the value with maximum fast references */
NewValue.Value = (ULONG_PTR)NewBlock | MAX_FAST_REFS;
}
else
{
/* Write the object address itself (which is empty) */
NewValue.Value = (ULONG_PTR)NewBlock;
}
/* Do the actual compare exchange */
NewValue.Object = InterlockedCompareExchangePointer(&FastRef->Object,
NewValue.Object,
Value.Object);
if (NewValue.Object != Value.Object) continue;
/* All done */
break;
}
/* Do the swap */
OldValue = ExCompareSwapFastReference(&CallBack->RoutineBlock,
NewBlock,
OldBlock);
/* Get the routine block */
CallbackRoutineBlock = (PVOID)(Value.Value & ~MAX_FAST_REFS);
CallbackBlock = ExGetObjectFastReference(OldValue);
Count = ExGetCountFastReference(OldValue);
/* Make sure the swap worked */
if (CallbackRoutineBlock == OldBlock)
if (CallbackBlock == OldBlock)
{
/* Make sure we replaced a valid pointer */
if (CallbackRoutineBlock)
if (CallbackBlock)
{
/* Acquire the flush lock and immediately release it */
KeEnterCriticalRegion();
@ -303,8 +209,8 @@ ExCompareExchangeCallBack(IN OUT PEX_CALLBACK CallBack,
/* Release rundown protection */
KeLeaveCriticalRegion();
ExfReleaseRundownProtectionEx(&CallbackRoutineBlock->RundownProtect,
Value.RefCnt + 1);
ExfReleaseRundownProtectionEx(&CallbackBlock->RundownProtect,
Count + 1);
}
/* Compare worked */

View file

@ -119,6 +119,15 @@ typedef struct
#endif
#ifdef _WIN64
#define ExpChangeRundown(x, y, z) InterlockedCompareExchange64((PLONGLONG)x, y, z)
#define ExpSetRundown(x, y) InterlockedExchange64((PLONGLONG)x, y)
#else
#define ExpChangeRundown(x, y, z) PtrToUlong(InterlockedCompareExchange((PLONG)x, PtrToLong(y), PtrToLong(z)))
#define ExpChangePushlock(x, y, z) LongToPtr(InterlockedCompareExchange((PLONG)x, PtrToLong(y), PtrToLong(z)))
#define ExpSetRundown(x, y) InterlockedExchange((PLONG)x, y)
#endif
/* INITIALIZATION FUNCTIONS *************************************************/
VOID
@ -439,35 +448,239 @@ ExDoCallBack(IN OUT PEX_CALLBACK Callback,
IN PVOID Argument1,
IN PVOID Argument2)
{
PEX_CALLBACK_ROUTINE_BLOCK CallbackRoutineBlock;
PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock;
PEX_CALLBACK_FUNCTION Function;
/* Reference the block */
CallbackRoutineBlock = ExReferenceCallBackBlock(Callback);
if (CallbackRoutineBlock)
CallbackBlock = ExReferenceCallBackBlock(Callback);
if (CallbackBlock)
{
/* Get the function */
Function = ExGetCallBackBlockRoutine(CallbackRoutineBlock);
Function = ExGetCallBackBlockRoutine(CallbackBlock);
/* Do the callback */
Function(Context, Argument1, Argument2);
/* Now dereference it */
ExDereferenceCallBackBlock(Callback, CallbackRoutineBlock);
ExDereferenceCallBackBlock(Callback, CallbackBlock);
}
}
/* FAST REFS ******************************************************************/
FORCEINLINE
PVOID
ExGetObjectFastReference(IN EX_FAST_REF FastRef)
{
/* Return the unbiased pointer */
return (PVOID)(FastRef.Value & ~MAX_FAST_REFS);
}
FORCEINLINE
ULONG
ExGetCountFastReference(IN EX_FAST_REF FastRef)
{
/* Return the reference count */
return FastRef.RefCnt;
}
FORCEINLINE
VOID
ExInitializeFastReference(OUT PEX_FAST_REF FastRef,
IN OPTIONAL PVOID Object)
{
/* Sanity check */
ASSERT((((ULONG_PTR)Object) & MAX_FAST_REFS) == 0);
/* Check if an object is being set */
if (!Object)
{
/* Clear the field */
FastRef->Object = NULL;
}
else
{
/* Otherwise, we assume the object was referenced and is ready */
FastRef->Value = (ULONG_PTR)Object | MAX_FAST_REFS;
}
}
FORCEINLINE
EX_FAST_REF
ExAcquireFastReference(IN OUT PEX_FAST_REF FastRef)
{
EX_FAST_REF OldValue, NewValue;
/* Start reference loop */
for (;;)
{
/* Get the current reference count */
OldValue = *FastRef;
if (OldValue.RefCnt)
{
/* Increase the reference count */
NewValue.Value = OldValue.Value - 1;
NewValue.Object = ExpChangePushlock(&FastRef->Object,
NewValue.Object,
OldValue.Object);
if (NewValue.Object != OldValue.Object) continue;
}
/* We are done */
break;
}
/* Return the old value */
return OldValue;
}
FORCEINLINE
BOOLEAN
ExInsertFastReference(IN OUT PEX_FAST_REF FastRef,
IN PVOID Object)
{
EX_FAST_REF OldValue, NewValue;
/* Sanity checks */
ASSERT(!(((ULONG_PTR)Object) & MAX_FAST_REFS));
/* Start update loop */
for (;;)
{
/* Get the current reference count */
OldValue = *FastRef;
/* Check if the current count is too high or if the pointer changed */
if (((OldValue.RefCnt + MAX_FAST_REFS) > MAX_FAST_REFS) ||
((OldValue.Value &~ MAX_FAST_REFS) != (ULONG_PTR)Object))
{
/* Fail */
return FALSE;
}
/* Update the reference count */
NewValue.Value = OldValue.Value + MAX_FAST_REFS;
NewValue.Object = ExpChangePushlock(&FastRef->Object,
NewValue.Object,
OldValue.Object);
if (NewValue.Object != OldValue.Object) continue;
/* We are done */
break;
}
/* Return success */
return TRUE;
}
BOOLEAN
FORCEINLINE
ExReleaseFastReference(IN PEX_FAST_REF FastRef,
IN PVOID Object)
{
EX_FAST_REF OldValue, NewValue;
/* Sanity checks */
ASSERT(Object != NULL);
ASSERT(!(((ULONG_PTR)Object) & MAX_FAST_REFS));
/* Start reference loop */
for (;;)
{
/* Get the current reference count */
OldValue = *FastRef;
/* Check if we're full if if the pointer changed */
if ((OldValue.Value ^ (ULONG_PTR)Object) >= MAX_FAST_REFS) return FALSE;
/* Decrease the reference count */
NewValue.Value = OldValue.Value + 1;
NewValue.Object = ExpChangePushlock(&FastRef->Object,
NewValue.Object,
OldValue.Object);
if (NewValue.Object != OldValue.Object) continue;
/* We are done */
break;
}
/* Return success */
return TRUE;
}
EX_FAST_REF
FORCEINLINE
ExSwapFastReference(IN PEX_FAST_REF FastRef,
IN PVOID Object)
{
EX_FAST_REF NewValue, OldValue;
/* Sanity check */
ASSERT((((ULONG_PTR)Object) & MAX_FAST_REFS) == 0);
/* Check if an object is being set */
if (!Object)
{
/* Clear the field */
NewValue.Object = NULL;
}
else
{
/* Otherwise, we assume the object was referenced and is ready */
NewValue.Value = (ULONG_PTR)Object | MAX_FAST_REFS;
}
/* Update the object */
OldValue.Object = InterlockedExchangePointer(&FastRef->Object, NewValue.Object);
return OldValue;
}
EX_FAST_REF
FORCEINLINE
ExCompareSwapFastReference(IN PEX_FAST_REF FastRef,
IN PVOID Object,
IN PVOID OldObject)
{
EX_FAST_REF OldValue, NewValue;
/* Sanity check and start swap loop */
ASSERT(!(((ULONG_PTR)Object) & MAX_FAST_REFS));
for (;;)
{
/* Get the current value */
OldValue = *FastRef;
/* Make sure there's enough references to swap */
if (!((OldValue.Value ^ (ULONG_PTR)OldObject) <= MAX_FAST_REFS)) break;
/* Check if we have an object to swap */
if (Object)
{
/* Set up the value with maximum fast references */
NewValue.Value = (ULONG_PTR)Object | MAX_FAST_REFS;
}
else
{
/* Write the object address itself (which is empty) */
NewValue.Value = (ULONG_PTR)Object;
}
/* Do the actual compare exchange */
NewValue.Object = ExpChangePushlock(&FastRef->Object,
NewValue.Object,
OldValue.Object);
if (NewValue.Object != OldValue.Object) continue;
/* All done */
break;
}
/* Return the old value */
return OldValue;
}
/* RUNDOWN *******************************************************************/
#ifdef _WIN64
#define ExpChangeRundown(x, y, z) InterlockedCompareExchange64((PLONGLONG)x, y, z)
#define ExpSetRundown(x, y) InterlockedExchange64((PLONGLONG)x, y)
#else
#define ExpChangeRundown(x, y, z) PtrToUlong(InterlockedCompareExchange((PLONG)x, PtrToLong(y), PtrToLong(z)))
#define ExpChangePushlock(x, y, z) LongToPtr(InterlockedCompareExchange((PLONG)x, PtrToLong(y), PtrToLong(z)))
#define ExpSetRundown(x, y) InterlockedExchange((PLONG)x, y)
#endif
/*++
* @name ExfAcquireRundownProtection
* INTERNAL MACRO

View file

@ -110,20 +110,8 @@ ObInitializeFastReference(IN PEX_FAST_REF FastRef,
/* Check if we were given an object and reference it 7 times */
if (Object) ObReferenceObjectEx(Object, MAX_FAST_REFS);
/* Sanity check */
ASSERT(!(((ULONG_PTR)Object) & MAX_FAST_REFS));
/* Check if the caller gave us an object */
if (Object)
{
/* He did, so write the biased pointer */
FastRef->Object = (PVOID)((ULONG_PTR)Object | MAX_FAST_REFS);
}
else
{
/* Otherwise, clear the current object */
FastRef->Object = NULL;
}
/* Setup the fast reference */
ExInitializeFastReference(FastRef, Object);
}
PVOID
@ -131,9 +119,10 @@ FASTCALL
ObFastReferenceObjectLocked(IN PEX_FAST_REF FastRef)
{
PVOID Object;
EX_FAST_REF OldValue = *FastRef;
/* Get the object and reference it slowly */
Object = (PVOID)((ULONG_PTR)FastRef->Object & MAX_FAST_REFS);
Object = ExGetObjectFastReference(OldValue);
if (Object) ObReferenceObject(Object);
return Object;
}
@ -142,25 +131,16 @@ PVOID
FASTCALL
ObFastReferenceObject(IN PEX_FAST_REF FastRef)
{
ULONG_PTR Value, NewValue;
EX_FAST_REF OldValue;
ULONG_PTR Count;
PVOID Object;
/* Start reference loop */
for (;;)
{
/* Get the current count */
Value = FastRef->Value;
if (!(Value & MAX_FAST_REFS)) break;
/* Reference the object and get it pointer */
OldValue = ExAcquireFastReference(FastRef);
Object = ExGetObjectFastReference(OldValue);
/* Increase the reference count */
NewValue = Value - 1;
if (ExpChangeRundown(FastRef, NewValue, Value) == Value) break;
}
/* Get the object and count */
Object = (PVOID)(Value &~ MAX_FAST_REFS);
Count = Value & MAX_FAST_REFS;
/* Check how many references are left */
Count = ExGetCountFastReference(OldValue);
/* Check if the reference count is over 1 */
if (Count > 1) return Object;
@ -170,25 +150,12 @@ ObFastReferenceObject(IN PEX_FAST_REF FastRef)
/* Otherwise, reference the object 7 times */
ObReferenceObjectEx(Object, MAX_FAST_REFS);
ASSERT(!(((ULONG_PTR)Object) & MAX_FAST_REFS));
for (;;)
/* Now update the reference count */
if (!ExInsertFastReference(FastRef, Object))
{
/* Check if the current count is too high */
Value = FastRef->Value;
if (((FastRef->RefCnt + MAX_FAST_REFS) > MAX_FAST_REFS) ||
((PVOID)((ULONG_PTR)FastRef->Object &~ MAX_FAST_REFS) != Object))
{
/* Completely dereference the object */
/* We failed: completely dereference the object */
ObDereferenceObjectEx(Object, MAX_FAST_REFS);
break;
}
else
{
/* Increase the reference count */
NewValue = Value + MAX_FAST_REFS;
if (ExpChangeRundown(FastRef, NewValue, Value) == Value) break;
}
}
/* Return the Object */
@ -200,30 +167,8 @@ FASTCALL
ObFastDereferenceObject(IN PEX_FAST_REF FastRef,
IN PVOID Object)
{
ULONG_PTR Value, NewValue;
/* Sanity checks */
ASSERT(Object);
ASSERT(!(((ULONG_PTR)Object) & MAX_FAST_REFS));
/* Start dereference loop */
for (;;)
{
/* Get the current count */
Value = FastRef->Value;
if ((Value ^ (ULONG_PTR)Object) < MAX_FAST_REFS)
{
/* Decrease the reference count */
NewValue = Value + 1;
if (ExpChangeRundown(FastRef, NewValue, Value) == Value) return;
}
else
{
/* Do a normal Dereference */
ObDereferenceObject(Object);
return;
}
}
/* Release a fast reference. If this failed, use the slow path */
if (!ExReleaseFastReference(FastRef, Object)) ObDereferenceObject(Object);
}
PVOID
@ -231,36 +176,20 @@ FASTCALL
ObFastReplaceObject(IN PEX_FAST_REF FastRef,
PVOID Object)
{
ULONG_PTR NewValue;
EX_FAST_REF OldRef;
EX_FAST_REF OldValue;
PVOID OldObject;
ULONG_PTR Count;
/* Check if we were given an object and reference it 7 times */
if (Object) ObReferenceObjectEx(Object, MAX_FAST_REFS);
/* Sanity check */
ASSERT(!(((ULONG_PTR)Object) & MAX_FAST_REFS));
/* Do the swap */
OldValue = ExSwapFastReference(FastRef, Object);
OldObject = ExGetObjectFastReference(OldValue);
/* Check if the caller gave us an object */
if (Object)
{
/* He did, so bias the pointer */
NewValue = (ULONG_PTR)Object | MAX_FAST_REFS;
}
else
{
/* No object, we're clearing */
NewValue = 0;
}
/* Switch objects */
OldRef.Value = InterlockedExchange((PLONG)&FastRef->Value, NewValue);
OldObject = (PVOID)((ULONG_PTR)OldRef.Object &~ MAX_FAST_REFS);
if ((OldObject) && (OldRef.RefCnt))
{
/* Dereference the old object */
ObDereferenceObjectEx(OldObject, OldRef.RefCnt);
}
/* Check if we had an active object and dereference it */
Count = ExGetCountFastReference(OldValue);
if ((OldObject) && (Count)) ObDereferenceObjectEx(OldObject, Count);
/* Return the old object */
return OldObject;