/*****************************Module*Header*******************************\
* Module Name: local.c                                                     *
*                                                                          *
* Support routines for client side objects.                                *
*                                                                          *
* Created: 30-May-1991 21:55:57                                            *
* Author: Charles Whitmer [chuckwh]                                        *
*                                                                          *
* Copyright (c) 1991,1993 Microsoft Corporation                            *
\**************************************************************************/

#include "precomp.h"
#pragma hdrstop

#include <ntcsrdll.h>

LHE             *pLocalTable;              // Points to handle table.
ULONG            iFreeLhe = INVALID_INDEX; // Identifies a free handle index.
ULONG            cLheCommitted = 0;        // Count of LHEs with committed RAM.
CRITICAL_SECTION semLocal;                 // Semaphore for handle allocation.


//XXX Useless but needed by csrgdi.h

#if DBG
ULONG gcHits  = 0;
ULONG gcBatch = 0;
ULONG gcCache = 0;
ULONG gcUser  = 0;
#endif

/******************************Private*Routine*****************************\
* bMakeMoreHandles ()                                                      *
*                                                                          *
* Commits more RAM to the local handle table and links the new free        *
* handles together.  Returns TRUE on success, FALSE on error.              *
*                                                                          *
* History:                                                                 *
*  Sat 01-Jun-1991 17:06:45 -by- Charles Whitmer [chuckwh]                 *
* Wrote it.                                                                *
\**************************************************************************/

BOOL bMakeMoreHandles()
{
    UINT ii;

// Commit more RAM for the handle table.

    if (
        (cLheCommitted >= MAX_HANDLES) ||
        (VirtualAlloc(
            (LPVOID) &pLocalTable[cLheCommitted],
            COMMIT_COUNT * sizeof(LHE),
            MEM_COMMIT,
            PAGE_READWRITE
            ) == (LPVOID) NULL)
       )
    {
        WARNING("bMakeMoreHandles(): failed to commit more memory\n");
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return(FALSE);
    }

// Initialize the new handles.

    ii = iFreeLhe = cLheCommitted;
    cLheCommitted += COMMIT_COUNT;

    for (; ii<cLheCommitted; ii++)
    {
        pLocalTable[ii].metalink = ii+1;
        pLocalTable[ii].iUniq    = 1;
        pLocalTable[ii].iType    = LO_NULL;
    }
    pLocalTable[ii-1].metalink = INVALID_INDEX;

    return(TRUE);
}

/******************************Public*Routine******************************\
* iAllocHandle (iType,hgre,pv)                                             *
*                                                                          *
* Allocates a handle from the local handle table, initializes fields in    *
* the handle entry.  Returns the handle index or INVALID_INDEX on error.   *
*                                                                          *
* History:                                                                 *
*  Sat 01-Jun-1991 17:08:54 -by- Charles Whitmer [chuckwh]                 *
* Wrote it.                                                                *
\**************************************************************************/

ULONG iAllocHandle(ULONG iType,ULONG hgre,PVOID pv)
{
    ULONG ii = INVALID_INDEX;
    PLHE  plhe;

// Get critical for handle allocation.

    ENTERCRITICALSECTION(&semLocal);

// Make sure a handle is available.

    if (iFreeLhe != INVALID_INDEX || bMakeMoreHandles())
    {
        ii = iFreeLhe;
        plhe = pLocalTable + ii;
        iFreeLhe = plhe->metalink;
        plhe->hgre     = hgre;
        plhe->cRef     = 0;
        plhe->iType    = (BYTE) iType;
        plhe->pv       = pv;
        plhe->metalink = 0;
        plhe->tidOwner = 0;
        plhe->cLock    = 0;
    }

// Leave the critical section.

    LEAVECRITICALSECTION(&semLocal);
    return(ii);
}

/******************************Public*Routine******************************\
* vFreeHandle (h)                                                          *
*                                                                          *
* Frees up a local handle.  The handle is added to the free list.  This    *
* may be called with either an index or handle.  The iUniq count is        *
* updated so the next user of this handle slot will have a different       *
* handle.                                                                  *
*                                                                          *
* Note for client side implementation:                                     *
*   Caller should have handle locked before calling this.                  *
*                                                                          *
* History:                                                                 *
*  Sat 01-Jun-1991 17:11:23 -by- Charles Whitmer [chuckwh]                 *
* Wrote it.                                                                *
\**************************************************************************/

