/*++ 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_