windows-nt/Source/XPSP1/NT/com/ole32/stg/docfile/fastlock.cxx
2020-09-26 16:20:57 +08:00

492 lines
13 KiB
C++

//+--------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1991 - 1992
//
// File: fastlock.cxx
//
// Contents: Implementation of CDfMutex methods for DocFiles
//
// History: 26-Jul-94 DonnaLi Created
//
//---------------------------------------------------------------
#include <dfhead.cxx>
#pragma hdrstop
#include <df32.hxx>
#include <secdes.hxx> // from com\inc
#ifdef UNICODE
#define GLOBAL_CS L"GlobalCsMutex"
#else
#define GLOBAL_CS "GlobalCsMutex"
#endif
//
// This is the number of characters to skip over in the name
// pased to CDfMutex::Init. The name consists of the string
// OleDfRoot followed by the hex representation of a unique
// number for each Docfile. We skip CHARS_TO_SKIP number of
// characters in the name to produce a related and yet unique
// name for the file mapping containing global state for the
// critical section.
//
#define CHARS_TO_SKIP 3
//+--------------------------------------------------------------
//
// Member: CDfMutex::Init, public
//
// Synopsis: This routine creates and initializes the global
// critical section if it does not already exist.
// It then attaches to the global critical section.
//
// Arguments: [lpName] - Supplies the critical section name
//
// Returns: Appropriate status code
//
// History: 26-Jul-94 DonnaLi Created
//
// Algorithm: Uses a mutex to serialize global critical section
// creation and initialization
// The name passed in is used to create or open the
// semaphore embedded in the global critical section.
// The name with the first CHARS_TO_SKIP characters
// skipped is used to create or open the file mapping
// containing global state of the critical section.
// If a file mapping with that name already exists,
// it is not reinitialized. The caller instead just
// attaches to it.
//
//---------------------------------------------------------------
SCODE
CDfMutex::Init(
TCHAR * lpName
)
{
HANDLE hGlobalMutex;
SCODE scResult = S_OK;
DWORD dwResult;
LPSECURITY_ATTRIBUTES lpsa = NULL;
#if WIN32 == 100 || WIN32 > 200
CGlobalSecurity gs;
if (FAILED(scResult = gs.Init(TRUE))) return scResult;
#else
LPSECURITY_ATTRIBUTES gs = NULL;
#endif
#ifndef MULTIHEAP
#if WIN32 == 100 || WIN32 > 200
CWorldSecurityDescriptor wsd;
SECURITY_ATTRIBUTES secattr;
secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
secattr.lpSecurityDescriptor = &wsd;
secattr.bInheritHandle = FALSE;
#endif
//
// Serialize all global critical section initialization
//
hGlobalMutex = CreateMutex(
#if WIN32 == 100 || WIN32 > 200
&secattr, // LPSECURITY_ATTRIBUTES lpsa
#else
gs,
#endif
TRUE, // BOOL fInitialOwner
GLOBAL_CS // LPCTSTR lpszMutexName
);
//
// If the mutex create/open failed, then bail
//
if ( !hGlobalMutex )
{
return LAST_SCODE;
}
if ( GetLastError() == ERROR_ALREADY_EXISTS )
{
//
// Since the mutex already existed, the request for ownership has
// no effect.
//
// wait for the mutex
//
if ( WaitForSingleObject (hGlobalMutex, INFINITE) == WAIT_FAILED )
{
scResult = LAST_SCODE;
CloseHandle (hGlobalMutex);
return scResult;
}
}
//
// We now own the global critical section creation mutex. Create/Open the
// named semaphore.
//
#endif
_hLockSemaphore = CreateSemaphore (
gs, // LPSECURITY_ATTRIBUTES lpsa
0, // LONG cSemInitial
MAXLONG-1, // LONG cSemMax
lpName // LPCTSTR lpszSemName
);
//
// If the semaphore create/open failed, then bail
//
if ( !_hLockSemaphore )
{
scResult = LAST_SCODE;
}
else
{
//
// Create/open a shared file mapping object
// If we created it, we need to initialize the global structure.
// Otherwise just point to it.
// The global critical section creation mutex allows us to do
// this safely.
//
_hSharedMapping = CreateFileMappingT (
INVALID_HANDLE_VALUE, // HANDLE hFile
gs, // LPSECURITY_ATTRIBUTES lpsa
PAGE_READWRITE, // DWORD fdwProtect
0, // DWORD dwMaximumSizeHigh
1024, // DWORD dwMaximumSizeLow
lpName+CHARS_TO_SKIP// LPCTSTR lpszMapName
);
if ( !_hSharedMapping )
{
scResult = LAST_SCODE;
CloseHandle (_hLockSemaphore);
_hLockSemaphore = (HANDLE)NULL;
}
else
{
dwResult = GetLastError();
_pGlobalPortion = (PGLOBAL_SHARED_CRITICAL_SECTION)
MapViewOfFile (
_hSharedMapping, // HANDLE hMapObject
FILE_MAP_WRITE, // DWORD fdwAccess
0, // DWORD dwOffsetHigh
0, // DWORD dwOffsetLow
0 // DWORD cbMap
);
if (!_pGlobalPortion)
{
scResult = LAST_SCODE;
CloseHandle (_hLockSemaphore);
_hLockSemaphore = (HANDLE)NULL;
CloseHandle (_hSharedMapping);
_hSharedMapping = (HANDLE)NULL;
}
else if (dwResult != ERROR_ALREADY_EXISTS )
{
//
// We created the file mapping, so initialize the
// global portion.
//
_pGlobalPortion->LockCount = -1;
#ifdef SUPPORT_RECURSIVE_LOCK
_pGlobalPortion->RecursionCount = 0;
_pGlobalPortion->OwningThread = 0;
#else
#if DBG == 1
_pGlobalPortion->OwningThread = 0;
#endif
#endif
_pGlobalPortion->Reserved = 0;
}
}
}
#ifndef MULTIHEAP
ReleaseMutex (hGlobalMutex);
CloseHandle (hGlobalMutex);
#endif
return scResult;
}
//+--------------------------------------------------------------
//
// Member: CDfMutex::~CDfMutex, public
//
// Synopsis: This routine detaches from an existing global
// critical section.
//
// History: 26-Jul-94 DonnaLi Created
//
// Algorithm: Create or get the entry from the multistream
//
//---------------------------------------------------------------
CDfMutex::~CDfMutex(
void
)
{
//If we're holding the mutex, we need to get rid of it here.
#ifdef SUPPORT_RECURSIVE_LOCK
if ((_pGlobalPortion) &&
(_pGlobalPortion->OwningThread == GetCurrentThreadId()))
{
#else
if (_pGlobalPortion)
{
#if DBG == 1
olAssert (_pGlobalPortion->OwningThread == 0 || _pGlobalPortion->OwningThread == GetCurrentThreadId());
#endif
#endif
Release();
}
if ( _pGlobalPortion )
{
UnmapViewOfFile (_pGlobalPortion);
}
if ( _hLockSemaphore )
{
CloseHandle (_hLockSemaphore);
}
if ( _hSharedMapping )
{
CloseHandle (_hSharedMapping);
}
}
//+--------------------------------------------------------------
//
// Member: CDfMutex::Take, public
//
// Synopsis: This routine enters the global critical section.
//
// Arguments: [dwTimeout] - Supplies the timeout
//
// Returns: Appropriate status code
//
// History: 26-Jul-94 DonnaLi Created
//
// Algorithm: Enters the critical section if nobody owns it or
// if the current thread already owns it.
// Waits for the critical section otherwise.
//
//---------------------------------------------------------------
SCODE
CDfMutex::Take (
DWORD dwTimeout
)
{
olAssert (_pGlobalPortion->LockCount >= -1);
#ifdef SUPPORT_RECURSIVE_LOCK
olAssert (_pGlobalPortion->RecursionCount >= 0);
DWORD ThreadId;
ThreadId = GetCurrentThreadId();
#endif
//
// Increment the lock variable. On the transition to 0, the caller
// becomes the absolute owner of the lock. Otherwise, the caller is
// either recursing, or is going to have to wait
//
if ( !InterlockedIncrement (&_pGlobalPortion->LockCount) )
{
//
// lock count went from -1 to 0, so the caller
// is the owner of the lock
//
#ifdef SUPPORT_RECURSIVE_LOCK
_pGlobalPortion->RecursionCount = 1;
_pGlobalPortion->OwningThread = ThreadId;
#else
#if DBG == 1
_pGlobalPortion->OwningThread = GetCurrentThreadId();
#endif
#endif
return S_OK;
}
else
{
#ifdef SUPPORT_RECURSIVE_LOCK
//
// If the caller is recursing, then increment the recursion count
//
if ( _pGlobalPortion->OwningThread == ThreadId )
{
_pGlobalPortion->RecursionCount++;
return S_OK;
}
else
{
#else
#if DBG == 1
olAssert (_pGlobalPortion->OwningThread != GetCurrentThreadId());
#endif
#endif
switch (WaitForSingleObject(
_hLockSemaphore,
dwTimeout
))
{
case WAIT_OBJECT_0:
case WAIT_ABANDONED:
#ifdef SUPPORT_RECURSIVE_LOCK
_pGlobalPortion->RecursionCount = 1;
_pGlobalPortion->OwningThread = ThreadId;
#else
#if DBG == 1
_pGlobalPortion->OwningThread = GetCurrentThreadId();
#endif
#endif
return S_OK;
case WAIT_TIMEOUT:
return STG_E_INUSE;
default:
return LAST_SCODE;
}
#ifdef SUPPORT_RECURSIVE_LOCK
}
#endif
}
}
//+--------------------------------------------------------------
//
// Member: CDfMutex::Release, public
//
// Synopsis: This routine leaves the global critical section
//
// History: 26-Jul-94 DonnaLi Created
//
// Algorithm: Leaves the critical section if this is the owning
// thread.
//
//---------------------------------------------------------------
VOID
CDfMutex::Release(
void
)
{
#ifdef SUPPORT_RECURSIVE_LOCK
if ( _pGlobalPortion->OwningThread != GetCurrentThreadId() ) return;
#else
#if DBG == 1
olAssert (_pGlobalPortion->OwningThread == 0 || _pGlobalPortion->OwningThread == GetCurrentThreadId());
#endif
#endif
olAssert (_pGlobalPortion->LockCount >= -1);
#ifdef SUPPORT_RECURSIVE_LOCK
olAssert (_pGlobalPortion->RecursionCount >= 0);
//
// decrement the recursion count. If it is still non-zero, then
// we are still the owner so don't do anything other than dec the lock
// count
//
if ( --_pGlobalPortion->RecursionCount )
{
InterlockedDecrement(&_pGlobalPortion->LockCount);
}
else
{
//
// We are really leaving, so give up ownership and decrement the
// lock count
//
_pGlobalPortion->OwningThread = 0;
#else
#if DBG == 1
_pGlobalPortion->OwningThread = 0;
#endif
#endif
//
// Check to see if there are other waiters. If so, then wake up a waiter
//
if ( InterlockedDecrement(&_pGlobalPortion->LockCount) >= 0 )
{
ReleaseSemaphore(
_hLockSemaphore, // HANDLE hSemaphore
1, // LONG cReleaseCount
NULL // LPLONG lplPreviousCount
);
}
#ifdef SUPPORT_RECURSIVE_LOCK
}
#endif
}
//+--------------------------------------------------------------
//
// Member: CDfMutex::IsHandleValid, public
//
// Synopsis: This routine checks the mutex handle for validity
//
// History: 09-May-2001 HenryLee created
//
//---------------------------------------------------------------
BOOL CDfMutex::IsHandleValid (TCHAR *ptcsName)
{
#if WIN32 == 100
BOOL fValid = FALSE;
NTSTATUS nts = STATUS_SUCCESS;
WCHAR wcsBuffer[MAX_PATH] = L"";
OBJECT_NAME_INFORMATION *poni = (OBJECT_NAME_INFORMATION *) wcsBuffer;
nts = NtQueryObject (_hLockSemaphore, ObjectNameInformation, poni,
sizeof(wcsBuffer), NULL);
if (NT_SUCCESS(nts))
{
if (poni->Name.Length < sizeof(wcsBuffer) - sizeof (*poni))
{
poni->Name.Buffer[poni->Name.Length / sizeof(WCHAR)] = L'\0';
if (!lstrcmp (poni->Name.Buffer, ptcsName))
fValid = TRUE;
}
}
#else
BOOL fValid = TRUE;
#endif
return fValid;
}