reactos/sdk/lib/drivers/wdf/shared/inc/private/common/fxobject.hpp
Victor Perevertkin 1f377076d7
[WDF] Fix KMDF so it can compile with ReactOS SDK
Not all files are included, but these are necessary to compile cdrom driver.
So far it can only be statically linked with drivers, a proper
implementation requires wdfldr helper driver
2020-11-03 00:06:27 +03:00

1506 lines
35 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Module Name:
FxObject.hpp
Abstract:
This is the C++ header for the FxObject
Author:
Revision History:
Made mode agnostic
IMPORTANT: Common code must call Initialize method of
FxObject before using it
Cannot put CreateAndInitialize method on this class as it
cannot be instantiated
--*/
#ifndef _FXOBJECT_H_
#define _FXOBJECT_H_
extern "C" {
#if defined(EVENT_TRACING)
#include "FxObject.hpp.tmh"
#endif
}
//
// Macros to pass line and file information to AddRef and Release calls.
//
// NOTE: ADDREF and RELEASE can be used by any class derived from FxObject or
// IFxMemory while the OFFSET only works with an FxObject derivation
//
#define ADDREF(_tag) AddRef(_tag, __LINE__, __FILE__)
#define RELEASE(_tag) Release(_tag, __LINE__, __FILE__)
#define ADDREF_OFFSET(_tag, offset) AddRefOverride(offset, _tag, __LINE__, __FILE__)
#define RELEASE_OFFSET(_tag, offset) ReleaseOverride(offset, _tag, __LINE__, __FILE__)
//
// This assumes that the bottom 3 bits of an FxObject are clear, ie that the
// FxObject is 8 byte aligned. The conversion from and to a handle value
// will set / clear these bits as appropriate.
//
enum FxHandleFlags {
FxHandleFlagIsOffset = 0x1,
FxHandleFlagMask = 0x7, // bottom 3 bits are free
};
//
// We cannot define FxHandleValueMask as an enumerant in FxHandleFlags because
// an enum is limited to sizeof(ULONG), which doesn't work for us on a 64 bit OS
//
extern DECLSPEC_SELECTANY const ULONG_PTR FxHandleValueMask = (~((ULONG_PTR) FxHandleFlagMask));
//
// The type itself is aligned, but the pointer is not b/c those interested in the
// offset do not need it to be aligned.
//
// The offset is aligned on an 8 byte boundary so that we have the lower 3 bits
// of the low byte to use for a bit field
//
typedef DECLSPEC_ALIGN(8) USHORT WDFOBJECT_OFFSET_ALIGNED;
typedef USHORT WDFOBJECT_OFFSET, *PWDFOBJECT_OFFSET;
struct FxQueryInterfaceParams {
//
// We intentionally do not declare a constructor for this structure.
// Instead, code should use an inline initializer list. This reduces the
// number of cycles on hot paths by removing a funciton call.
//
// FxQueryInterfaceParams(
// PVOID* Object,
// WDFTYPE Type
// ) :
// Type(Type), Object(Object), Offset(0) {if (Object != NULL) *Object = NULL; }}
//
// Object to query for
//
PVOID* Object;
//
// Type for the object to query for
//
WDFTYPE Type;
//
// Offset of handle within its owning object. If zero, the Object was the
// handle. If not zero, ((PUCHAR) Object)-Offset will yield the owning
// Object.
//
WDFOBJECT_OFFSET Offset;
};
//
// type of object being allocated. An internal object does *NOT*
// 1) have its size rounded up to an alignment value
// 2) extra size and context header appended to the allocation
//
enum FxObjectType : UINT32 {
FxObjectTypeInvalid = 0,
FxObjectTypeInternal,
FxObjectTypeExternal,
FxObjectTypeEmbedded,
};
// Ensures that a BOOL type is generated from a flag mask
#define FLAG_TO_BOOL(_Flags, _FlagMask) (!!((_Flags) & (_FlagMask)))
enum FxObjectLockState {
ObjectDoNotLock = 0,
ObjectLock = 1
};
//
// Defines for FxObject::m_ObjectFlags
//
// NOTE: if you modify (add, remove, reassign a value) this enum in any way
// you must also change FxObject::m_ObjectFlagsByName.* to match your changes!!
//
enum FXOBJECT_FLAGS {
FXOBJECT_FLAGS_PASSIVE_CALLBACKS = 0x00000001, // Object must have all callbacks at passive level
// implies passive destroy
FXOBJECT_FLAGS_NODELETEDDI = 0x00000002, // The WdfObjectDelete DDI is invalid
FXOBJECT_FLAGS_DELETECALLED = 0x00000004, // DeleteObject method called
FXOBJECT_FLAGS_COMMITTED = 0x00000008, // Commit called
FXOBJECT_FLAGS_PASSIVE_DISPOSE = 0x00000010, // Object must be Dispose()'d at passive level
FXOBJECT_FLAGS_FORCE_DISPOSE_THREAD = 0x00000020, // Object is always disposed in the dispose thread
// UNUSED = 0x00000040,
FXOBJECT_FLAGS_HAS_DEBUG = 0x00000080, // has FxObjectDebugExtension before object pointer
FXOBJECT_FLAGS_EARLY_DISPOSED_EXT = 0x00000100, // Early disposed externally to the state machine
FXOBJECT_FLAGS_TRACE_STATE = 0x00000200, // log state changes to the IFR
FXOBJECT_FLAGS_HAS_CLEANUP = 0x00000400,
FXOBJECT_FLAGS_DISPOSE_OVERRIDE = 0x00000800,
};
//
// FxObject state represents whether an object is
// tearing down, and what phase its in.
//
enum FxObjectState {
FxObjectStateInvalid = 0,
FxObjectStateCreated,
FxObjectStateDisposed,
FxObjectStateDisposingEarly,
FxObjectStateDisposingDisposeChildren,
FxObjectStateDeferedDisposing,
FxObjectStateDeferedDeleting,
FxObjectStateWaitingForEarlyDispose,
FxObjectStateWaitingForParentDeleteAndDisposed,
FxObjectStateDeletedDisposing,
FxObjectStateDeletedAndDisposed,
FxObjectStateDeferedDestroy,
FxObjectStateDestroyed,
FxObjectStateMaximum,
};
enum FxObjectDroppedEvent {
FxObjectDroppedEventAssignParentObject = 0,
FxObjectDroppedEventAddChildObjectInternal,
FxObjectDroppedEventRemoveChildObjectInternal,
FxObjectDroppedEventDeleteObject,
FxObjectDroppedEventPerformEarlyDispose,
FxObjectDroppedEventRemoveParentAssignment,
FxObjectDroppedEventParentDeleteEvent,
};
// begin_wpp config
// CUSTOM_TYPE(FxObjectState, ItemEnum(FxObjectState));
// CUSTOM_TYPE(FxObjectDroppedEvent, ItemEnum(FxObjectDroppedEvent));
// end_wpp
#define DECLARE_INTERNAL_NEW_OPERATOR() \
PVOID \
__inline \
operator new( \
__in size_t Size, \
__in PFX_DRIVER_GLOBALS FxDriverGlobals \
) \
{ \
return FxObjectHandleAlloc(FxDriverGlobals, \
NonPagedPool, \
Size, \
0, \
WDF_NO_OBJECT_ATTRIBUTES,\
0, \
FxObjectTypeInternal); \
}
struct FxObjectDebugExtension {
FxTagTracker* TagTracker;
FxVerifierLock* VerifierLock;
BYTE StateHistory[8];
LONG StateHistoryIndex;
//
// Signature lives after all the fields to avoid byte padding if it is
// first on 64 bit systems.
//
ULONG Signature;
DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) ULONG AllocationStart[1];
};
enum FxObjectDebugExtensionValues {
FxObjectDebugExtensionSize = FIELD_OFFSET(FxObjectDebugExtension, AllocationStart),
FxObjectDebugExtensionSignature = 'DOxF',
};
class UfxObject;
class FxObject {
friend UfxObject; //UMDF object wrapper
friend FxDisposeList;
friend VOID GetTriageInfo(VOID);
private:
#if FX_CORE_MODE==FX_CORE_USER_MODE
#ifndef INLINE_WRAPPER_ALLOCATION
PVOID m_COMWrapper;
#endif
#endif
WDFTYPE m_Type;
//
// This is the already computed value of
// WDF_ALIGN_SIZE_UP(sizeof(derived object), MEMORY_ALLOCATION_ALIGNMENT) +
// WDF_ALIGN_SIZE_UP(extraContext, MEMORY_ALLOCATION_ALIGNMENT)
//
USHORT m_ObjectSize;
// Reference count is operated on with interlocked operations
LONG m_Refcnt;
PFX_DRIVER_GLOBALS m_Globals;
// Flags are protected by m_SpinLock, bits defined by the enum FXOBJECT_FLAGS
union {
USHORT m_ObjectFlags;
// Each field in this struct correspond to ascending enumerant values
// in FXOBJECT_FLAGS.
//
// NOTE: you should never touch these fields in any code, they are here
// strictly for use by the debugger extension so that it doesn't
// have to result FXOBJECT_FLAGS enumerant name strings into values.
//
struct {
USHORT PassiveCallbacks : 1;
USHORT NoDeleteDDI : 1;
USHORT DeleteCalled : 1;
USHORT Committed : 1;
USHORT PassiveDispose : 1;
USHORT ForceDisposeThread : 1;
USHORT Unused : 1;
USHORT HasDebug : 1;
USHORT EarlyDisposedExt : 1;
USHORT TraceState : 1;
} m_ObjectFlagsByName;
};
//
// m_ObjectState is protected by m_SpinLock. Contains values in the
// FxObjectState enum.
//
USHORT m_ObjectState;
// List of Child Objects
LIST_ENTRY m_ChildListHead;
// SpinLock protects m_ObjectState, m_ObjectFlags, m_ChildListHead, and m_ParentObject
MxLock m_SpinLock;
//
// Parent object when this object is a child.
//
// This is protected by the child objects m_SpinLock
//
FxObject* m_ParentObject;
//
// Link for when this object is a child
//
// This is accessed by the parent of this object, and
// protected by the parents spinlock.
//
LIST_ENTRY m_ChildEntry;
//
//
//
SINGLE_LIST_ENTRY m_DisposeSingleEntry;
protected:
union {
//
// This field is set when the object is being contructed or before
// Commit() and from then on is read only.
//
// FxDeviceBase* is used by the core object state machine. Derived
// objects might need the fuller FxDevice* (and they can safely get at
// it since they know they are a part of the derived FxDevice*).
//
CfxDeviceBase* m_DeviceBase;
CfxDevice* m_Device;
};
private:
FxObject(
VOID
)
{
// Always make the caller supply a type and size
}
// Do not specify argument names
FX_DECLARE_VF_FUNCTION_P1(
VOID,
VerifyConstruct,
_In_ BOOLEAN
);
VOID
__inline
Construct(
__in BOOLEAN Embedded
)
{
m_Refcnt = 1;
m_ObjectState = FxObjectStateCreated;
m_ObjectFlags = 0;
m_ParentObject = NULL;
InitializeListHead(&m_ChildListHead);
InitializeListHead(&m_ChildEntry);
m_DisposeSingleEntry.Next = NULL;
m_DeviceBase = NULL;
VerifyConstruct(m_Globals, Embedded);
}
VOID
__inline
SetObjectStateLocked(
__in FxObjectState NewState
)
{
if (m_ObjectFlags & FXOBJECT_FLAGS_TRACE_STATE) {
DoTraceLevelMessage(
m_Globals, TRACE_LEVEL_VERBOSE, TRACINGOBJECT,
"Object %p, WDFOBJECT %p transitioning from %!FxObjectState! to "
"%!FxObjectState!", this, GetObjectHandleUnchecked(),
m_ObjectState, NewState);
if (IsDebug()) {
FxObjectDebugExtension* pExtension;
pExtension = GetDebugExtension();
pExtension->StateHistory[
InterlockedIncrement(&pExtension->StateHistoryIndex)] =
(BYTE) NewState;
}
}
m_ObjectState = (USHORT) NewState;
}
protected:
FxObject(
__in WDFTYPE Type,
__in USHORT Size,
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in FxObjectType ObjectType
);
FxObjectDebugExtension*
GetDebugExtension(
VOID
)
{
return CONTAINING_RECORD(this, FxObjectDebugExtension, AllocationStart);
}
BOOLEAN
IsDebug(
VOID
)
{
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_HAS_DEBUG);
}
static
PVOID
_GetBase(
__in FxObject* Object
)
{
if (Object->IsDebug()) {
return _GetDebugBase(Object);
}
else {
return (PVOID) Object;
}
}
VOID
AllocateTagTracker(
__in WDFTYPE Type
);
virtual
VOID
SelfDestruct(
VOID
)
{
delete this;
}
PVOID
__inline
GetObjectHandleUnchecked(
VOID
)
{
//
// We don't support offset handles in the base FxObject implementation.
// Offsets are specialized to internal objects of an FxObject
//
if (m_ObjectSize > 0) {
return _ToHandle(this);
}
else {
return NULL;
}
}
VOID
__inline
DestroyChildren(
VOID
)
{
PLIST_ENTRY ple;
FxObject *pChild;
while (!IsListEmpty(&m_ChildListHead)) {
ple = RemoveHeadList(&m_ChildListHead);
pChild = CONTAINING_RECORD(ple, FxObject, m_ChildEntry);
//
// Mark entry as unlinked
//
InitializeListHead(&pChild->m_ChildEntry);
//
// Inform child object of destruction. Object may be gone after return.
//
pChild->ParentDeleteEvent();
}
}
public:
#ifdef INLINE_WRAPPER_ALLOCATION
#if FX_CORE_MODE==FX_CORE_USER_MODE
static
USHORT
GetWrapperSize(
);
virtual
PVOID
GetCOMWrapper(
) = 0;
#else
static
USHORT
__inline
GetWrapperSize(
)
{
return 0;
}
#endif
#else
#if FX_CORE_MODE==FX_CORE_USER_MODE
PVOID GetCOMWrapper(){ return m_COMWrapper; }
void SetCOMWrapper(__drv_aliasesMem PVOID Wrapper){ m_COMWrapper = Wrapper; }
#endif
#endif
FxObject(
__in WDFTYPE Type,
__in USHORT Size,
__in PFX_DRIVER_GLOBALS FxDriverGlobals
);
virtual
~FxObject(
VOID
);
PVOID
__inline
operator new(
__in size_t Size,
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in FxObjectType Type
)
{
UNREFERENCED_PARAMETER(Type);
return FxObjectHandleAlloc(FxDriverGlobals,
NonPagedPool,
Size,
0,
WDF_NO_OBJECT_ATTRIBUTES,
0,
Type);
}
PVOID
__inline
operator new(
__in size_t Size,
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in_opt PWDF_OBJECT_ATTRIBUTES Attributes,
__in USHORT ExtraSize = 0
)
{
return FxObjectHandleAlloc(FxDriverGlobals,
NonPagedPool,
Size,
0,
Attributes,
ExtraSize,
FxObjectTypeExternal);
}
VOID
operator delete(
__in PVOID Memory
);
static
FxObject*
_FromDisposeEntry(
__in PSINGLE_LIST_ENTRY Entry
)
{
return CONTAINING_RECORD(Entry, FxObject, m_DisposeSingleEntry);
}
//
// m_ObjectSize contains the size of the object + extra size. m_ObjectSize
// was already rounded up to the correct alignment when it was contructed.
//
#define GET_CONTEXT_HEADER() WDF_PTR_ADD_OFFSET_TYPE(this, m_ObjectSize, FxContextHeader*)
VOID
SetNoContextHeader(
VOID
)
{
m_ObjectSize = 0;
}
PVOID
__inline
GetObjectHandle(
VOID
)
{
ASSERT(GetRefCnt() > 0);
return GetObjectHandleUnchecked();
}
static
FxObject*
_GetObjectFromHandle(
__in WDFOBJECT Handle,
__inout PWDFOBJECT_OFFSET ObjectOffset
)
{
ULONG_PTR handle, flags;
handle = (ULONG_PTR) Handle;
//
// store the flags and then clear them off so we have a valid value
//
flags = FxHandleFlagMask & handle;
handle &= ~FxHandleFlagMask;
//
// It is assumed the caller has already set the offset to zero
//
// *ObjectOffset = 0;
//
// We always apply the mask
//
handle = handle ^ FxHandleValueMask;
if (flags & FxHandleFlagIsOffset) {
//
// The handle is a pointer to an offset value. Return the offset
// to the caller and then compute the object since the pointer
// to the offset is part of the object we are returning.
//
*ObjectOffset = *(PWDFOBJECT_OFFSET) handle;
return (FxObject*) (((PUCHAR) handle) - *ObjectOffset);
}
else {
//
// No offset, no verification. We pass the FxObject as the handle
//
return (FxObject*) handle;
}
}
static
PVOID
__inline
_ToHandle(
__in FxObject* Object
)
{
//
// Always XOR the constant. Faster than checking
// FxDriverGlobals->FxVerifierHandle.
//
return (PVOID) (((ULONG_PTR) Object) ^ FxHandleValueMask);
}
static
VOID
__inline
_ReferenceActual(
__in WDFOBJECT Object,
__in_opt PVOID Tag,
__in LONG Line,
__in PSTR File
)
{
FxObject* pObject;
WDFOBJECT_OFFSET offset;
offset = 0;
pObject = FxObject::_GetObjectFromHandle(Object, &offset);
if (offset == 0) {
pObject->AddRef(Tag, Line, File);
}
else {
pObject->AddRefOverride(offset, Tag, Line, File);
}
}
static
VOID
__inline
_DereferenceActual(
__in WDFOBJECT Object,
__in_opt PVOID Tag,
__in LONG Line,
__in PSTR File
)
{
FxObject* pObject;
WDFOBJECT_OFFSET offset;
offset = 0;
pObject = FxObject::_GetObjectFromHandle(Object, &offset);
if (offset == 0) {
pObject->Release(Tag, Line, File);
}
else {
pObject->ReleaseOverride(offset, Tag, Line, File);
}
}
__inline
FxContextHeader*
GetContextHeader(
VOID
)
{
if (m_ObjectSize == 0) {
return NULL;
}
else {
return GET_CONTEXT_HEADER();
}
}
__inline
PFX_DRIVER_GLOBALS
GetDriverGlobals(
VOID
)
{
return m_Globals;
}
WDFTYPE
GetType(
VOID
)
{
return m_Type;
}
USHORT
GetObjectSize(
VOID
)
{
return m_ObjectSize;
}
LONG
GetRefCnt(
VOID
)
{
return m_Refcnt;
}
FxTagTracker*
GetTagTracker(
VOID
)
{
if (IsDebug()) {
return CONTAINING_RECORD(this,
FxObjectDebugExtension,
AllocationStart)->TagTracker;
}
else {
return NULL;
}
}
CfxDevice*
GetDevice(
VOID
)
{
return m_Device;
}
CfxDeviceBase*
GetDeviceBase(
VOID
)
{
return m_DeviceBase;
}
VOID
SetDeviceBase(
__in CfxDeviceBase* DeviceBase
)
{
m_DeviceBase = DeviceBase;
}
static
PVOID
_GetDebugBase(
__in FxObject* Object
)
{
return CONTAINING_RECORD(Object, FxObjectDebugExtension, AllocationStart);
}
__inline
VOID
CallCleanup(
VOID
)
{
if (m_ObjectFlags & FXOBJECT_FLAGS_HAS_CLEANUP) {
CallCleanupCallbacks();
}
}
ULONG
__inline
AddRef(
__in_opt PVOID Tag = NULL,
__in LONG Line = 0,
__in_opt PSTR File = NULL
)
{
FxTagTracker* pTagTracker;
ULONG c;
c = InterlockedIncrement(&m_Refcnt);
//
// Catch the transition from 0 to 1. Since the REF_OBJ starts off at 1,
// we should never have to increment to get to this value.
//
ASSERT(c > 1);
pTagTracker = GetTagTracker();
if (pTagTracker != NULL) {
pTagTracker->UpdateTagHistory(Tag, Line, File, TagAddRef, c);
}
return c;
}
virtual
ULONG
Release(
__in_opt PVOID Tag = NULL,
__in LONG Line = 0,
__in_opt PSTR File = NULL
)
{
FxTagTracker* pTagTracker;
ULONG c;
pTagTracker = GetTagTracker();
if (pTagTracker != NULL) {
pTagTracker->UpdateTagHistory(Tag, Line, File, TagRelease, m_Refcnt - 1);
}
c = InterlockedDecrement(&m_Refcnt);
if (c == 0) {
FinalRelease();
}
return c;
}
virtual
ULONG
AddRefOverride(
__in WDFOBJECT_OFFSET Offset,
__in_opt PVOID Tag = NULL,
__in LONG Line = 0,
__in_opt PSTR File = NULL
)
{
UNREFERENCED_PARAMETER(Offset);
return AddRef(Tag, Line, File);
}
virtual
ULONG
ReleaseOverride(
__in WDFOBJECT_OFFSET Offset,
__in_opt PVOID Tag = NULL,
__in LONG Line = 0,
__in_opt PSTR File = NULL
)
{
UNREFERENCED_PARAMETER(Offset);
return Release(Tag, Line, File);
}
_Must_inspect_result_
virtual
NTSTATUS
QueryInterface(
__in FxQueryInterfaceParams* Params
);
VOID
MarkTraceState(
VOID
)
{
m_ObjectFlags |= FXOBJECT_FLAGS_TRACE_STATE;
}
BOOLEAN
__inline
IsTraceState(
VOID
)
{
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_TRACE_STATE);
}
VOID
__inline
TraceDroppedEvent(
__in FxObjectDroppedEvent Event
)
{
if (IsTraceState()) {
DoTraceLevelMessage(
m_Globals, TRACE_LEVEL_INFORMATION, TRACINGOBJECT,
"Object %p, WDFOBJECT %p, state %!FxObjectState! dropping event"
" %!FxObjectDroppedEvent!",
this, GetObjectHandleUnchecked(), m_ObjectState, Event);
}
}
VOID
MarkPassiveDispose(
__in FxObjectLockState State = ObjectLock
)
{
//
// Object which can have > passive level locks, but needs to be Dispose()'d
// at passive level. This means that the object's client cleanup
// routines will also be guaranteed to run at passive.
//
if (State == ObjectLock) {
KIRQL oldIrql;
m_SpinLock.Acquire(&oldIrql);
m_ObjectFlags |= FXOBJECT_FLAGS_PASSIVE_DISPOSE;
m_SpinLock.Release(oldIrql);
}
else {
m_ObjectFlags |= FXOBJECT_FLAGS_PASSIVE_DISPOSE;
}
}
//
// Sets that the object is a passive level only object who
// accesses page-able pool, routines, or has a driver
// specified passive constraint on callbacks such as
// Cleanup and Destroy.
//
VOID
MarkPassiveCallbacks(
__in FxObjectLockState State = ObjectLock
)
{
if (State == ObjectLock) {
KIRQL oldIrql;
m_SpinLock.Acquire(&oldIrql);
m_ObjectFlags |= FXOBJECT_FLAGS_PASSIVE_CALLBACKS | FXOBJECT_FLAGS_PASSIVE_DISPOSE;
m_SpinLock.Release(oldIrql);
}
else {
m_ObjectFlags |= FXOBJECT_FLAGS_PASSIVE_CALLBACKS | FXOBJECT_FLAGS_PASSIVE_DISPOSE;
}
}
VOID
MarkForceDisposeThread(
__in FxObjectLockState State = ObjectLock
)
{
//
// Object must always be disposed in a separate thread
// to allow waiting for some outstanding async
// operation to complete.
//
if (State == ObjectLock) {
KIRQL oldIrql;
m_SpinLock.Acquire(&oldIrql);
m_ObjectFlags |= FXOBJECT_FLAGS_FORCE_DISPOSE_THREAD;
m_SpinLock.Release(oldIrql);
}
else {
m_ObjectFlags |= FXOBJECT_FLAGS_FORCE_DISPOSE_THREAD;
}
}
BOOLEAN
IsPassiveCallbacks(
__in BOOLEAN AcquireLock = TRUE
)
{
BOOLEAN result;
KIRQL oldIrql = PASSIVE_LEVEL;
if (AcquireLock) {
m_SpinLock.Acquire(&oldIrql);
}
result = IsPassiveCallbacksLocked();
if (AcquireLock) {
m_SpinLock.Release(oldIrql);
}
return result;
}
BOOLEAN
IsPassiveDispose(
__in BOOLEAN AcquireLock = TRUE
)
{
BOOLEAN result;
KIRQL oldIrql = PASSIVE_LEVEL;
if (AcquireLock) {
m_SpinLock.Acquire(&oldIrql);
}
result = IsPassiveDisposeLocked();
if (AcquireLock) {
m_SpinLock.Release(oldIrql);
}
return result;
}
BOOLEAN
IsForceDisposeThread(
__in BOOLEAN AcquireLock = TRUE
)
{
BOOLEAN result;
KIRQL oldIrql = PASSIVE_LEVEL;
if (AcquireLock) {
m_SpinLock.Acquire(&oldIrql);
}
result = IsForceDisposeThreadLocked();
if (AcquireLock) {
m_SpinLock.Release(oldIrql);
}
return result;
}
VOID
MarkCommitted(
VOID
)
{
//
// Since no client code is accessing the handle yet, we don't need to
// acquire the object state lock to set the flag since this will be
// the only thread touching m_ObjectFlags.
//
m_ObjectFlags |= FXOBJECT_FLAGS_COMMITTED;
}
BOOLEAN
IsCommitted(
VOID
)
{
//
// No need to acquire the lock because it is assumed the caller is
// calling on an object whose ref count has gone to zero so there are
// no other callers to contend with who might set this flag or modify
// m_ObjectFlags.
//
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_COMMITTED);
}
VOID
MarkDisposeOverride(
__in FxObjectLockState State = ObjectLock
)
{
if (State == ObjectLock) {
KIRQL irql;
m_SpinLock.Acquire(&irql);
m_ObjectFlags |= FXOBJECT_FLAGS_DISPOSE_OVERRIDE;
m_SpinLock.Release(irql);
}
else {
m_ObjectFlags |= FXOBJECT_FLAGS_DISPOSE_OVERRIDE;
}
}
VOID
MarkNoDeleteDDI(
__in FxObjectLockState State = ObjectLock
)
{
if (State == ObjectLock) {
KIRQL irql;
m_SpinLock.Acquire(&irql);
m_ObjectFlags |= FXOBJECT_FLAGS_NODELETEDDI;
m_SpinLock.Release(irql);
}
else {
m_ObjectFlags |= FXOBJECT_FLAGS_NODELETEDDI;
}
}
BOOLEAN
IsNoDeleteDDI(
VOID
)
{
// No need for lock since its only set in constructor/init
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_NODELETEDDI);
}
//
// Commit the WDF object before returning handle to the caller.
//
_Must_inspect_result_
NTSTATUS
Commit(
__in_opt PWDF_OBJECT_ATTRIBUTES Attributes,
__out_opt WDFOBJECT* ObjectHandle,
__in_opt FxObject* Parent = NULL,
__in BOOLEAN AssignDriverAsDefaultParent = TRUE
);
VOID
DeleteFromFailedCreate(
VOID
);
VOID
ClearEvtCallbacks(
VOID
);
BOOLEAN
EarlyDispose(
VOID
);
//
// Request that an object be deleted.
//
// This can be the result of a WDF API or a WDM event.
//
virtual
VOID
DeleteObject(
VOID
);
//
// Invoked by FxObject *once* when the object is either
// being deleted, or rundown due to its parent object
// being deleted.
//
// An object can override this to perform per object
// cleanup if required.
//
// TRUE means that the cleanup callbacks should be called when the function
// returns. FALSE indicates that the caller will take care of calling the
// cleanup callbacks on behalf of the state machine.
//
virtual
BOOLEAN
Dispose(
VOID
);
//
// Request to make ParentObject the parent for this object.
//
_Must_inspect_result_
NTSTATUS
AssignParentObject(
__in FxObject* ParentObject
);
_Must_inspect_result_
NTSTATUS
AddContext(
__in FxContextHeader *Header,
__in PVOID* Context,
__in PWDF_OBJECT_ATTRIBUTES Attributes
);
//
// Request that this Object be removed from the child association
// list for its parent
//
_Must_inspect_result_
NTSTATUS
RemoveParentAssignment(
VOID
);
//
// Adds a reference to the parent object pointer if != NULL
//
_Must_inspect_result_
FxObject*
GetParentObjectReferenced(
__in PVOID Tag
);
//
// This is public to allow debug code to assert that
// an object has been properly disposed either through
// calling DeleteObject, or being disposed by its parent.
//
BOOLEAN
IsDisposed(
VOID
)
{
KIRQL oldIrql;
BOOLEAN disposed;
if (m_Globals->FxVerifierOn &&
m_Globals->FxVerifierHandle) {
m_SpinLock.Acquire(&oldIrql);
if (m_ObjectState == FxObjectStateCreated) {
disposed = FALSE;
}
else {
//
// Parent is disposing us, or we are being disposed
//
disposed = TRUE;
}
m_SpinLock.Release(oldIrql);
return disposed;
}
else {
return TRUE;
}
}
static
PFX_POOL_HEADER
_CleanupPointer(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in FxObject* Object
)
{
PFX_POOL_HEADER pHeader;
PVOID pObjectBase;
pObjectBase = _GetBase(Object);
pHeader = CONTAINING_RECORD(pObjectBase, FX_POOL_HEADER, AllocationStart);
//
// If PoolTracker is on then do....
//
if (FxDriverGlobals->IsPoolTrackingOn()) {
//
// Decommission this NonPaged Allocation tracker
//
FxPoolRemoveNonPagedAllocateTracker((PFX_POOL_TRACKER) pHeader->Base);
}
return pHeader;
}
_Must_inspect_result_
static
NTSTATUS
_GetEffectiveLock(
__in FxObject* Object,
__in_opt IFxHasCallbacks* Callbacks,
__in BOOLEAN AutomaticLocking,
__in BOOLEAN PassiveCallbacks,
__out FxCallbackLock** CallbackLock,
__out_opt FxObject** CallbackLockObject
);
//
// Implementation for WdfObjectQuery
//
_Must_inspect_result_
static
NTSTATUS
_ObjectQuery(
_In_ FxObject* Object,
_In_ CONST GUID* Guid,
_In_ ULONG QueryBufferLength,
_Out_writes_bytes_(QueryBufferLength)
PVOID QueryBuffer
);
protected:
VOID
DeleteEarlyDisposedObject(
VOID
);
private:
VOID
FinalRelease(
VOID
);
//
// This is used by verifier to ensure that DeleteObject
// is only called once.
//
// It must be accessed under the m_SpinLock.
//
// It returns TRUE if this is the first call.
//
BOOLEAN
MarkDeleteCalledLocked(
VOID
)
{
BOOLEAN retval;
retval = !(m_ObjectFlags & FXOBJECT_FLAGS_DELETECALLED);
m_ObjectFlags |= FXOBJECT_FLAGS_DELETECALLED;
return retval;
}
BOOLEAN
IsPassiveCallbacksLocked(
VOID
)
{
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_PASSIVE_CALLBACKS);
}
BOOLEAN
IsPassiveDisposeLocked(
VOID
)
{
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_PASSIVE_DISPOSE);
}
BOOLEAN
IsForceDisposeThreadLocked(
VOID
)
{
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_FORCE_DISPOSE_THREAD);
}
BOOLEAN
ShouldDeferDisposeLocked(
__out_opt PKIRQL PreviousIrql = NULL
)
{
if (IsForceDisposeThreadLocked()) {
return TRUE;
}
else if (IsPassiveDisposeLocked()) {
//
// Only call KeGetCurrentIrql() if absolutely necessary. It is an
// expensive call and we want to minimize the cycles required when
// destroying an object that requires passive rundown
//
//
// Cases:
// 1) Caller does not know the current IRQL, so we must query for it
//
// 2) Caller knew prev IRQL, so we used the caller's value
//
if (PreviousIrql == NULL) { // case 1
if (Mx::MxGetCurrentIrql() != PASSIVE_LEVEL) {
return TRUE;
}
}
else if (*PreviousIrql != PASSIVE_LEVEL) { // case 2
return TRUE;
}
}
return FALSE;
}
VOID
ParentDeleteEvent(
VOID
);
BOOLEAN
PerformEarlyDispose(
VOID
);
_Releases_lock_(this->m_SpinLock.m_Lock)
__drv_requiresIRQL(DISPATCH_LEVEL)
BOOLEAN
PerformEarlyDisposeWorkerAndUnlock(
__in __drv_restoresIRQL KIRQL OldIrql,
__in BOOLEAN CanDefer
);
_Releases_lock_(this->m_SpinLock.m_Lock)
__drv_requiresIRQL(DISPATCH_LEVEL)
BOOLEAN
PerformDisposingDisposeChildrenLocked(
__in __drv_restoresIRQL KIRQL OldIrql,
__in BOOLEAN CanDefer
);
_Releases_lock_(this->m_SpinLock.m_Lock)
__drv_requiresIRQL(DISPATCH_LEVEL)
BOOLEAN
DeleteWorkerAndUnlock(
__in __drv_restoresIRQL KIRQL OldIrql,
__in BOOLEAN CanDefer
);
VOID
QueueDeferredDisposeLocked(
__in FxObjectState NewDeferedState
);
VOID
DeferredDisposeWorkItem(
VOID
);
_Releases_lock_(this->m_SpinLock.m_Lock)
__drv_requiresIRQL(DISPATCH_LEVEL)
BOOLEAN
DisposeChildrenWorker(
__in FxObjectState NewDeferedState,
__in __drv_restoresIRQL KIRQL OldIrql,
__in BOOLEAN CanDefer
);
_When_(Unlock, _Releases_lock_(this->m_SpinLock.m_Lock))
__drv_when(Unlock, __drv_requiresIRQL(DISPATCH_LEVEL))
VOID
DeletedAndDisposedWorkerLocked(
__in __drv_when(Unlock, __drv_restoresIRQL) KIRQL OldIrql,
__in BOOLEAN Unlock = TRUE
);
_Must_inspect_result_
NTSTATUS
RemoveParentAssociation(
VOID
);
_Must_inspect_result_
NTSTATUS
AddChildObjectInternal(
__in FxObject* ChildObject
);
_Must_inspect_result_
NTSTATUS
RemoveChildObjectInternal(
__in FxObject* ChildObject
);
VOID
ProcessDestroy(
VOID
);
VOID
CallCleanupCallbacks(
VOID
);
};
#endif // _FXOBJECT_H_