Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

604 lines
17 KiB

//============================================================================
// 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;
}