windows-nt/Source/XPSP1/NT/base/ntos/ob/fastref.c
2020-09-26 16:20:57 +08:00

305 lines
7.3 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
rundown.c
Abstract:
This module houses routines that do fast referencing of object manager
objects. This is just a thin layer around the fast ref package in EX.
The EX routines are all inline so their description is here.
The basic principle of these routines is to allow fast referencing of
objects held in pointers protected by locks. This is done by assuming
the pointer to an object is aligned on a 8 byte boundary and using the
bottom 3 bits of the pointer as a fast referencing mechanism. The
assumption of this algorithm is that the pointer changes far less
frequently than it is referenced.
Given the following bit definition of a
pointer:
+-----------+---+
| p | n |
+-----------+---+
p << 3 : Object pointer. Bottom three bits are zero. p may be null in
which case n must be zero
n : Total number of pre-references unused
For a non-null p the total number of references on the target object
associated with this structure is >= 1 + 7 - n. There is an associated
reference for the pointer itself and one for each of the possible
extra references.
Fast references proceed to perform one of the following transformation:
+-----------+---+ +-----------+-----+
| p | n | => | p | n-1 | n > 0, p != NULL
+-----------+---+ +-----------+-----+
+-----------+---+ +-----------+---+
| NULL | 0 | => | NULL | 0 | NULL pointers are never fast refed
+-----------+---+ +-----------+---+ and never have cached references
Slow references do the following transformation:
+-----------+---+ +-----------+-----+
| p | 0 | => | p | 7 | An addition 8 references are
+-----------+---+ +-----------+-----+ added to the object
The second transformation is either done under a lock or done by the
thread that does the transition with n = 1 => n = 0.
The reference obtained by this fast algorithm may be released by
dereferencing the target object directly or by attempting to return the
reference to the pointer. Returning the reference to the pointer has
the following transformations:
+-----------+---+ +-----------+-----+
| p | n | => | p | n+1 | n < 7, p != NULL
+-----------+---+ +-----------+-----+
+-----------+---+ +-----------+-----+
| p | 7 | => | p | 0 | Dereference the object directly
+-----------+---+ +-----------+-----+
+-----------+---+ +-----------+-----+ Dereference the object directly
| q | n | => | q | n | as the pointer p has been
+-----------+---+ +-----------+-----+ replaced by q. q May be NULL
Author:
Neill Clift (NeillC) 29-Jul-2000
Revision History:
--*/
#include "obp.h"
#pragma hdrstop
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,ObInitializeFastReference)
#pragma alloc_text(PAGE,ObFastReferenceObject)
#pragma alloc_text(PAGE,ObFastReferenceObjectLocked)
#pragma alloc_text(PAGE,ObFastDereferenceObject)
#pragma alloc_text(PAGE,ObFastReplaceObject)
#endif
NTKERNELAPI
VOID
FASTCALL
ObInitializeFastReference (
IN PEX_FAST_REF FastRef,
IN PVOID Object
)
/*++
Routine Description:
Initialize a fast reference structure.
Arguments:
FastRef - Rundown block to be initialized
Return Value:
None
--*/
{
//
// If an object was given then bias the object reference by the cache size.
//
if (Object != NULL) {
ObReferenceObjectEx (Object, ExFastRefGetAdditionalReferenceCount ());
}
ExFastRefInitialize (FastRef, Object);
}
NTKERNELAPI
PVOID
FASTCALL
ObFastReferenceObject (
IN PEX_FAST_REF FastRef
)
/*++
Routine Description:
This routine attempts a fast reference of an object in a fast ref
structure.
Arguments:
FastRef - Rundown block to be used for the reference
Return Value:
PVOID - Object that was referenced or NULL if we failed
--*/
{
EX_FAST_REF OldRef;
PVOID Object;
ULONG RefsToAdd, Unused;
//
// Attempt the fast reference
//
OldRef = ExFastReference (FastRef);
Object = ExFastRefGetObject (OldRef);
//
// We fail if there wasn't an object or if it has no cached references
// left. Both of these cases had the cached reference count zero.
//
Unused = ExFastRefGetUnusedReferences (OldRef);
if (Unused <= 1) {
if (Unused == 0) {
return NULL;
}
//
// If we took the counter to zero then attempt to make life easier for
// the next referencer by resetting the counter to its max. Since we now
// have a reference to the object we can do this.
//
RefsToAdd = ExFastRefGetAdditionalReferenceCount ();
ObReferenceObjectEx (Object, RefsToAdd);
//
// Try to add the added references to the cache. If we fail then just
// release them.
//
if (!ExFastRefAddAdditionalReferenceCounts (FastRef, Object, RefsToAdd)) {
ObDereferenceObjectEx (Object, RefsToAdd);
}
}
return Object;
}
NTKERNELAPI
PVOID
FASTCALL
ObFastReferenceObjectLocked (
IN PEX_FAST_REF FastRef
)
/*++
Routine Description:
This routine does a slow object reference. This must be called while
holding a lock.
Arguments:
FastRef - Rundown block to be used to reference the object
Return Value:
PVOID - Object that was referenced or NULL if there was no object.
--*/
{
PVOID Object;
EX_FAST_REF OldRef;
OldRef = *FastRef;
Object = ExFastRefGetObject (OldRef);
if (Object != NULL) {
ObReferenceObject (Object);
}
return Object;
}
NTKERNELAPI
VOID
FASTCALL
ObFastDereferenceObject (
IN PEX_FAST_REF FastRef,
IN PVOID Object
)
/*++
Routine Description:
This routine does a fast dereference if possible.
Arguments:
FastRef - Rundown block to be used to dereference the object
Return Value:
None.
--*/
{
if (!ExFastRefDereference (FastRef, Object)) {
//
// If the object changed or there is no space left in the reference
// cache then just deref the object.
//
ObDereferenceObject (Object);
}
}
NTKERNELAPI
PVOID
FASTCALL
ObFastReplaceObject (
IN PEX_FAST_REF FastRef,
IN PVOID Object
)
/*++
Routine Description:
This routine does a swap of the object. This must be called while holding
a lock.
Arguments:
FastRef - Rundown block to be used to do the swap.
Return Value:
PVOID - Object that was in the block before the swap..
--*/
{
EX_FAST_REF OldRef;
PVOID OldObject;
ULONG RefsToReturn;
//
// If we have been given an object then bias it by the correct amount.
//
if (Object != NULL) {
ObReferenceObjectEx (Object, ExFastRefGetAdditionalReferenceCount ());
}
//
// Do the swap
//
OldRef = ExFastRefSwapObject (FastRef, Object);
OldObject = ExFastRefGetObject (OldRef);
//
// If there was an original object then we need to work out how many
// cached references there were (if any) and return them.
//
if (OldObject != NULL) {
RefsToReturn = ExFastRefGetUnusedReferences (OldRef);
if (RefsToReturn > 0) {
ObDereferenceObjectEx (OldObject, RefsToReturn);
}
}
return OldObject;
}