ReaderWriterLockTracker Factory() { var created = new ReaderWriterLockTracker(RecursionPolicy); if (Debugger.IsAttached) { created.BeforeDispose += Debug_TrackerDisposedWhileInUse; } return(created); }
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); }