|
|
//============================================================================
// Copyright (c) 2000, Microsoft Corporation
//
// File: mapper.c
//
// History:
// Yi Sun June-27-2000 Created
//
// Abstract:
// We implement a locking system with read and write locks. Callers
// can simply acquire a read lock to the obj to prevent the obj
// from being released since whoever doing the release is supposed
// acquire the write lock first. Routines for mapping between
// obj pointers and handles are also provided.
//============================================================================
#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include "windows.h"
#include "tapi.h"
#include "ndptsp.h"
typedef struct _RW_LOCK { CRITICAL_SECTION critSec; // critical section
HANDLE hEvent; // no-one-holds-any-lock event
DWORD dwRefCt; // number of threads holding locks
} RW_LOCK, *PRW_LOCK;
typedef struct _MAPPER_ENTRY { // DEF: a free entry is one that no obj is associated with
RW_LOCK rwLock; // a lock for each entry to ensure thread-safe
PVOID pObjPtr; // point to the mem block of the associated obj
// NULL when the entry is free
FREEOBJPROC pfnFreeProc; // function to call to free the obj
WORD wID; // id used for detecting bad handles
// valid value range: 1 - 0x7FFF
WORD wIndexNextFree; // index of the next free entry in the global
// mapper array, invalid when the entry is busy
} MAPPER_ENTRY, *PMAPPER_ENTRY;
typedef struct _HANDLE_OBJECT_MAPPER { RW_LOCK rwLock; // a global lock for the whole mapper
WORD wNextID; // a global id counter incremented
// after each handle mapping
WORD wIndexFreeHead; // index of head of free entry list
DWORD dwCapacity; // total number of entries in the array
DWORD dwFree; // total number of free entries left
PMAPPER_ENTRY pArray; // the global array that keeps all the mapping
} HANDLE_OBJECT_MAPPER; // the capacity to begin with, can be read from registry
#define INITIAL_MAPPER_SIZE 32
#define MAXIMUM_MAPPER_SIZE (64 * 1024) // 16-bit index limitation
// the global mapper object
static HANDLE_OBJECT_MAPPER gMapper;
BOOL InitializeRWLock( IN PRW_LOCK pLock ) { // create an autoreset event, non-signaled initially
pLock->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (NULL == pLock->hEvent) { return FALSE; }
InitializeCriticalSection(&pLock->critSec); pLock->dwRefCt = 0;
return TRUE; }
BOOL UninitializeRWLock( IN PRW_LOCK pLock ) { pLock->dwRefCt = 0; DeleteCriticalSection(&pLock->critSec); return CloseHandle(pLock->hEvent); }
//
// NOTE: due to the limitation of the current
// implementation, calling AcquireWriteLock()
// while holding a read lock of the same
// RW_LOCK will result in a DEADLOCK!!!
// Be sure to release the read lock before
// attempting to acquire the write lock.
// This limitation can be lifted by implementing
// lock upgrade (from read to write) which
// requires RW_LOCK to remember ids of all
// owning threads.
//
VOID AcquireReadLock( IN PRW_LOCK pLock ) { //
// increase the ref count, then leave
// the critical section to allow others
// to enter
//
EnterCriticalSection(&pLock->critSec); ++pLock->dwRefCt; LeaveCriticalSection(&pLock->critSec); }
VOID ReleaseReadLock( IN PRW_LOCK pLock ) { //
// decrease the ref count, check whether
// the new ref count is 0 (meaning no one
// else holds any lock), if yes, signal
// the event to allow others waiting to
// acquire write locks to continue
//
EnterCriticalSection(&pLock->critSec); if (0 == --pLock->dwRefCt) { SetEvent(pLock->hEvent); } LeaveCriticalSection(&pLock->critSec); }
VOID AcquireWriteLock( IN PRW_LOCK pLock ) { //
// enter critical section, check whether
// the ref count is 0: if yes, return
// without leaving the critical section
// to block others from entering; if no,
// leave the section before wait for others
// to release locks then reenter the section
//
try_entering_crit_sec: EnterCriticalSection(&pLock->critSec); if (pLock->dwRefCt > 0) { // make sure leaving critSec before waiting
LeaveCriticalSection(&pLock->critSec);
WaitForSingleObject(pLock->hEvent, INFINITE); goto try_entering_crit_sec; } pLock->dwRefCt = 1; }
VOID ReleaseWriteLock( IN PRW_LOCK pLock ) { //
// reset the ref count to 0, signal
// the event, leave the critical section
//
pLock->dwRefCt = 0; SetEvent(pLock->hEvent); LeaveCriticalSection(&pLock->critSec); }
LONG InitializeMapper( ) { DWORD dwIndex;
TspLog(DL_TRACE, "InitializeMapper: entering...");
// alloc and zeroinit the array
gMapper.pArray = (PMAPPER_ENTRY) MALLOC(INITIAL_MAPPER_SIZE * sizeof(MAPPER_ENTRY)); if (NULL == gMapper.pArray) { TspLog(DL_ERROR, "InitializeMapper: failed to alloc(1) mapper array"); return LINEERR_NOMEM; }
// init the global lock for the mapper
InitializeRWLock(&gMapper.rwLock);
gMapper.wNextID = 1; gMapper.wIndexFreeHead = 0; gMapper.dwCapacity = INITIAL_MAPPER_SIZE; gMapper.dwFree = INITIAL_MAPPER_SIZE;
// init the lock for each mapper entry and link the free entry list
for (dwIndex = 0; dwIndex < INITIAL_MAPPER_SIZE - 1; dwIndex++) { InitializeRWLock(&(gMapper.pArray[dwIndex].rwLock)); gMapper.pArray[dwIndex].wIndexNextFree = (WORD)(dwIndex + 1); } InitializeRWLock(&(gMapper.pArray[INITIAL_MAPPER_SIZE - 1].rwLock));
return TAPI_SUCCESS; }
VOID UninitializeMapper() { DWORD dwIndex;
for (dwIndex = 0; dwIndex < gMapper.dwCapacity; dwIndex++) { UninitializeRWLock(&(gMapper.pArray[dwIndex].rwLock)); }
UninitializeRWLock(&gMapper.rwLock);
FREE(gMapper.pArray);
TspLog(DL_TRACE, "UninitializeMapper: exited"); }
//
// NOTE: both OpenObjHandle() and CloseObjHandle() acquire write lock of
// gMapper.rwLock at the beginning and release it at the end;
// but that's not the case for AcquireObjReadLock(), GetObjWithReadLock(),
// AcquireObjWriteLock() and GetObjWithWriteLock(): they acquire read
// lock of gMapper.rwLock at the beginning and never release it before
// exit, the lock is actually released in either ReleaseObjReadLock()
// or ReleaseObjWriteLock(), which means the caller thread of these
// four lock-acquiring functions actually not only holds the lock
// it intends to acquire but also holds the read lock of gMapper.rwLock
// as a by-product. The reason for that is preventing CloseObjHandle()
// from getting the write lock of gMapper.rwLock while waiting for the
// write lock for a mapper entry -- that sure will result in a DEADLOCK
// because if another thread has the read lock for that entry, for it to
// release the lock, it needs to acquire the read lock of gMapper.rwLock.
// The consequence of keeping the read lock of gMapper.rwLock is that
// the caller thread has to call ReleaseObjXXXLock() to release it
// before calling OpenObjHandle() or CloseObjHandle() to avoid another
// kind of DEADLOCK (see previous NOTE).
//
LONG OpenObjHandle( IN PVOID pObjPtr, IN FREEOBJPROC pfnFreeProc, OUT HANDLE *phObj ) { WORD wIndex; PMAPPER_ENTRY pEntry; DWORD dwHandle;
AcquireWriteLock(&gMapper.rwLock);
if (0 == gMapper.dwFree) { DWORD dwIndex; DWORD dwOldSize = gMapper.dwCapacity; PMAPPER_ENTRY pOldArray = gMapper.pArray;
if (MAXIMUM_MAPPER_SIZE == gMapper.dwCapacity) { TspLog(DL_ERROR, "OpenObjHandle: failed to grow mapper array"); ReleaseWriteLock(&gMapper.rwLock); return LINEERR_OPERATIONFAILED; }
// increase the capacity by a factor of two
gMapper.dwCapacity <<= 1; // allocate a new array twice the old size, then zeroinit it
gMapper.pArray = (PMAPPER_ENTRY) MALLOC(gMapper.dwCapacity * sizeof(MAPPER_ENTRY)); if (NULL == gMapper.pArray) { TspLog(DL_ERROR, "OpenObjHandle: failed to alloc(2) mapper array"); ReleaseWriteLock(&gMapper.rwLock); return LINEERR_NOMEM; } TspLog(DL_INFO, "OpenObjHandle: the mapper array has grown to %d", gMapper.dwCapacity);
// copy the old array over
for (dwIndex = 0; dwIndex < dwOldSize; dwIndex++) { CopyMemory(&(gMapper.pArray[dwIndex].rwLock), &(pOldArray[dwIndex].rwLock), sizeof(RW_LOCK));
//
// Delete the lock from the old table and initialize
// the cs in the new table. Otherwise pageheap will
// assert when oldtable is being freed - and its not
// a good thing anyway. Note that since the global
// lock is held across all Acquire/Get/Release functions
// for the lock, this is a safe operation to do here -
// no object would be holding the lock when this is
// being done since we are holding the write lock
// for the gmapper.
//
DeleteCriticalSection(&pOldArray[dwIndex].rwLock.critSec); InitializeCriticalSection(&gMapper.pArray[dwIndex].rwLock.critSec); gMapper.pArray[dwIndex].pObjPtr = pOldArray[dwIndex].pObjPtr; gMapper.pArray[dwIndex].pfnFreeProc = pOldArray[dwIndex].pfnFreeProc; gMapper.pArray[dwIndex].wID = pOldArray[dwIndex].wID; }
// init locks for new entries and link them
for (dwIndex = dwOldSize; dwIndex < gMapper.dwCapacity - 1; dwIndex++) { InitializeRWLock(&(gMapper.pArray[dwIndex].rwLock)); gMapper.pArray[dwIndex].wIndexNextFree = (WORD)(dwIndex + 1); } InitializeRWLock(&(gMapper.pArray[gMapper.dwCapacity - 1].rwLock));
// reset the globals
gMapper.dwFree = dwOldSize; gMapper.wIndexFreeHead = (WORD)dwOldSize;
// free the old array
FREE(pOldArray); }
ASSERT(gMapper.dwFree != 0); wIndex = gMapper.wIndexFreeHead; pEntry = gMapper.pArray + wIndex; gMapper.wIndexFreeHead = pEntry->wIndexNextFree; gMapper.dwFree--;
pEntry->pObjPtr = pObjPtr; pEntry->pfnFreeProc = pfnFreeProc; pEntry->wID = gMapper.wNextID++;
// make sure wNextID is within range
if (gMapper.wNextID & 0x8000) { gMapper.wNextID = 1; } pEntry->wIndexNextFree = 0; // it's always 0 when the entry is not free
//
// bit 0 is always 0
// bits 1-16 contains the index into pArray
// bits 17-31 contains the id
//
// this enables us to differentiate the TSP handles
// created here for outgoing calls and the pseudo handles
// created in NDPROXY for incoming calls which always
// has the lower bit set
//
dwHandle = (((pEntry->wID) << 16) | wIndex) << 1;
// a handle is a ptr, so on 64-bit platform, dwHandle needs to be extended
*phObj = (HANDLE)UlongToPtr(dwHandle);
ReleaseWriteLock(&gMapper.rwLock); return TAPI_SUCCESS; }
LONG CloseObjHandle( IN HANDLE hObj ) { DWORD dwHandle = PtrToUlong(hObj) >> 1; WORD wIndex = (WORD)(dwHandle & 0xFFFF); WORD wID = (WORD)(dwHandle >> 16);
AcquireWriteLock(&gMapper.rwLock);
if ((wIndex >= gMapper.dwCapacity) || (wID != gMapper.pArray[wIndex].wID) || (NULL == gMapper.pArray[wIndex].pObjPtr)) { TspLog(DL_WARNING, "CloseObjHandle: bad handle(%p)", hObj);
ReleaseWriteLock(&gMapper.rwLock); return LINEERR_OPERATIONFAILED; }
AcquireWriteLock(&gMapper.pArray[wIndex].rwLock);
#if DBG
TspLog(DL_TRACE, "CloseObjHandle: closing handle(%p)", hObj); #endif //DBG
// free the obj
(*(gMapper.pArray[wIndex].pfnFreeProc))(gMapper.pArray[wIndex].pObjPtr);
// close obj handle
gMapper.pArray[wIndex].pObjPtr = NULL; gMapper.pArray[wIndex].pfnFreeProc = NULL; gMapper.pArray[wIndex].wID = 0;
// insert the entry into the free list as the head
gMapper.pArray[wIndex].wIndexNextFree = gMapper.wIndexFreeHead; gMapper.wIndexFreeHead = wIndex;
// update the free total
gMapper.dwFree++;
ReleaseWriteLock(&gMapper.pArray[wIndex].rwLock); ReleaseWriteLock(&gMapper.rwLock); return TAPI_SUCCESS; }
LONG AcquireObjReadLock( IN HANDLE hObj ) { DWORD dwHandle = PtrToUlong(hObj) >> 1; WORD wIndex = (WORD)(dwHandle & 0xFFFF); WORD wID = (WORD)(dwHandle >> 16);
AcquireReadLock(&gMapper.rwLock);
if ((wIndex >= gMapper.dwCapacity) || (wID != gMapper.pArray[wIndex].wID) || (NULL == gMapper.pArray[wIndex].pObjPtr)) { TspLog(DL_WARNING, "AcquireObjReadLock: bad handle(%p)", hObj); ReleaseReadLock(&gMapper.rwLock); return LINEERR_OPERATIONFAILED; }
AcquireReadLock(&gMapper.pArray[wIndex].rwLock);
#if DBG
TspLog(DL_TRACE, "AcquireObjReadLock: RefCt(%p, %d)", hObj, gMapper.pArray[wIndex].rwLock.dwRefCt); #endif //DBG
return TAPI_SUCCESS; }
LONG GetObjWithReadLock( IN HANDLE hObj, OUT PVOID *ppObjPtr ) { DWORD dwHandle = PtrToUlong(hObj) >> 1; WORD wIndex = (WORD)(dwHandle & 0xFFFF); WORD wID = (WORD)(dwHandle >> 16);
AcquireReadLock(&gMapper.rwLock);
if ((wIndex >= gMapper.dwCapacity) || (wID != gMapper.pArray[wIndex].wID) || (NULL == gMapper.pArray[wIndex].pObjPtr)) { TspLog(DL_WARNING, "GetObjWithReadLock: bad handle(%p)", hObj); ReleaseReadLock(&gMapper.rwLock); return LINEERR_OPERATIONFAILED; }
AcquireReadLock(&gMapper.pArray[wIndex].rwLock);
#if DBG
TspLog(DL_TRACE, "GetObjWithReadLock: RefCt(%p, %d)", hObj, gMapper.pArray[wIndex].rwLock.dwRefCt); #endif //DBG
*ppObjPtr = gMapper.pArray[wIndex].pObjPtr; return TAPI_SUCCESS; }
LONG ReleaseObjReadLock( IN HANDLE hObj ) { DWORD dwHandle = PtrToUlong(hObj) >> 1; WORD wIndex = (WORD)(dwHandle & 0xFFFF); WORD wID = (WORD)(dwHandle >> 16);
if ((wIndex >= gMapper.dwCapacity) || (wID != gMapper.pArray[wIndex].wID) || (NULL == gMapper.pArray[wIndex].pObjPtr)) { TspLog(DL_WARNING, "ReleaseObjReadLock: bad handle(%p)", hObj); return LINEERR_OPERATIONFAILED; }
ReleaseReadLock(&gMapper.pArray[wIndex].rwLock);
#if DBG
TspLog(DL_TRACE, "ReleaseObjReadLock: RefCt(%p, %d)", hObj, gMapper.pArray[wIndex].rwLock.dwRefCt); #endif //DBG
ReleaseReadLock(&gMapper.rwLock); return TAPI_SUCCESS; }
LONG AcquireObjWriteLock( IN HANDLE hObj ) { DWORD dwHandle = PtrToUlong(hObj) >> 1; WORD wIndex = (WORD)(dwHandle & 0xFFFF); WORD wID = (WORD)(dwHandle >> 16);
AcquireReadLock(&gMapper.rwLock);
if ((wIndex >= gMapper.dwCapacity) || (wID != gMapper.pArray[wIndex].wID) || (NULL == gMapper.pArray[wIndex].pObjPtr)) { TspLog(DL_WARNING, "AcquireObjWriteLock: bad handle(%p)", hObj); ReleaseReadLock(&gMapper.rwLock); return LINEERR_OPERATIONFAILED; }
AcquireWriteLock(&gMapper.pArray[wIndex].rwLock);
#if DBG
TspLog(DL_TRACE, "AcquireObjWriteLock: RefCt(%p, %d)", hObj, gMapper.pArray[wIndex].rwLock.dwRefCt); #endif //DBG
return TAPI_SUCCESS; }
LONG GetObjWithWriteLock( IN HANDLE hObj, OUT PVOID *ppObjPtr ) { DWORD dwHandle = PtrToUlong(hObj) >> 1; WORD wIndex = (WORD)(dwHandle & 0xFFFF); WORD wID = (WORD)(dwHandle >> 16);
AcquireReadLock(&gMapper.rwLock);
if ((wIndex >= gMapper.dwCapacity) || (wID != gMapper.pArray[wIndex].wID) || (NULL == gMapper.pArray[wIndex].pObjPtr)) { TspLog(DL_WARNING, "GetObjWithWriteLock: bad handle(%p)", hObj); ReleaseReadLock(&gMapper.rwLock); return LINEERR_OPERATIONFAILED; }
AcquireWriteLock(&gMapper.pArray[wIndex].rwLock);
#if DBG
TspLog(DL_TRACE, "GetObjWithWriteLock: RefCt(%p, %d)", hObj, gMapper.pArray[wIndex].rwLock.dwRefCt); #endif //DBG
*ppObjPtr = gMapper.pArray[wIndex].pObjPtr; return TAPI_SUCCESS; }
LONG ReleaseObjWriteLock( IN HANDLE hObj ) { DWORD dwHandle = PtrToUlong(hObj) >> 1; WORD wIndex = (WORD)(dwHandle & 0xFFFF); WORD wID = (WORD)(dwHandle >> 16);
if ((wIndex >= gMapper.dwCapacity) || (wID != gMapper.pArray[wIndex].wID) || (NULL == gMapper.pArray[wIndex].pObjPtr)) { TspLog(DL_WARNING, "ReleaseObjWriteLock: bad handle(%p)", hObj); return LINEERR_OPERATIONFAILED; }
ReleaseWriteLock(&gMapper.pArray[wIndex].rwLock);
#if DBG
TspLog(DL_TRACE, "ReleaseObjWriteLock: RefCt(%p, %d)", hObj, gMapper.pArray[wIndex].rwLock.dwRefCt); #endif //DBG
ReleaseReadLock(&gMapper.rwLock); return TAPI_SUCCESS; }
|