//////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 1999-2000 Microsoft Corporation
//
//  Module Name:
//      SpinLock.h
//
//  Description:
//      Spin Lock implementation.
//
//  Maintained By:
//      Geoffrey Pease (GPease) 27-NOV-1999
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

// CSpinLock
class 
CSpinLock
{
private: // Data
    LONG *  m_plLock;           // pointer to the lock
    LONG    m_lSpinCount;       // counts the number of sleeps
    LONG    m_lTimeout;         // count until acquire lock fails
#if DBG==1
    BOOL    m_fAcquired;        // DEBUG: internal state of the lock
    BOOL    m_fAcquiredOnce;    // DEBUG: lock was acquired at least once.
#endif

public: // Methods
    explicit 
        CSpinLock( LONG * plLock, LONG lTimeout ) : 
            m_plLock( plLock ),
            m_lTimeout( lTimeout )
    { 
        Assert( m_lTimeout >= 0 || m_lTimeout == INFINITE );
#if DBG==1
        m_fAcquired     = FALSE;
        m_fAcquiredOnce = FALSE;
#endif
    };

    //
    ~CSpinLock() 
    { 
#if DBG==1
        AssertMsg( m_fAcquired     == FALSE, "Lock was not released!" ); 
        AssertMsg( m_fAcquiredOnce != FALSE, "Lock was never acquired. Why was I needed?" );
#endif
    };

    //////////////////////////////////////////////////////////////////////////
    //
    // HRESULT
    // AcquireLock( void )
    //
    // Description:
    //      Acquires the spin lock. Does not return until the lock is 
    //      acquired.
    //
    // Arguments:
    //      None.
    //
    // Return Values:
    //      S_OK    - Sucess.
    //      HRESULT_FROM_WIN32( ERROR_LOCK_FAILED ) - Lock failed.
    //
    //////////////////////////////////////////////////////////////////////////
    HRESULT
        AcquireLock( void )
    {
        HRESULT hr;
        LONG l = TRUE;

#if DBG==1
        AssertMsg( m_fAcquired == FALSE, "Trying to acquire a lock that it already own. Thread is going to freeze up." );
        m_fAcquiredOnce = TRUE;
#endif

        m_lSpinCount = 0;

        for(;;)
        {
            l = InterlockedCompareExchange( m_plLock, TRUE, FALSE );
            if ( l == FALSE )
            {
                //
                // Lock acquired.
                //
                hr = S_OK;
                break;
            } // if: got lock
            else
            {
                m_lSpinCount++;
                if ( m_lSpinCount > m_lTimeout )
                {
                    AssertMsg( m_lSpinCount >= 0, "This lock doesn't seem to have been released properly." );
                    if ( m_lTimeout != INFINITE )
                    {
                        hr = THR( HRESULT_FROM_WIN32( ERROR_LOCK_FAILED ) );
                        break;

                    } // if: not infinite

                } // if: count exceeded

                //
                // Put a breakpoint here if you think that someone is double
                // locking.
                //
                Sleep( 1 );

            } // if: lock not acquired

        } // for: forever        

#if DBG==1
        m_fAcquired = TRUE;
#endif
        return hr;
    }; // AcquireLock( )

    //////////////////////////////////////////////////////////////////////////
    //
    // HRESULT
    // ReleaseLock( void )
    //
    // Description:
    //      Releases the spin lock. Return immediately.
    //
    // Arguments:
    //      None.
    //
    // Return Values:
    //      S_OK    - Success.
    //
    //////////////////////////////////////////////////////////////////////////
    HRESULT
        ReleaseLock( void )
    {
#if DBG==1
        AssertMsg( m_fAcquired == TRUE, "Releasing a lock that was not owned." );
#endif
        *m_plLock   = FALSE;
#if DBG==1
        m_fAcquired = FALSE;
#endif
        return S_OK;
    }; // ReleaseLock( )

}; // class CSpinLock