// Funnel all delegates through here to ensure proper procedure for getting and releasing locks. private bool Execute(TKey key, LockType type, Action <ReaderWriterLockSlim> closure, int?millisecondsTimeout = null, bool throwsOnTimeout = false) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (closure == null) { throw new ArgumentNullException(nameof(closure)); } ReaderWriterLockSlimExensions.ValidateMillisecondsTimeout(millisecondsTimeout); Contract.EndContractBlock(); var context = ContextPool.Take(); var rwlock = GetLock(key, type, context, millisecondsTimeout, throwsOnTimeout); if (rwlock == null) { return(false); } try { closure(rwlock.Lock); } finally { try { ReleaseLock(rwlock.Lock, type); rwlock.Clear(context); ContextPool.Give(context); } catch (Exception ex) { ex.WriteToDebug(); // The above cannot fail or dire concequences... Debugger.Break(); throw; } } return(true); }
private bool AcquireLock(ReaderWriterLockSlim target, LockType type, int?millisecondsTimeout = null, bool throwsOnTimeout = false) { if (target == null) { throw new ArgumentNullException(nameof(target)); } ReaderWriterLockSlimExensions.ValidateMillisecondsTimeout(millisecondsTimeout); Contract.EndContractBlock(); switch (type) { case LockType.Read: return(target.EnterReadLock(millisecondsTimeout, throwsOnTimeout)); case LockType.ReadUpgradeable: return(target.EnterUpgradeableReadLock(millisecondsTimeout, throwsOnTimeout)); case LockType.Write: return(target.EnterWriteLock(millisecondsTimeout, throwsOnTimeout)); } return(false); }
private ReaderWriterLockTracker GetLock(TKey key, LockType type, object context, int?millisecondsTimeout = null, bool throwsOnTimeout = false) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } ReaderWriterLockSlimExensions.ValidateMillisecondsTimeout(millisecondsTimeout); Contract.EndContractBlock(); if (IsDisposed) { return(null); } // Need to be able to enter a lock before releasing access in order to prevent removal... var r = CleanupManager.ReadValue( () => { // It is possible that a read could be acquired while disposing just before the dispose. if (IsDisposed) { return(null); } // Get a tracker... ReaderWriterLockTracker result; { // Compare the tracker retrieved with the one created... ReaderWriterLockTracker created = null; do { result = Locks.GetOrAdd(key, k => created = LockPool.Take()); } // Safeguard against rare case of when a disposed tracker is retained via an exception (possibly?). :( while (!IsDisposed && result.IsDisposed); // If the one created is not the one retrieved, go ahead and add it to the pool so it doesn't go to waste. if (created != null && created != result) { if (IsDisposed) { created.Dispose(); } else { LockPool.Give(created); } } // This should never get out of sync, but just in case... var rlock = result.Lock; if (rlock == null || result.IsDisposed) { Debug.Fail("A lock tracker was retained after it was disposed."); return(null); } else if (Debugger.IsAttached && rlock.RecursionPolicy == LockRecursionPolicy.NoRecursion) { if (rlock.IsWriteLockHeld && type == LockType.Read) { Debugger.Break(); // } } } // Quick check to avoid further processes... if (IsDisposed) { return(null); } var lockHeld = false; if (!result.Reserve(context)) { return(null); } try { // result.Lock will only be null if the tracker has been disposed. lockHeld = AcquireLock(result.Lock, type, millisecondsTimeout, throwsOnTimeout); } catch (LockRecursionException lrex) { lrex.WriteToDebug(); Debugger.Break(); // Need to be able to track down source. throw; } finally { if (!lockHeld) { result.Clear(context); } } return(lockHeld ? result : null); }); // In the rare case that a dispose could be initiated during this ReadValue: // We need to not propagate locking... if (r == null || !IsDisposed) { return(r); } ReleaseLock(r.Lock, type); r.Clear(context); return(null); }