|
|
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
sputils.c
Abstract:
Core sputils library file
Author:
Jamie Hunter (JamieHun) Jun-27-2000
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
typedef ULONG (__cdecl *PFNDbgPrintEx)(IN ULONG ComponentId,IN ULONG Level,IN PCH Format, ...); static PFNDbgPrintEx pfnDbgPrintEx = NULL; static BOOL fInitDebug = FALSE;
static LONG RefCount = 0; // when this falls back to zero, release all resources
static BOOL SucceededInit = FALSE;
#define COUNTING 1
#if COUNTING
static DWORD pSpCheckHead = 0xaabbccdd; static LONG pSpFailCount = 0; static LONG pSpInitCount = 0; static LONG pSpUninitCount = 0; static LONG pSpConflictCount = 0; static BOOL pSpDoneInit = FALSE; static BOOL pSpFailedInit = FALSE; static DWORD pSpCheckTail = 0xddccbbaa; #endif
//
// At some point, a thread, process or module will call pSetupInitializeUtils,
// and follow by a call to pSetupUninitializeUtils when done (cleanup)
//
// prior to this point, there's been no initialization other than static
// constants (above) pSetupInitializeUtils and pSetupUninitializeUtils must be
// mut-ex with each other and themselves
// thread A may call pSetupInitializeUtils while thread B is calling
// pSetupUninitializeUtils, the init in this case must succeed
// we can't use a single mutex or event object, since it must be cleaned up
// when pSetupUninitializeUtils succeeds
// we can't use a simple user-mode spin-lock, since priorities may be different,
// and it's just plain ugly using Sleep(0)
// so we have the _AcquireInitMutex and _ReleaseInitMutex implementations below
// it's guarenteed that when _AcquireInitMutex returns, it is not using any
// resources to hold the lock
// it will hold an event object if the thread is blocked, per blocked thread.
// This is ok since the number of blocked threads at any time will be few.
//
// It works as follows:
//
// a linked list of requests is maintained, with head at pWaitHead
// The head is interlocked, and when an item is inserted at pWaitHead
// it's entries must be valid, and can no longer be touched until
// the mutex is acquired.
//
// if the request is the very first, it need not block, will not block,
// as (at worst) the other thread has just removed it's request from the head
// and is about to return. The thread that inserts the first request into the
// list automatically owns the mutex.
//
// if the request is anything but the first, it will have an event object
// that will eventually be signalled, and at that point owns the mutex.
//
// the Thread that owns the mutex may modify anything on the wait-list,
// including pWaitHead.
//
// If the thread that owns the mutex is pWaitHead at the point it's releasing
// mutex, it does not need to signal anyone. This is protected by
// InterlockedCompareExchangePointer. If it finds itself in this state, the next
// pSetupInitializeUtils will automatically obtain the mutex, also protected
// by InterlockedCompareExchangePointer.
//
// If there are waiting entries in the list, then the tail-most waiting entry is
// signalled, at which point the related thread now owns the mutex.
//
#ifdef UNICODE
typedef struct _LinkWaitList { HANDLE hEvent; // for this item
struct _LinkWaitList *pNext; // from Head to Tail
struct _LinkWaitList *pPrev; // from Tail to Head
} LinkWaitList;
static LinkWaitList * pWaitHead = NULL; // insert new wait items here
static BOOL _AcquireInitMutex( OUT LinkWaitList *pEntry ) /*++
Routine Description:
Atomically acquire process mutex with no pre-requisite initialization other than static globals. Each blocked call will require an event to be created.
Requests cannot be nested per thread (deadlock will occur)
Arguments:
pEntry - structure to hold mutex information. This structure must persist until call to _ReleaseInitMutex.
Global:pWaitHead - atomic linked list of mutex requests
Return Value:
TRUE if mutex acquired. FALSE on failure (no resources)
--*/ { LinkWaitList *pTop; DWORD res; pEntry->pPrev = NULL; pEntry->pNext = NULL; pEntry->hEvent = NULL; //
// fast lock, this will only succeed if we're the first and we have no reason to wait
// this saves us needlessly creating an event
//
if(!InterlockedCompareExchangePointer(&pWaitHead,pEntry,NULL)) { return TRUE; }
#if COUNTING
InterlockedIncrement(&pSpConflictCount); #endif
//
// someone has (or, at least a moment ago, had) the lock, so we need an event
//
pEntry->hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); if(!pEntry->hEvent) { return FALSE; } //
// once pEntry is added to list, it cannot be touched until
// WaitSingleObject is satisfied (unless we were the first)
// if pWaitHead changes in the middle of the loop, we'll repeat again
//
do { pTop = pWaitHead; pEntry->pNext = pTop; } while (pTop != InterlockedCompareExchangePointer(&pWaitHead,pEntry,pTop)); if(pTop) { //
// we're not the first on the list
// the owner of pTop will signal our event, wait for it.
//
res = WaitForSingleObject(pEntry->hEvent,INFINITE); pEntry->hEvent = NULL; } //
// don't need event any more, the fact we've been signalled indicates we've now got the lock
//
CloseHandle(pEntry->hEvent); if(res != WAIT_OBJECT_0) { MYASSERT(res == WAIT_OBJECT_0); return FALSE; } return TRUE; }
static VOID _ReleaseInitMutex( IN LinkWaitList *pEntry ) /*++
Routine Description:
release process mutex previously acquired by _AcquireInitMutex thread must own the mutex no resources required for this action. This call may only be done once for each _AcquireInitMutex
Arguments:
pEntry - holding mutex information. This structure must have been initialized by _AcquireInitMutex.
Global:pWaitHead - atomic linked list of mutex requests
Return Value:
none.
--*/ { LinkWaitList *pHead; LinkWaitList *pWalk; LinkWaitList *pPrev;
MYASSERT(!pEntry->pNext); pHead = InterlockedCompareExchangePointer(&pWaitHead,NULL,pEntry); if(pHead == pEntry) { //
// we were at head of list as well as at tail of list
// list has now been reset to NULL
// and may even already contain an entry due to a pLock call
return; } if(!pEntry->pPrev) { //
// we need to walk down list from pHead to pEntry
// at the same time, remember back links
// so we don't need to do this every time
// note, we will never get here if pHead==pEntry
//
MYASSERT(pHead); MYASSERT(!pHead->pPrev); for(pWalk = pHead;pWalk != pEntry;pWalk = pWalk->pNext) { MYASSERT(pWalk->pNext); MYASSERT(!pWalk->pNext->pPrev); pWalk->pNext->pPrev = pWalk; } } pPrev = pEntry->pPrev; pPrev->pNext = NULL; // aids debugging, even in free build.
SetEvent(pPrev->hEvent); return; }
#else
//
// ANSI functions *MUST* work on Win95
// to support install of Whistler
// InterlockedCompareExchange(Pointer)
// is not supported
// so we'll use something simple/functional instead
// that uses the supported InterlockedExchange
//
static LONG SimpleCritSec = FALSE; typedef PVOID LinkWaitList;
static BOOL _AcquireInitMutex( OUT LinkWaitList *pEntry ) { while(InterlockedExchange(&SimpleCritSec,TRUE) == TRUE) { //
// release our timeslice
// we should rarely be spinning here
// starvation can occur in some circumstances
// if initializing threads are of different priorities
//
Sleep(0); } return TRUE; }
static VOID _ReleaseInitMutex( IN LinkWaitList *pEntry ) { if(InterlockedExchange(&SimpleCritSec,FALSE) == FALSE) { MYASSERT(0 && SimpleCritSec); } }
#endif
BOOL pSetupInitializeUtils( VOID ) /*++
Routine Description:
Initialize this library balance each successful call to this function with equal number of calls to pSetupUninitializeUtils
Arguments:
none
Return Value:
TRUE if init succeeded, FALSE otherwise
--*/ { LinkWaitList Lock;
if(!_AcquireInitMutex(&Lock)) { #if COUNTING
InterlockedIncrement(&pSpFailCount); #endif
return FALSE; } #if COUNTING
InterlockedIncrement(&pSpInitCount); #endif
RefCount++; if(RefCount==1) { pSpDoneInit = TRUE; SucceededInit = _pSpUtilsMemoryInitialize(); if(!SucceededInit) { pSpFailedInit = TRUE; _pSpUtilsMemoryUninitialize(); } } _ReleaseInitMutex(&Lock); return SucceededInit; }
BOOL pSetupUninitializeUtils( VOID ) /*++
Routine Description:
Uninitialize this library This should be called for each successful call to pSetupInitializeUtils
Arguments:
none
Return Value:
TRUE if cleanup succeeded, FALSE otherwise
--*/ { LinkWaitList Lock; #if COUNTING
InterlockedIncrement(&pSpUninitCount); #endif
if(!SucceededInit) { return FALSE; } if(!_AcquireInitMutex(&Lock)) { return FALSE; } RefCount--; if(RefCount == 0) { _pSpUtilsMemoryUninitialize(); SucceededInit = FALSE; } _ReleaseInitMutex(&Lock); return TRUE; }
VOID _pSpUtilsAssertFail( IN PSTR FileName, IN UINT LineNumber, IN PSTR Condition ) { int i; CHAR Name[MAX_PATH]; PCHAR p; LPSTR Msg; DWORD msglen; DWORD sz;
//
// obtain module name
//
sz = GetModuleFileNameA(NULL,Name,MAX_PATH); if((sz == 0) || (sz > MAX_PATH)) { strcpy(Name,"?"); } if(p = strrchr(Name,'\\')) { p++; } else { p = Name; } msglen = strlen(p)+strlen(FileName)+strlen(Condition)+128; //
// assert might be out of memory condition
// stack alloc is more likely to succeed than memory alloc
//
try { Msg = (LPSTR)_alloca(msglen); wsprintfA( Msg, "SPUTILS: Assertion failure at line %u in file %s!%s: %s\r\n", LineNumber, p, FileName, Condition ); } except (EXCEPTION_EXECUTE_HANDLER) { Msg = "SpUTILS ASSERT!!!! (out of stack)\r\n"; }
OutputDebugStringA(Msg); DebugBreak(); }
VOID pSetupDebugPrintEx( DWORD Level, PCTSTR format, ... OPTIONAL )
/*++
Routine Description:
Send a formatted string to the debugger. Note that this is expected to work cross-platform, but use preferred debugger
Arguments:
format - standard printf format string.
Return Value:
NONE.
--*/
{ TCHAR buf[1026]; // bigger than max size
va_list arglist;
if (!fInitDebug) { pfnDbgPrintEx = (PFNDbgPrintEx)GetProcAddress(GetModuleHandle(TEXT("NTDLL")), "DbgPrintEx"); fInitDebug = TRUE; }
va_start(arglist, format); wvsprintf(buf, format, arglist);
if (pfnDbgPrintEx) { #ifdef UNICODE
(*pfnDbgPrintEx)(DPFLTR_SETUP_ID, Level, "%ls",buf); #else
(*pfnDbgPrintEx)(DPFLTR_SETUP_ID, Level, "%s",buf); #endif
} else { OutputDebugString(buf); } }
|