/// <summary> /// Check the thread with the current turn for the lock and grant a secondary lock if applicable. /// </summary> /// <param name="candidateLock">An unexpired lock request on the current thread, or null to just check the turn thread.</param> /// <returns>The Thread with the current turn for the lock, or null if there are none holding or waiting.</returns> internal Thread CheckCurrentTurnThread(InterprocessLock candidateLock) { if (candidateLock != null && candidateLock.OwningThread != Thread.CurrentThread) { throw new InvalidOperationException("A lock request may only be waited on by the thread which created it."); } lock (m_QueueLock) { if (m_CurrentLockTurn != null) { Thread currentOwningThread = m_CurrentLockTurn.OwningThread; if (candidateLock != null && Thread.CurrentThread == currentOwningThread) { candidateLock.GrantTheLock(m_CurrentLockTurn); // Set it as a secondary lock on that holder (same thread). if (candidateLock.ActualLock == m_CurrentLockTurn) // Sanity-check that it was successful. { candidateLock.OurLockProxy = this; // So its dispose-on-close setting pass-through can function. } } return(currentOwningThread); // Whether it's a match or some other thread. } return(null); // No thread owns the lock. } }
/// <summary> /// Wait for our turn to have the lock (and wait for the lock) up to our time limit /// </summary> /// <param name="lockRequest"></param> /// <returns></returns> internal bool AwaitOurTurnOrTimeout(InterprocessLock lockRequest) { if (lockRequest.IsExpired) { throw new InvalidOperationException("Can't wait on an expired lock request."); } if (string.Equals(lockRequest.FullName, m_LockFullFileNamePath, StringComparison.OrdinalIgnoreCase) == false) { throw new InvalidOperationException("A lock request may not be queued to a proxy for a different full name."); } if (lockRequest.OwningThread != Thread.CurrentThread) { throw new InvalidOperationException("A lock request may only be waited on by the thread which created it."); } lockRequest.OurLockProxy = this; // Mark the request as pending with us. // Do NOT clear out current lock owner, this will allow DequeueNextRequest to find one already there, if any. bool ourTurn = StartNextTurn(lockRequest); // Gets its own queue lock. if (ourTurn == false) { // It's not our turn yet, we need to wait our turn. Are we willing to wait? if (lockRequest.WaitForLock && lockRequest.WaitTimeout > DateTimeOffset.Now) { ourTurn = lockRequest.AwaitTurnOrTimeout(); } // Still not our turn? if (ourTurn == false) { if (!CommonCentralLogic.SilentMode) { // Who actually has the lock right now? if (m_CurrentLockTurn != null) { Thread currentOwningThread = m_CurrentLockTurn.OwningThread; int currentOwningThreadId = -1; string currentOwningThreadName = "null"; if (currentOwningThread != null) // To make sure we can't get a null-ref exception from logging this... { currentOwningThreadId = currentOwningThread.ManagedThreadId; currentOwningThreadName = currentOwningThread.Name ?? string.Empty; } m_Logger.LogDebug("{0}\r\nA lock request gave up because it is still being held by another thread.\r\n" + "Lock file: {1}\r\nCurrent holding thread: {2} ({3})", lockRequest.WaitForLock ? "Lock request timed out" : "Lock request couldn't wait", m_LockFullFileNamePath, currentOwningThreadId, currentOwningThreadName); } else { m_Logger.LogError("Lock request turn error\r\nA lock request failed to get its turn but the current lock turn is null. " + "This probably should not happen.\r\nLock file: {0}\r\n", m_LockFullFileNamePath); } } lockRequest.Dispose(); // Expire the request. return(false); // Failed to get the lock. Time to give up. } } // Yay, now it's our turn! Do we already hold the lock? bool validLock; if (m_FileLock != null) { validLock = true; // It's our request's turn and this proxy already holds the lock! } else { validLock = TryGetLock(lockRequest); // Can we get the lock? } // Do we actually have the lock now? if (validLock) { lockRequest.GrantTheLock(lockRequest); // It owns the actual lock itself now. } else { if (!CommonCentralLogic.SilentMode) { m_Logger.LogTrace("{0}\r\nA lock request gave up because it could not obtain the file lock. " + "It is most likely still held by another process.\r\nLock file: {1}", lockRequest.WaitForLock ? "Lock request timed out" : "Lock request couldn't wait", m_LockFullFileNamePath); } lockRequest.Dispose(); // Failed to get the lock. Expire the request and give up. } return(validLock); }