private void LockProxy_Disposed(object sender, EventArgs e) { var disposingProxy = (DistributedLockProxy)sender; var lockKey = disposingProxy.Name; // Only remove the proxy if the one we're disposing is the one in our collection for that key. DistributedLockProxy actualProxy; if (_proxies.TryRemove(lockKey, out actualProxy)) { if (ReferenceEquals(actualProxy, disposingProxy)) { //good, the object we got is us so we are the one true proxy. disposingProxy.Disposed -= LockProxy_Disposed; } else { //ruh roh; it wasn't us - we need to put that back in. DistributedLockProxy errantProxy = null; Debug.Write(string.Format("Lock proxy for lock {0} is not our object, re-inserting", lockKey)); _proxies.AddOrUpdate(lockKey, actualProxy, (key, proxy) => { errantProxy = proxy; return(actualProxy); }); //we really should merge proxies.. errantProxy.Dispose(); } } else { //we're somehow a dangling proxy; still release our delegate reference so we don't leak. Debug.Write(string.Format("Lock proxy for lock {0} was not in the proxies dictionary.", lockKey)); disposingProxy.Disposed -= LockProxy_Disposed; } }
/// <summary> /// Attempt to lock the repository with the provided index path. /// </summary> /// <param name="requester">The object that is requesting the lock (useful for debugging purposes)</param> /// <param name="name">The name of the lock to get within the current scope</param> /// <param name="timeoutSeconds">The maximum number of seconds to wait on the lock before giving up.</param> /// <param name="grantedLock">The disposable Lock object if the lock was acquired.</param> /// <returns>True if the lock was acquired or false if the lock timed out.</returns> public bool TryLock(object requester, string name, int timeoutSeconds, out DistributedLock grantedLock) { if (requester == null) { throw new ArgumentNullException(nameof(requester)); } if (name == null) { throw new ArgumentNullException(nameof(name)); } grantedLock = null; var candidateLock = new DistributedLock(requester, name, timeoutSeconds); // Lookup or create the proxy for the requested lock. var lockProxy = _proxies.GetOrAdd(candidateLock.Name, (key) => { var newProxy = new DistributedLockProxy(_provider, key); newProxy.Disposed += LockProxy_Disposed; return(newProxy); }); lock (lockProxy) { try { // Does the current thread already hold the lock? (If it was still waiting on it, we couldn't get here.) var currentTurnLockId = lockProxy.CheckCurrentTurnThread(candidateLock); if (currentTurnLockId != null && DistributedLockManager.CurrentLockId == currentTurnLockId && candidateLock.ActualLock != null) { Debug.Write(string.Format("Existing Lock Already Acquired: {0}-{1}", Name, name)); grantedLock = candidateLock; // It's a secondary lock, so we don't need to queue it or wait. return(true); } // Or is the lock currently held by another thread that we don't want to wait for? if (currentTurnLockId != null && candidateLock.WaitForLock == false) { Debug.Write(string.Format("Unable to Acquire Lock (can't wait): {0}-{1}", Name, name)); candidateLock.Dispose(); // We don't want to wait for it, so don't bother queuing an expired request. return(false); // Just fail out. } lockProxy.QueueRequest(candidateLock); // Otherwise, queue the request inside the lock to keep the proxy around. } finally { Monitor.Pulse(lockProxy); //to get whoever's waiting a kick in the pants. } } // Now we have the proxy and our request is queued. Make sure some thread is trying to get the file lock. bool ourTurn = false; // Assume false. try { ourTurn = lockProxy.AwaitOurTurnOrTimeout(candidateLock); } finally { if (ourTurn == false) { // We have to make sure this gets disposed if we didn't get the lock, even if a ThreadAbortException occurs. candidateLock.Dispose(); // Bummer, we didn't get it. Probably already disposed, but safe to do again. candidateLock = null; // Clear it out to report the failure. } } Debug.Write(string.Format(candidateLock == null ? "Unable to Acquire Lock: {0}-{1}" : "Lock Acquired: {0}-{1}", Name, name)); grantedLock = candidateLock; return(grantedLock != null); }