internal void GrantTheLock(DistributedLock actualLock) { lock (_myLock) { try { if (actualLock != null && actualLock.IsDisposed == false && actualLock.OwningLockId == _owningLockId && string.Equals(actualLock.Name, Name, StringComparison.OrdinalIgnoreCase)) { // We don't need to lock around this because we're bypassing the proxy's queue and staying only on our own thread. _actualLock = actualLock; _waitTimeout = DateTimeOffset.MaxValue; // We have a lock (sort of), so reset our timeout to forever. } else { // It's an invalid call, so make sure our setting is cleared out. _actualLock = null; throw new InvalidOperationException("Can't set a secondary lock from an invalid actual lock."); } } finally { Monitor.PulseAll(_myLock); } } }
internal DistributedLock(object requester, string name, int timeoutSeconds) { _owningLockId = DistributedLockManager.CurrentLockId; _owningObject = requester; Name = name; _actualLock = null; _myTurn = false; _waitForLock = (timeoutSeconds > 0); _waitTimeout = _waitForLock ? DateTimeOffset.Now.AddSeconds(timeoutSeconds) : DateTimeOffset.Now; }
/// <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); }