Exemplo n.º 1
0
        private int DequeueNextRequest()
        {
            lock (m_QueueLock)
            {
                int dequeueCount = 0;

                // Make sure we don't thread-abort in the middle of this logic.
                try
                {
                }
                finally
                {
                    while (m_CurrentLockTurn == null && m_WaitQueue.Count > 0)
                    {
                        m_CurrentLockTurn = m_WaitQueue.Dequeue();
                        dequeueCount++;

                        if (m_CurrentLockTurn.IsExpired)
                        {
                            m_CurrentLockTurn.Dispose(); // There's no one waiting on that request, so just discard it.
                            m_CurrentLockTurn = null;    // Get the next one (if any) on next loop.
                        }
                        else
                        {
                            m_CurrentLockTurn.Disposed += Lock_Disposed; // Subscribe to their Disposed event.  Now we care.
                        }
                    }
                }

                return(dequeueCount);
            }
        }
        private async void RepositoryPublishMain()
        {
            //before we get going, lets stall for a few seconds.  We aren't a critical operation, and I don't
            //want to get in the way of the application starting up.
            if (BackgroundStartupDelay > 0)
            {
                if (!Log.SilentMode)
                {
                    Log.Write(LogMessageSeverity.Information, LogCategory,
                              "Waiting for startup delay to start publisher",
                              "To avoid competing against other startup activities we're going to delay for {0} before we start.",
                              BackgroundStartupDelay);
                }

                await Task.Delay(BackgroundStartupDelay).ConfigureAwait(false);
            }

            InterprocessLock backgroundLock = null; //we can't do our normal using trick in this situation.

            try
            {
                //we have two totally different modes:  Either WE'RE the background processor or someone else is.
                //if we are then we move on to start publishing.  If someone else is then we just poll
                //the lock to see if whoever owned it has exited.
                backgroundLock = GetLock(0);
                while ((backgroundLock == null) && (m_StopRequested == false))
                {
                    //we didn't get the lock - so someone else is currently the main background thread.
                    SleepUntilNextCheck(m_MultiprocessLockCheckInterval);
                    backgroundLock = GetLock(0);
                }

                //if we got the lock then we want to go ahead and perform background processing.
                if (backgroundLock != null)
                {
                    //here is where we want to keep looping - it will return every time the subscription changes.
                    await RepositoryPublishLoop().ConfigureAwait(false);

                    //release the lock; we'll get it on the next round.
                    backgroundLock.Dispose();
                    backgroundLock = null;
                }
            }
            catch (Exception ex)
            {
                GC.KeepAlive(ex);
            }
            finally
            {
                if (backgroundLock != null)
                {
                    backgroundLock.Dispose();
                }

                lock (m_SessionPublishThreadLock)
                {
                    //clear the dispatch thread variable since we're about to exit.
                    m_SessionPublishThread = null;

                    //we want to write out that we had a problem and mark that we're failed so we'll get restarted.
                    m_SessionPublishThreadFailed = true;

                    System.Threading.Monitor.PulseAll(m_SessionPublishThreadLock);
                }

                if (!Log.SilentMode)
                {
                    Log.Write(LogMessageSeverity.Information, LogCategory, "Background session publisher thread has stopped", null);
                }
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Performs the actual releasing of managed and unmanaged resources.
        /// Most usage should instead call Dispose(), which will call Dispose(true) for you
        /// and will suppress redundant finalization.
        /// </summary>
        /// <param name="releaseManaged">Indicates whether to release managed resources.
        /// This should only be called with true, except from the finalizer which should call Dispose(false).</param>
        private void Dispose(bool releaseManaged)
        {
            if (releaseManaged)
            {
                // Free managed resources here (normal Dispose() stuff, which should itself call Dispose(true))
                // Other objects may be referenced in this case

                lock (m_QueueLock)
                {
                    if (!m_Disposed)
                    {
                        m_Disposed = true; // Make sure we don't do it more than once.

                        // Empty our queue (although it should already be empty!).
                        while (m_WaitQueue.Count > 0)
                        {
                            InterprocessLock lockInstance = m_WaitQueue.Dequeue();
                            //lockInstance.Disposed -= Lock_Disposed; // Suppress the events, don't start new turns!
                            lockInstance.Dispose(); // Tell any threads still waiting that their request has expired.
                        }

                        if (m_CurrentLockTurn == null)
                        {
                            // No thread is currently prepared to do this, so clear them here.
                            if (m_LockRequest != null)
                            {
                                m_LockRequest.Dispose();
                                m_LockRequest = null;
                            }

                            if (m_FileLock != null)
                            {
                                m_FileLock.Dispose();
                                m_FileLock = null;
                            }
                        }

                        // We're not fully disposed until the current lock owner gets disposed so we can release the lock.
                        // But fire the event to tell the RepositoryLockManager that we are no longer a valid proxy.
                        OnDispose();
                    }
                }
            }
            else
            {
                // Free native resources here (alloc's, etc)
                // May be called from within the finalizer, so don't reference other objects here

                // But we need to make sure the file opens get cleaned up, so we will anyway if we have to.... ???
                if (m_LockRequest != null)
                {
                    m_LockRequest.Dispose();
                    m_LockRequest = null;
                }

                if (m_FileLock != null)
                {
                    m_FileLock.Dispose();
                    m_FileLock = null;
                }
            }
        }
Exemplo n.º 4
0
        /// <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);
        }
Exemplo n.º 5
0
        /// <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);
        }