/*++

Copyright (c) Microsoft Corporation

Module Name:

    FxSpinLock.cpp

Abstract:

    This module implements FxSpinLock

Author:



Environment:

   Both kernel and user mode

Revision History:

--*/

#include "fxsupportpch.hpp"
#include "fxspinlock.hpp"

extern "C" {
// #include "FxSpinLock.tmh"
}

FxSpinLock::FxSpinLock(
    __in PFX_DRIVER_GLOBALS FxDriverGlobals,
    __in USHORT ExtraSize
    ) :
    FxObject(FX_TYPE_SPIN_LOCK, COMPUTE_OBJECT_SIZE(sizeof(FxSpinLock), ExtraSize), FxDriverGlobals)
{
    FX_SPIN_LOCK_HISTORY* pHistory;

    m_Irql = 0;
    m_InterruptLock = FALSE;

    pHistory = GetHistory();

    if (pHistory != NULL) {
        RtlZeroMemory(pHistory, sizeof(FX_SPIN_LOCK_HISTORY));

        pHistory->CurrentHistory = &pHistory->History[0];
    }
}


__drv_raisesIRQL(DISPATCH_LEVEL)
__drv_maxIRQL(DISPATCH_LEVEL)
VOID
FxSpinLock::AcquireLock(
    __in PVOID CallersAddress
    )
{
    PFX_SPIN_LOCK_HISTORY pHistory;
    KIRQL irql;

    m_SpinLock.Acquire(&irql);

    m_Irql = irql;

    pHistory = GetHistory();

    if (pHistory != NULL) {
        PFX_SPIN_LOCK_HISTORY_ENTRY pCur;

        //
        // This assert should never fire here, but this helps track ownership
        // in the case of a release without an acquire.
        //
        ASSERT(pHistory->OwningThread == NULL);
        pHistory->OwningThread = Mx::MxGetCurrentThread();

        pCur = pHistory->CurrentHistory;

        Mx::MxQueryTickCount(&pCur->AcquiredAtTime);
        pCur->CallersAddress = CallersAddress;
    }
}

__drv_requiresIRQL(DISPATCH_LEVEL)
VOID
FxSpinLock::ReleaseLock(
    VOID
    )
{
    PFX_SPIN_LOCK_HISTORY pHistory;

    pHistory = GetHistory();

    if (pHistory != NULL) {
        LARGE_INTEGER now;
        PFX_SPIN_LOCK_HISTORY_ENTRY pCur;

        if (pHistory->OwningThread != Mx::MxGetCurrentThread()) {
            if (pHistory->OwningThread == NULL) {
                DoTraceLevelMessage(
                    GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGERROR,
                    "WDFSPINLOCK %p being released by thread 0x%p, but was "
                    "never acquired!", GetObjectHandle(), Mx::MxGetCurrentThread());
            }
            else {
                DoTraceLevelMessage(
                    GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGERROR,
                    "WDFSPINLOCK 0x%p not owned by thread 0x%p, owned by thread 0x%p",
                    GetObjectHandle(), Mx::MxGetCurrentThread(),
                    pHistory->OwningThread);
            }

            FxVerifierBugCheck(GetDriverGlobals(),
                               WDF_INVALID_LOCK_OPERATION,
                               (ULONG_PTR) GetObjectHandle(),
                               0x1);
            //
            // Will not get here
            //
            return;
        }

        ASSERT(pHistory->OwningThread != NULL);

        Mx::MxQueryTickCount(&now);

        pCur = pHistory->CurrentHistory;
        pCur->LockedDuraction = now.QuadPart - pCur->AcquiredAtTime.QuadPart;

        pHistory->CurrentHistory++;
        if (pHistory->CurrentHistory >=
            pHistory->History + FX_SPIN_LOCK_NUM_HISTORY_ENTRIES) {
            pHistory->CurrentHistory = pHistory->History;
        }

        pHistory->OwningThread = NULL;
    }

    m_SpinLock.Release(m_Irql);
}