Dryad/DryadVertex/VertexHost/system/classlib/include/DrCriticalSection.h

521 lines
13 KiB
C++

/*
Copyright (c) Microsoft Corporation
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License
at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions and
limitations under the License.
*/
/*
* DrCriticalSection
*
* Defines a utility class for wrapping a critical section
*/
#pragma once
#define DEFAULT_SPIN_COUNT 4000u
#define DEFAULT_DR_LOG_HELD_TOO_LONG_TIMEOUT (500 * DrTimeInterval_Millisecond)
class DrCriticalSectionBase : public CRITICAL_SECTION
{
public:
void Init(
__in PCSTR name = NULL,
DWORD spinCount = DEFAULT_SPIN_COUNT,
bool logUsage = false,
DrTimeInterval logHeldTooLongTimeout = DEFAULT_DR_LOG_HELD_TOO_LONG_TIMEOUT
);
void Uninit()
{
DebugLogAssert( RecursionCount==0 && OwningThread == 0);
DeleteCriticalSection( this );
}
void Enter( PCSTR functionName = NULL, PCSTR fileName = NULL, UINT lineNumber = 0 );
void Leave( PCSTR functionName = NULL, PCSTR fileName = NULL, UINT lineNumber = 0 );
bool Acquired() const
{
return RecursionCount > 0 && (DWORD)(DWORD_PTR)OwningThread == GetCurrentThreadId();
}
// TODO: deprecate this, misspelled
bool Aquired() const
{
return RecursionCount > 0 && (DWORD)(DWORD_PTR)OwningThread == GetCurrentThreadId();
}
// Returns 0 if this thread does not own the critical section
UInt32 GetRecursionCountIfAcquired()
{
return (RecursionCount > 0 && (DWORD)(DWORD_PTR)OwningThread == GetCurrentThreadId()) ? (UInt32)RecursionCount : 0 ;
}
// also returns true if n is 0 and the thread does not own the critical section
bool AcquiredExactNumberOfTimes(UInt32 n)
{
return (GetRecursionCountIfAcquired() == n);
}
bool AcquiredExactlyOnce()
{
return RecursionCount == 1 && (DWORD)(DWORD_PTR)OwningThread == GetCurrentThreadId();
}
bool NotAcquired() const
{
return (DWORD)(DWORD_PTR)OwningThread != GetCurrentThreadId();
}
// TODO: deprecate this, misspelled
bool NotAquired() const
{
return (DWORD)(DWORD_PTR)OwningThread != GetCurrentThreadId();
}
// returns the old spin count
DWORD SetSpinCount(DWORD spinCount)
{
return SetCriticalSectionSpinCount(this, spinCount);
}
// Could cause inconsistent settings if called concurrently. If you might be calling this
// method concurrently, you should claim the lock before calling. Since in most cases
// this is called before the lock is actually used, this precaution is
// not taken by default.
void SetCriticalSectionLoggingParameters(
bool logUsage = true,
DrTimeInterval logHeldTooLongTimeout = DEFAULT_DR_LOG_HELD_TOO_LONG_TIMEOUT
);
void SetCriticalSectionLogging(
bool logUsage = true
);
void SetCriticalSectionLogHeldTooLongTimeout(
DrTimeInterval logHeldTooLongTimeout = DEFAULT_DR_LOG_HELD_TOO_LONG_TIMEOUT
);
void SetCriticalSectionName(__in PCSTR name)
{
_name = name;
}
bool TryEnter( PCSTR functionName = NULL, PCSTR fileName = NULL, UINT lineNumber = 0)
{
BOOL entered = TryEnterCriticalSection(this);
if ( entered )
{
_lastFunctionName = functionName;
_lastFileName = fileName;
_lastLineNumber = lineNumber;
}
if ( _logUsage )
{
if ( entered )
{
if (fileName != NULL) {
DrLogD( "CritSect TRY ENTER OK, %s at %s %s(%u), addr=%08Ix",
_name, functionName, fileName, lineNumber, this );
} else {
DrLogD( "CritSect TRY ENTER OK, %s, addr=%08Ix",
_name, this );
}
}
else
{
if (fileName != NULL) {
DrLogW( "CritSect TRY ENTER FAILED, %s at %s %s(%u), addr=%08Ix",
_name, functionName, fileName, lineNumber, this );
} else {
DrLogW( "CritSect TRY ENTER FAILED, %s, addr=%08Ix",
_name, this );
}
}
}
return entered != FALSE;
}
PCSTR _name;
bool _logUsage;
DWORD _logHeldTooLongTimeoutMs;
PCSTR _lastFunctionName;
PCSTR _lastFileName;
UINT _lastLineNumber;
DWORD _enterTimeMs;
};
class DrCriticalSection : public DrCriticalSectionBase
{
public:
DrCriticalSection(
PCSTR name = NULL,
DWORD spinCount = DEFAULT_SPIN_COUNT,
bool logUsage = false,
UINT logHeldTooLongTimeout = DEFAULT_DR_LOG_HELD_TOO_LONG_TIMEOUT
)
{
Init( name, spinCount, logUsage, logHeldTooLongTimeout );
}
~DrCriticalSection()
{
Uninit();
}
};
class AutoLock
{
friend class AutoLockLogged;
public:
AutoLock()
{
_lock = NULL;
}
AutoLock( DrCriticalSectionBase* lock )
{
_lock = lock;
_lock->Enter();
}
AutoLock( DrCriticalSectionBase& lock )
{
_lock = &lock;
_lock->Enter();
}
AutoLock( DrCriticalSectionBase* lock, PCSTR functionName, PCSTR fileName, UINT lineCount )
{
_lock = lock;
_lock->Enter( functionName, fileName, lineCount );
}
~AutoLock()
{
if ( _lock != NULL )
{
_lock->Leave();
}
}
void Enter( DrCriticalSectionBase* newLock = NULL )
{
if ( newLock != NULL )
{
LogAssert( _lock == NULL );
_lock = newLock;
}
LogAssert( _lock != NULL );
_lock->Enter();
}
void Leave()
{
LogAssert( _lock != NULL );
_lock->Leave();
_lock = NULL;
}
private:
AutoLock( const AutoLock& );
AutoLock& operator=( const AutoLock& );
DrCriticalSectionBase* _lock;
};
typedef AutoLock DrScopedCritSec;
typedef AutoLock DrAutoCriticalSection;
#define DR_ENTER(lock) (lock).Enter( __FUNCTION__, __FILE__, __LINE__ )
#define DR_LEAVE(lock) (lock).Leave( __FUNCTION__, __FILE__, __LINE__ )
#define DR_ENTER_NOLOG(lock) (lock).Enter()
#define DR_LEAVE_NOLOG(lock) (lock).Leave()
#define EnterAndLog() Enter( __FUNCTION__, __FILE__, __LINE__ )
#define LeaveAndLog() Leave( __FUNCTION__, __FILE__, __LINE__ )
#define LOCK_JOIN_NAME2(a, b, c) a##_line_##b##_counter_##c
#define LOCK_JOIN_NAME(a, b, c) LOCK_JOIN_NAME2(a, b, c)
#define LOCK(lock) AutoLock LOCK_JOIN_NAME(autoLock, __LINE__, __COUNTER__) (&lock, __FUNCTION__, __FILE__, __LINE__);
#define LOCK_NOLOG(lock) AutoLock LOCK_JOIN_NAME(autoLock, __LINE__, __COUNTER__) (&lock);
#define DR_IN_CRITSEC(cs) LOCK(cs)
#define DRLOCKABLENOIMPL \
public: \
virtual void Lock() override = 0; \
virtual void Lock() const override = 0; \
virtual void Unlock() override = 0; \
virtual void Unlock() const override = 0; \
#define DRLOCKABLEIMPL_NO_LOCK \
public: \
virtual void Lock() override\
{ \
} \
void Lock() const override \
{ \
} \
virtual void Unlock() override \
{ \
} \
virtual void Unlock() const override \
{ \
}
#define DRLOCKABLEIMPL_PROTO \
public: \
virtual void Lock(); \
virtual void Lock() const; \
virtual void Unlock(); \
virtual void Unlock() const; \
#define DRLOCKABLEIMPL_BASE(InterfaceClass) \
protected: \
mutable DrCriticalSection m_cs; \
public: \
virtual void Lock() override \
{ \
m_cs.Enter(); \
} \
virtual void Lock() const override \
{ \
m_cs.Enter(); \
} \
virtual void Unlock() override \
{ \
m_cs.Leave(); \
} \
virtual void Unlock() const override \
{ \
m_cs.Leave(); \
}
#define DRLOCKABLEIMPL_DELEGATE(pImpl) \
public: \
virtual void Lock() override \
{ \
pImpl->Lock(); \
} \
virtual void Lock() const override \
{ \
pImpl->Lock(); \
} \
virtual void Unlock() override \
{ \
pImpl->Unlock(); \
} \
virtual void Unlock() const override \
{ \
pImpl->Unlock(); \
}
#define DRLOCKABLEIMPL_DELEGATE_OUTSIDE_CLASS(classname, pImpl) \
void classname::Lock() \
{ \
pImpl->Lock(); \
} \
void classname::Lock() const \
{ \
pImpl->Lock(); \
} \
void classname::Unlock() \
{ \
pImpl->Unlock(); \
} \
void classname::Unlock() const \
{ \
pImpl->Unlock(); \
}
#define DRLOCKABLEIMPL DRLOCKABLEIMPL_BASE(IDrLockable)
class IDrLockable
{
public:
virtual void Lock() = 0;
virtual void Lock() const = 0;
virtual void Unlock() = 0;
virtual void Unlock() const = 0;
virtual ~IDrLockable()
{
}
};
class DrLockable : public IDrLockable
{
DRLOCKABLEIMPL
};
template<class T> class DrScopedLock
{
public:
DrScopedLock(T *pT = NULL)
{
m_pT = pT;
if (pT != NULL) {
pT->Lock();
}
}
~DrScopedLock()
{
if (m_pT != NULL) {
m_pT->Unlock();
}
}
private:
T *m_pT;
};
typedef DrScopedLock<const IDrLockable> DrScopedLockable;
extern DrCriticalSection *g_pDrGlobalCritSec; // A general shared critical section that is intialized on first use or at static constructor time
extern DrCriticalSection *DrInitializeGlobalCritSec();
__inline DrCriticalSection *DrGetGlobalCritSec()
{
if (g_pDrGlobalCritSec == NULL) {
DrInitializeGlobalCritSec();
}
return g_pDrGlobalCritSec;
}
__inline void DrEnterGlobalCritSec()
{
if (g_pDrGlobalCritSec == NULL) {
DrInitializeGlobalCritSec();
}
#pragma prefast(push)
#pragma prefast(disable:6011)
g_pDrGlobalCritSec->Enter();
#pragma prefast(pop)
}
__inline void DrLeaveGlobalCritSec()
{
g_pDrGlobalCritSec->Leave();
}
// A scoped lock of the singleton global cosmos critical section
class DrScopedGlobalLock
{
public:
DrScopedGlobalLock()
{
DrEnterGlobalCritSec();
}
~DrScopedGlobalLock()
{
DrLeaveGlobalCritSec();
}
};
// put this in your header file to declare a static singleton lock that is never destructed and can be
// used within static initializers
#define DECLARE_STATIC_LOCK(name) \
class name \
{ \
public: \
static DrCriticalSection *InitialGetCritSec() \
{ \
DrScopedGlobalLock glock; \
if (s_pCritSec == NULL) { \
s_pCritSec = new DrCriticalSection(); \
} \
return s_pCritSec; \
} \
__forceinline static DrCriticalSection *GetCritSec() \
{ \
if (s_pCritSec == NULL) { \
InitialGetCritSec(); \
} \
return s_pCritSec; \
} \
static void Lock() \
{ \
GetCritSec()->Enter(); \
} \
static void Unlock() \
{ \
GetCritSec()->Enter(); \
} \
private: \
static DrCriticalSection *s_pCritSec; \
}; \
// Put this in your cpp file to provide a definition for a static singleton lock that is never destructed and can be
// used within static initializers.
#define DEFINE_STATIC_LOCK(name) \
DrCriticalSection * name::s_pCritSec = NULL;
// A scoped lock of a singleton static critical section that is never destructed and can be
// used within static initializers.
template<class LockName> class DrScopedStaticLock
{
public:
DrScopedStaticLock()
{
LockName::Lock();
}
~DrScopedStaticLock()
{
LockName::Unlock();
}
};
#define DRLOCKABLEIMPL_DELEGATE_TO_STATIC(LockName) \
public: \
virtual void Lock() override \
{ \
LockName::Lock(); \
} \
virtual void Lock() const override \
{ \
LockName::Lock(); \
} \
virtual void Unlock() override \
{ \
LockName::Unlock(); \
} \
virtual void Unlock() const override \
{ \
LockName::Unlock(); \
}