/**************************************************************************\
* 
* Copyright (c) 1999  Microsoft Corporation
*
* Module Name:
*
*   Locking
*
* Abstract:
*
*   Lockable: A base class for lockable objects, which contains a lock.
*   Lock: Represents a held lock on an object (acquires and releases an
*         object's lock in its constructor and destructor).
*
* Revision History:
*
*   02/22/1999 davidx
*       Created it.
*   09/08/1999 agodfrey
*       Moved to Runtime\Lockable.hpp
*
\**************************************************************************/

#ifndef _LOCKABLE_HPP
#define _LOCKABLE_HPP

namespace GpRuntime 
{

//
// TODO: Remove the 'Gp' prefix from GpLockable and GpLock
//           
           
//--------------------------------------------------------------------------
// Base class for lockable API objects
//--------------------------------------------------------------------------

class GpLockable
{
    friend class GpLock;

public:

    GpLockable()
    {
        LockCount = -1;
    }

    ~GpLockable()
    {
        // Comment out to prevent Office crash.  3/14/00 -- ikkof
//        ASSERTMSG(LockCount == -1, ("~GpLock: non-zero lock count"));
    }

    LONG* GetLockCount()
    {
        return &LockCount;
    }

    BOOL IsLocked() const
    {
        return (LockCount != -1);
    }

    VOID Reset()
    {
        LockCount = -1;
    }
    
    // Copy constructor and assignment operator

    GpLockable(GpLockable & lockable)
    {
        LockCount = -1;
    }

    GpLockable &operator=(const GpLockable & lockable)
    {
        return *this;
    }

protected:

    LONG LockCount;             // number of locks, minus one
};

//--------------------------------------------------------------------------
// Class for locking API objects
// NOTE: These locks are not reentrant!
//--------------------------------------------------------------------------

class GpLock
{
public:

    GpLock(GpLockable* lockable)
    {
        // In flatapi, we sometimes need to check if an optional (NULL)
        // parameter is busy, which means we would pass NULL into this
        // constructor.  An optional parameter should not be considered locked. 
        if (lockable != NULL)
        {
            LockCount = &lockable->LockCount;

            // Note that it generates less code when we store the result
            // here than it is to convert to a BOOL here and store that.

            Result = InterlockedIncrement(LockCount);
        }
        else
        {
            Result = 0;
            LockCount = &Result;
        }
    }

    ~GpLock()
    {
        InterlockedDecrement(LockCount);
    }

    BOOL IsValid()  const
    {
        return (Result == 0);
    }

    BOOL LockFailed() const
    {
        return (Result != 0);
    }

    VOID MakePermanentLock()
    {
        // This is useful when deleting an object.  First, lock it, then 
        // leave it in a locked state.  Technically the memory will be released
        // and available for reuse, but the ObjectLock will still be left in
        // a locked state.

        LockCount = &Result;
    }

private:

    LONG Result;
    LONG *LockCount;
};

}

#endif // !_LOCKABLE_HPP