diff options
Diffstat (limited to 'src/vm/rwlock.h')
-rw-r--r-- | src/vm/rwlock.h | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/src/vm/rwlock.h b/src/vm/rwlock.h new file mode 100644 index 0000000000..dc8e67e6fb --- /dev/null +++ b/src/vm/rwlock.h @@ -0,0 +1,287 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +//+------------------------------------------------------------------- +// +// File: RWLock.h +// +// Contents: Reader writer lock implementation that supports the +// following features +// 1. Cheap enough to be used in large numbers +// such as per object synchronization. +// 2. Supports timeout. This is a valuable feature +// to detect deadlocks +// 3. Supports caching of events. The allows +// the events to be moved from least contentious +// regions to the most contentious regions. +// In other words, the number of events needed by +// Reader-Writer lockls is bounded by the number +// of threads in the process. +// 4. Supports nested locks by readers and writers +// 5. Supports spin counts for avoiding context switches +// on multi processor machines. +// 6. Supports functionality for upgrading to a writer +// lock with a return argument that indicates +// intermediate writes. Downgrading from a writer +// lock restores the state of the lock. +// 7. Supports functionality to Release Lock for calling +// app code. RestoreLock restores the lock state and +// indicates intermediate writes. +// 8. Recovers from most common failures such as creation of +// events. In other words, the lock mainitains consistent +// internal state and remains usable +// +// Classes: CRWLock, +// CStaticRWLock +// +//-------------------------------------------------------------------- + +#ifdef FEATURE_RWLOCK +#ifndef _RWLOCK_H_ +#define _RWLOCK_H_ +#include "common.h" +#include "threads.h" + +// If you do define this, make sure you define this in managed as well. +//#define RWLOCK_STATISTICS 0 + +extern DWORD gdwDefaultTimeout; +extern DWORD gdwDefaultSpinCount; + + +//+------------------------------------------------------------------- +// +// Struct: LockCookie +// +// Synopsis: Lock cookies returned to the client +// +//+------------------------------------------------------------------- +typedef struct { + DWORD dwFlags; + DWORD dwWriterSeqNum; + WORD wReaderLevel; + WORD wWriterLevel; + DWORD dwThreadID; +} LockCookie; + +//+------------------------------------------------------------------- +// +// Class: CRWLock +// +// Synopsis: Class the implements the reader writer locks. +// +//+------------------------------------------------------------------- +class CRWLock : public Object +{ + friend class MscorlibBinder; + +public: + // Constuctor + CRWLock(); + + // Cleanup + void Cleanup(); + + OBJECTHANDLE GetObjectHandle(); + HRESULT CreateOwnerIterator(SIZE_T *pIterator); + static void GetNextOwner(SIZE_T Iterator, IHostTask **ppOwnerHostTask); + static void DeleteOwnerIterator(SIZE_T Iterator); + + // Statics that do the core work + static FCDECL1 (void, StaticPrivateInitialize, CRWLock *pRWLock); + static FCDECL1 (void, StaticPrivateDestruct, CRWLock *pRWLock); + static FCDECL2 (void, StaticAcquireReaderLockPublic, CRWLock *pRWLock, DWORD dwDesiredTimeout); + static FCDECL2 (void, StaticAcquireWriterLockPublic, CRWLock *pRWLock, DWORD dwDesiredTimeout); + static FCDECL1 (void, StaticReleaseReaderLockPublic, CRWLock *pRWLock); + static FCDECL1 (void, StaticReleaseWriterLockPublic, CRWLock *pRWLock); + static FCDECL3 (void, StaticDoUpgradeToWriterLockPublic, CRWLock *pRWLock, LockCookie * pLockCookie, DWORD dwDesiredTimeout); + static FCDECL2 (void, StaticDowngradeFromWriterLock, CRWLock *pRWLock, LockCookie* pLockCookie); + static FCDECL2 (void, StaticDoReleaseLock, CRWLock *pRWLock, LockCookie * pLockCookie); + static FCDECL2 (void, StaticRestoreLockPublic, CRWLock *pRWLock, LockCookie* pLockCookie); + static FCDECL1 (FC_BOOL_RET, StaticIsReaderLockHeld, CRWLock *pRWLock); + static FCDECL1 (FC_BOOL_RET, StaticIsWriterLockHeld, CRWLock *pRWLock); + static FCDECL1 (INT32, StaticGetWriterSeqNum, CRWLock *pRWLock); + static FCDECL2 (FC_BOOL_RET, StaticAnyWritersSince, CRWLock *pRWLock, DWORD dwSeqNum); +private: + static void StaticAcquireReaderLock(CRWLock **ppRWLock, DWORD dwDesiredTimeout); + static void StaticAcquireWriterLock(CRWLock **ppRWLock, DWORD dwDesiredTimeout); + static void StaticReleaseReaderLock(CRWLock **ppRWLock); + static void StaticReleaseWriterLock(CRWLock **ppRWLock); + static void StaticRecoverLock(CRWLock **ppRWLock, LockCookie *pLockCookie, DWORD dwFlags); + static void StaticRestoreLock(CRWLock **ppRWLock, LockCookie *pLockCookie); + static void StaticUpgradeToWriterLock(CRWLock **ppRWLock, LockCookie *pLockCookie, DWORD dwDesiredTimeout); +public: + // Assert functions +#ifdef _DEBUG + BOOL AssertWriterLockHeld(); + BOOL AssertWriterLockNotHeld(); + BOOL AssertReaderLockHeld(); + BOOL AssertReaderLockNotHeld(); + BOOL AssertReaderOrWriterLockHeld(); + void AssertHeld() + { + WRAPPER_NO_CONTRACT; + AssertWriterLockHeld(); + } + void AssertNotHeld() + { + WRAPPER_NO_CONTRACT; + AssertWriterLockNotHeld(); + AssertReaderLockNotHeld(); + } +#else + void AssertWriterLockHeld() { LIMITED_METHOD_CONTRACT; } + void AssertWriterLockNotHeld() { LIMITED_METHOD_CONTRACT; } + void AssertReaderLockHeld() { LIMITED_METHOD_CONTRACT; } + void AssertReaderLockNotHeld() { LIMITED_METHOD_CONTRACT; } + void AssertReaderOrWriterLockHeld() { LIMITED_METHOD_CONTRACT; } + void AssertHeld() { LIMITED_METHOD_CONTRACT; } + void AssertNotHeld() { LIMITED_METHOD_CONTRACT; } +#endif + + // Helper functions +#ifdef RWLOCK_STATISTICS + DWORD GetReaderEntryCount() + { + LIMITED_METHOD_CONTRACT; + return(_dwReaderEntryCount); + } + DWORD GetReaderContentionCount() { LIMITED_METHOD_CONTRACT; return(_dwReaderContentionCount); } + DWORD GetWriterEntryCount() { LIMITED_METHOD_CONTRACT; return(_dwWriterEntryCount); } + DWORD GetWriterContentionCount() { LIMITED_METHOD_CONTRACT; return(_dwWriterContentionCount); } +#endif + // Static functions + static void *operator new(size_t size) + { + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return ::operator new(size); + } + static void ProcessInit(); + + static void SetTimeout(DWORD dwTimeout) + { + LIMITED_METHOD_CONTRACT; + + gdwDefaultTimeout = dwTimeout; + } + static DWORD GetTimeout() + { + LIMITED_METHOD_CONTRACT; + return(gdwDefaultTimeout); + } + static void SetSpinCount(DWORD dwSpinCount) + { + LIMITED_METHOD_CONTRACT; + + gdwDefaultSpinCount = g_SystemInfo.dwNumberOfProcessors > 1 + ? dwSpinCount + : 0; + } + static DWORD GetSpinCount() { LIMITED_METHOD_CONTRACT; return(gdwDefaultSpinCount); } + +private: + // Private helpers + static void ChainEntry(Thread *pThread, LockEntry *pLockEntry); + LockEntry *GetLockEntry(Thread *pThread = NULL); + LockEntry *FastGetOrCreateLockEntry(); + LockEntry *SlowGetOrCreateLockEntry(Thread *pThread); + void FastRecycleLockEntry(LockEntry *pLockEntry); + static void RecycleLockEntry(LockEntry *pLockEntry); + + CLREvent* GetReaderEvent(HRESULT *pHR); + CLREvent* GetWriterEvent(HRESULT *pHR); + void ReleaseEvents(); + + static LONG RWInterlockedCompareExchange(LONG RAW_KEYWORD(volatile) *pvDestination, + LONG dwExchange, + LONG dwComperand); + static LONGLONG RWInterlockedCompareExchange64(LONGLONG RAW_KEYWORD(volatile) *pvDestination, + LONGLONG qwExchange, + LONGLONG qwComparand); + static void* RWInterlockedCompareExchangePointer(PVOID RAW_KEYWORD(volatile) *pvDestination, + PVOID pExchange, + PVOID pComparand); + static LONG RWInterlockedExchangeAdd(LONG RAW_KEYWORD(volatile) *pvDestination, LONG dwAddState); + static LONG RWInterlockedIncrement(LONG RAW_KEYWORD(volatile) *pdwState); + + static DWORD RWWaitForSingleObject(CLREvent* event, DWORD dwTimeout); + static void RWSetEvent(CLREvent* event); + static void RWResetEvent(CLREvent* event); + static void RWSleep(DWORD dwTime); + +#if defined(ENABLE_CONTRACTS_IMPL) + // The LOCK_TAKEN/RELEASED macros need a "pointer" to the lock object to do + // comparisons between takes & releases (and to provide debugging info to the + // developer). We can't use "this" (*ppRWLock), because CRWLock is an Object and thus + // can move. So we use _dwLLockID instead. It's not exactly unique, but it's + // good enough--worst that can happen is if a thread takes RWLock A and erroneously + // releases RWLock B (instead of A), we'll fail to catch that if their _dwLLockID's + // are the same. On 64 bits, we can use both _dwULockID & _dwLLockID and be unique + static void * GetPtrForLockContract(CRWLock ** ppRWLock) + { +#if defined(_WIN64) + return (void *) + ( + ( + ((__int64) ((*ppRWLock)->_dwULockID)) << 32 + ) + | + ( + (__int64) ((*ppRWLock)->_dwLLockID) + ) + ); +#else //defined(_WIN64) + return LongToPtr((*ppRWLock)->_dwLLockID); +#endif //defined(_WIN64) + } +#endif //defined(ENABLE_CONTRACTS_IMPL) + + // private new + static void *operator new(size_t size, void *pv) { LIMITED_METHOD_CONTRACT; return(pv); } + + // Private data + CLREvent *_hWriterEvent; + CLREvent *_hReaderEvent; + OBJECTHANDLE _hObjectHandle; + Volatile<LONG> _dwState; + LONG _dwULockID; + LONG _dwLLockID; + DWORD _dwWriterID; + DWORD _dwWriterSeqNum; + WORD _wWriterLevel; +#ifdef RWLOCK_STATISTICS + // WARNING: You must explicitly #define RWLOCK_STATISTICS when you build + // in both the VM and BCL directories, as the managed class must also + // contain these fields! + Volatile<LONG> _dwReaderEntryCount; + Volatile<LONG> _dwReaderContentionCount; + Volatile<LONG> _dwWriterEntryCount; + Volatile<LONG> _dwWriterContentionCount; + Volatile<LONG> _dwEventsReleasedCount; +#endif + + // Static data + static Volatile<LONGLONG> s_mostRecentLockID; + static CrstStatic s_RWLockCrst; +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF<CRWLock> RWLOCKREF; + +#else +typedef CRWLock* RWLOCKREF; +#endif + +#endif // _RWLOCK_H_ + +#endif // FEATURE_RWLOCK + |