VOID vFreeHandle(ULONG_PTR h)
{
// Extract the index from the handle.

    UINT ii = MASKINDEX(h);

// Get critical for handle deallocation.

    ENTERCRITICALSECTION(&semLocal);

// Caller should lock handle before freeing.

    ASSERTOPENGL(pLocalTable[ii].cLock == 1,
                 "vFreeHandle(): cLock != 1\n");
    ASSERTOPENGL(pLocalTable[ii].tidOwner == GetCurrentThreadId(),
                 "vFreeHandle(): thread not owner\n");

// Add the handle to the free list.

    pLocalTable[ii].metalink = iFreeLhe;
    iFreeLhe = ii;

// Increment the iUniq count.

    pLocalTable[ii].iUniq++;
    if (pLocalTable[ii].iUniq == 0)
        pLocalTable[ii].iUniq = 1;
    pLocalTable[ii].iType = LO_NULL;

// Leave the critical section.

    LEAVECRITICALSECTION(&semLocal);
}

/******************************Public*Routine******************************\
* cLockHandle (h)
*
* Lock handle for access by a thread.  If another thread possesses the lock,
* this will fail.
*
* Returns:
*   Lock count.  Returns -1 if failure.
*
* History:
*  31-Jan-1995 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

LONG cLockHandle(ULONG_PTR h)
{
    LONG lRet = -1;

// Extract the index from the handle.

    UINT ii = MASKINDEX(h);
    PLHE plhe = pLocalTable + ii;

// Get critical for handle locking.

    ENTERCRITICALSECTION(&semLocal);

    if ((ii >= cLheCommitted) ||
        (!MATCHUNIQ(plhe,h))  ||
        ((plhe->iType != LO_RC))
       )
    {
        DBGLEVEL1(LEVEL_ERROR, "cLockHandle: invalid handle 0x%lx\n", h);
        SetLastError(ERROR_INVALID_HANDLE);
        goto cLockHandle_exit;
    }

// If not currently locked or if current owning thread is the same,
// do the lock.

    if ( (pLocalTable[ii].cLock == 0) ||
         (pLocalTable[ii].tidOwner == GetCurrentThreadId()) )
    {
        pLocalTable[ii].cLock++;
        pLocalTable[ii].tidOwner = GetCurrentThreadId();

        lRet = (LONG) pLocalTable[ii].cLock;
    }
    else
    {
        WARNING("cLockHandle(): current thread not owner\n");
        SetLastError(ERROR_BUSY);
    }

// Leave the critical section.

cLockHandle_exit:
    LEAVECRITICALSECTION(&semLocal);
    return lRet;
}

/******************************Public*Routine******************************\
* vUnlockHandle (h)
*
* Removes a lock from the handle.  Must be owned by current thread.
*
* Note:
*   Caller should possess the lock before this is called.  This implies
*   that cLockHandle must be called and its return code must be heeded.
*
* History:
*  31-Jan-1995 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

VOID vUnlockHandle(ULONG_PTR h)
{
// Extract the index from the handle.

    UINT ii = MASKINDEX(h);

// Get critical for handle deallocation.

    ENTERCRITICALSECTION(&semLocal);

// If not currently locked or if current owning thread is the same,
// do the lock.

    ASSERTOPENGL(pLocalTable[ii].cLock > 0,
                 "vUnlockHandle(): not locked\n");
    ASSERTOPENGL(pLocalTable[ii].tidOwner == GetCurrentThreadId(),
                 "vUnlockHandle(): thread not owner\n");

    if ( (pLocalTable[ii].cLock > 0) &&
         (pLocalTable[ii].tidOwner == GetCurrentThreadId()) )
    {
        pLocalTable[ii].cLock--;
    }

// Leave the critical section.

    LEAVECRITICALSECTION(&semLocal);
}