/*++

Copyright (c) Microsoft Corporation

Module Name:

    FxNonPagedObject.hpp

Abstract:

    This module defines the abstract FxNonPagedObject class.

Author:



Environment:

    Both kernel and user mode

Revision History:


        Made mode agnostic

        IMPORTANT: Common code must call Initialize method of
        FxNonPagedObject before using it

        Cannot put CreateAndInitialize method on this class as it
        cannot be instantiated

--*/

#ifndef _FXNONPAGEDOBJECT_H_
#define _FXNONPAGEDOBJECT_H_

extern "C" {

#if defined(EVENT_TRACING)
#include "FxNonPagedObject.hpp.tmh"
#endif

}

class FxNonPagedObject : public FxObject
{
private:

    MxLock  m_NPLock;

public:
    FxNonPagedObject(
        __in WDFTYPE Type,
        __in USHORT Size,
        __in PFX_DRIVER_GLOBALS FxDriverGlobals
        ) :
        FxObject(Type, Size, FxDriverGlobals)
    {
        if (IsDebug()) {
            if (GetDriverGlobals()->FxVerifierLock) {
                //
                // VerifierLock CreateAndInitialize failure is not fatal,
                // we just won't track anything
                //
                FxVerifierLock * verifierLock = NULL;
                (void) FxVerifierLock::CreateAndInitialize(&verifierLock,
                                                    GetDriverGlobals(),
                                                    this);
                GetDebugExtension()->VerifierLock = verifierLock;
            }
        }
    }

    FxNonPagedObject(
        __in WDFTYPE Type,
        __in USHORT Size,
        __in PFX_DRIVER_GLOBALS FxDriverGlobals,
        __in FxObjectType ObjectType
        ) :
        FxObject(Type, Size, FxDriverGlobals, ObjectType)
    {
        if (IsDebug()) {
            if (GetDriverGlobals()->FxVerifierLock) {
                //
                // VerifierLock CreateAndInitialize failure is not fatal,
                // we just won't track anything
                //
                FxVerifierLock * verifierLock = NULL;
                (void) FxVerifierLock::CreateAndInitialize(&verifierLock,
                                                    GetDriverGlobals(),
                                                    this);
                GetDebugExtension()->VerifierLock = verifierLock;
            }
        }
    }

    virtual
    ~FxNonPagedObject(
        VOID
        )
    {
        if (IsDebug()) {
            FxObjectDebugExtension* pExtension;

            pExtension = GetDebugExtension();

            if (pExtension->VerifierLock != NULL) {
                delete pExtension->VerifierLock;
                pExtension->VerifierLock = NULL;
            }
        }
    }

    _Acquires_lock_(this->m_NPLock.m_Lock)
    __drv_maxIRQL(DISPATCH_LEVEL)
    __drv_setsIRQL(DISPATCH_LEVEL)
    VOID
    Lock(
        __out __drv_deref(__drv_savesIRQL) PKIRQL PreviousIrql
        )
    {
        if (IsDebug()) {
            FxObjectDebugExtension* pExtension;

            pExtension = GetDebugExtension();

            if (pExtension->VerifierLock != NULL) {
                pExtension->VerifierLock->Lock(PreviousIrql, FALSE);
                //
                // return here so that we don't acquire the non verified lock
                // below
                //
                return;
            }
        }

        m_NPLock.Acquire(PreviousIrql);
    }

    _Releases_lock_(this->m_NPLock.m_Lock)
    __drv_requiresIRQL(DISPATCH_LEVEL)
    __inline
    VOID
    Unlock(
        __in __drv_restoresIRQL KIRQL PreviousIrql
        )
    {
        if (IsDebug()) {
            FxObjectDebugExtension* pExtension;

            pExtension = GetDebugExtension();

            if (pExtension->VerifierLock != NULL) {
                pExtension->VerifierLock->Unlock(PreviousIrql, FALSE);

                //
                // return here so that we don't release the non verified lock
                // below
                //
                return;
            }
        }

        m_NPLock.Release(PreviousIrql);
    }

#if FX_CORE_MODE==FX_CORE_KERNEL_MODE

    _Acquires_lock_(this->m_NPLock.m_Lock)
    __drv_requiresIRQL(DISPATCH_LEVEL)
    VOID
    LockAtDispatch(
        VOID
        )
    {
        if (IsDebug()) {
            FxObjectDebugExtension* pExtension;

            pExtension = GetDebugExtension();

            if (pExtension->VerifierLock != NULL) {
                KIRQL previousIrql;

                pExtension->VerifierLock->Lock(&previousIrql, TRUE);

                ASSERT(previousIrql == DISPATCH_LEVEL);
                //
                // return here so that we don't acquire the non verified lock
                // below
                //
                return;
            }
        }

        m_NPLock.AcquireAtDpcLevel();
    }

    _Requires_lock_held_(this->m_NPLock.m_Lock)
    _Releases_lock_(this->m_NPLock.m_Lock)
    __drv_requiresIRQL(DISPATCH_LEVEL)
    __inline
    VOID
    UnlockFromDispatch(
        VOID
        )
    {
        if (IsDebug()) {
            FxObjectDebugExtension* pExtension;

            pExtension = GetDebugExtension();

            if (pExtension->VerifierLock != NULL) {
                pExtension->VerifierLock->Unlock(DISPATCH_LEVEL, TRUE);

                //
                // return here so that we don't acquire the non verified lock
                // below
                //
                return;
            }
        }

        m_NPLock.ReleaseFromDpcLevel();
    }

#endif   //FX_CORE_MODE==FX_CORE_KERNEL_MODE
};

#endif // _FXNONPAGEDOBJECT_H_