static void LockProxy_Disposed(object sender, EventArgs e) { InterprocessLockProxy disposingProxy = (InterprocessLockProxy)sender; lock (g_Lock) { string lockKey = disposingProxy.FullName; InterprocessLockProxy actualProxy; // Only remove the proxy if the one we're disposing is the one in our collection for that key. if (g_Proxies.TryGetValue(lockKey, out actualProxy) && ReferenceEquals(actualProxy, disposingProxy)) { g_Proxies.Remove(lockKey); } System.Threading.Monitor.PulseAll(g_Lock); } }
/// <summary> /// Query whether a particular lock is available without holding on to it. /// </summary> /// <param name="requester">The object that is querying the lock (useful for debugging purposes)</param> /// <param name="indexPath">The fully qualified path to the directory containing the index file of the repository</param> /// <param name="lockName">The name of the lock to query (locks are a combination of index and this name)</param> /// <returns>True if the lock could have been obtained. False if the lock could not be obtained without waiting.</returns> public static bool QueryLockAvailable(object requester, string indexPath, string lockName) { string fileName = InterprocessLockProxy.GetLockFileName(indexPath, lockName); if (System.IO.File.Exists(fileName) == false) { return(true); // Lock file didn't exist, so we could have obtained it. } bool lockAvailable; using (InterprocessLock attemptedLock = Lock(requester, indexPath, lockName, 0, true)) { lockAvailable = (attemptedLock != null); } return(lockAvailable); }
/// <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="indexPath">The fully qualified path to the directory containing the index file of the repository</param> /// <param name="lockName">The name of the lock to get (locks are a combination of index and this name)</param> /// <param name="timeoutSeconds">The maximum number of seconds to wait on the lock before giving up.</param> /// <param name="deleteOnClose">Whether the lock file should be deleted on close or left around for reuse.</param> /// <returns>A Repository Lock object if the lock could be obtained or Null if the lock timed out.</returns> public static InterprocessLock Lock(object requester, string indexPath, string lockName, int timeoutSeconds, bool deleteOnClose) { if (requester == null) { throw new ArgumentNullException(nameof(requester)); } if (indexPath == null) { throw new ArgumentNullException(nameof(indexPath)); } if (lockName == null) { throw new ArgumentNullException(nameof(lockName)); } InterprocessLock candidateLock = new InterprocessLock(requester, indexPath, lockName, timeoutSeconds); // Lookup or create the proxy for the requested lock. InterprocessLockProxy lockProxy; lock (g_Lock) { if (g_Proxies.TryGetValue(candidateLock.FullName, out lockProxy) == false) { // Didn't exist, need to make one. lockProxy = new InterprocessLockProxy(indexPath, lockName, deleteOnClose); #if DEBUG if (string.Equals(lockProxy.FullName, candidateLock.FullName, StringComparison.OrdinalIgnoreCase) == false) { throw new InvalidOperationException("Proxy generated a different full name than the candidate lock."); } #endif lockProxy.Disposed += LockProxy_Disposed; g_Proxies.Add(lockProxy.FullName, lockProxy); } // Does the current thread already hold the lock? (If it was still waiting on it, we couldn't get here.) Thread currentTurnThread = lockProxy.CheckCurrentTurnThread(candidateLock); if (Thread.CurrentThread == currentTurnThread && candidateLock.ActualLock != null) { return(candidateLock); // It's a secondary lock, so we don't need to queue it or wait. } // Or is the lock currently held by another thread that we don't want to wait for? if (currentTurnThread != null && candidateLock.WaitForLock == false) { candidateLock.Dispose(); // We don't want to wait for it, so don't bother queuing an expired request. return(null); // Just fail out. } lockProxy.QueueRequest(candidateLock); // Otherwise, queue the request inside the lock to keep the proxy around. } // 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. } } // Otherwise... yay, we got it! return(candidateLock); }