Example #1
0
        /// <remarks>
        ///     Not thread safe; ensure this is invoked only by the timer within this class.
        /// </remarks>
        private void MonitorWaits(object sender, object e)
        {
            foreach (var record in Waits)
            {
                // a lock should always be available or added prior to a wait; if not we'll take the null ref exception
                // that would follow. it should be impossible to hit this so a catastrophic failure is appropriate.
                Locks.TryGetValue(record.Key, out var recordLock);

                // enter a read lock first; TryPeek and TryDequeue are atomic so there's no risky operation until later.
                recordLock.EnterUpgradeableReadLock();

                try
                {
                    if (record.Value.TryPeek(out var nextPendingWait))
                    {
                        if (nextPendingWait.CancellationToken != null && ((CancellationToken)nextPendingWait.CancellationToken).IsCancellationRequested)
                        {
                            if (record.Value.TryDequeue(out var cancelledWait))
                            {
                                cancelledWait.TaskCompletionSource.SetException(new OperationCanceledException("The wait was cancelled."));
                            }
                        }
                        else if (nextPendingWait.DateTime.AddSeconds(nextPendingWait.TimeoutAfter) < DateTime.UtcNow && record.Value.TryDequeue(out var timedOutWait))
                        {
                            timedOutWait.TaskCompletionSource.SetException(new TimeoutException($"The wait timed out after {timedOutWait.TimeoutAfter} seconds."));
                        }
                    }

                    if (record.Value.IsEmpty)
                    {
                        // enter the write lock to prevent Wait() (which obtains a read lock) from enqueing any more waits
                        // before we can delete the dictionary record
                        recordLock.EnterWriteLock();

                        try
                        {
                            // check the queue again to ensure Wait() didn't enqueue anything between the last check and when we
                            // entered the write lock.  this is guarateed to be safe since we now have exclusive access to the record
                            if (record.Value.IsEmpty)
                            {
                                Waits.TryRemove(record.Key, out _);
                                Locks.TryRemove(record.Key, out _);
                            }
                        }
                        finally
                        {
                            recordLock.ExitWriteLock();
                        }
                    }
                }
                finally
                {
                    recordLock.ExitUpgradeableReadLock();
                }
            }
        }
Example #2
0
        private void Disposition(WaitKey key, Action <PendingWait> action)
        {
            if (Waits.TryGetValue(key, out var queue) && queue.TryDequeue(out var wait))
            {
                action(wait);
                wait.Dispose();

                if (Locks.TryGetValue(key, out var recordLock))
                {
                    // enter a read lock first; TryPeek and TryDequeue are atomic so there's no risky operation until later.
                    recordLock.EnterUpgradeableReadLock();

                    try
                    {
                        // clean up entries in the Waits and Locks dictionaries if the corresponding ConcurrentQueue is empty.
                        // this is tricky, because we don't want to remove a record if another thread is in the process of
                        // enqueueing a new wait.
                        if (queue.IsEmpty)
                        {
                            // enter the write lock to prevent Wait() (which obtains a read lock) from enqueing any more waits
                            // before we can delete the dictionary record. it's ok and expected that Wait() might add this record
                            // back to the dictionary as soon as this unblocks; we're preventing new waits from being discarded if
                            // they are added by another thread just prior to the TryRemove() operation below.
                            recordLock.EnterWriteLock();

                            try
                            {
                                // check the queue again to ensure Wait() didn't enqueue anything between the last check and when
                                // we entered the write lock. this is guarateed to be safe since we now have exclusive access to
                                // the record and it should be impossible to remove a record containing a non-empty queue
                                if (queue.IsEmpty)
                                {
                                    Waits.TryRemove(key, out _);
                                    Locks.TryRemove(key, out _);
                                }
                            }
                            finally
                            {
                                recordLock.ExitWriteLock();
                            }
                        }
                    }
                    finally
                    {
                        recordLock.ExitUpgradeableReadLock();
                    }
                }
            }
        }
Example #3
0
        public static void SaveLog(string testName, string log)
        {
            if (!Locks.TryGetValue(testName, out var lockObject))
            {
                lockObject = new object();
                if (!Locks.TryAdd(testName, lockObject))
                {
                    lockObject = Locks[testName];
                }
            }

            lock (lockObject)
            {
                var filePath = System.IO.Path.Combine(Path, testName + ".txt");
                Directory.CreateDirectory(Path);
                File.AppendAllLines(filePath, new[] { log });
            }
        }
Example #4
0
 public void BeginGuardedOperation()
 {
     lock (sync)
     {
         if (lockID == Guid.Empty)
         {
             throw new InvalidOperationException("Guarded operation " +
                                                 "was blocked because no lock has been obtained.");
         }
         object currentLock;
         Locks.TryGetValue(lockID, out currentLock);
         if (currentLock != SlotMarker)
         {
             throw new InvalidOperationException("Guarded operation " +
                                                 "was blocked because the lock was obtained on a " +
                                                 "different thread from the calling thread.");
         }
     }
 }
Example #5
0
 private void ReleaseLock()
 {
     lock (sync)
     {
         if (lockID == Guid.Empty)
         {
             throw new InvalidOperationException("This instance cannot " +
                                                 "be unlocked because no lock currently exists.");
         }
         object currentLock;
         Locks.TryGetValue(lockID, out currentLock);
         if (currentLock == SlotMarker)
         {
             Locks.Remove(lockID);
             lockID = Guid.Empty;
         }
         else
         {
             throw new InvalidOperationException("Unlock must be invoked " +
                                                 "from same thread that invoked Lock.");
         }
     }
 }