Пример #1
0
        /// <summary>
        /// Slow path to peek the item from queue (uses <see cref="_peekMonitor"/>)
        /// </summary>
        /// <param name="item">The item removed from queue</param>
        /// <param name="timeoutTracker">Timeout tracker</param>
        /// <param name="token">Token</param>
        /// <returns>True if the item was removed</returns>
        private bool TryPeekSlow(out T item, TimeoutTracker timeoutTracker, CancellationToken token)
        {
            if (timeoutTracker.OriginalTimeout != 0 && _peekMonitor.WaiterCount > 0)
            {
                Thread.Yield();
            }

            if (_peekMonitor.WaiterCount == 0 || Volatile.Read(ref _itemCount) > 0)
            {
                if (TryPeekFast(out item))
                {
                    return(true);
                }
            }

            using (var waiter = _peekMonitor.Enter(timeoutTracker.RemainingMilliseconds, token))
            {
                if (TryPeekFast(out item))
                {
                    return(true);
                }

                while (!waiter.IsTimeouted)
                {
                    waiter.Wait(WaitPollingTimeout);
                    if (TryPeekFast(out item))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
Пример #2
0
        public void TestCustomTimeoutWorks()
        {
            using (var testInst = new MonitorObject())
            {
                int result = 0;
                var task   = Task.Run(() =>
                {
                    using (var waiter = testInst.Enter(60000))
                    {
                        if (waiter.Wait(100))
                        {
                            Interlocked.Exchange(ref result, 1);
                        }
                        else
                        {
                            Interlocked.Exchange(ref result, 2);
                        }
                    }
                });

                TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount);
                TimingAssert.AreEqual(10000, 2, () => Volatile.Read(ref result));

                task.Wait();
            }
        }
Пример #3
0
        public void TestNotificationWithPredicateRef()
        {
            using (var testInst = new MonitorObject())
            {
                int result = 0;
                int state  = 0;
                var task   = Task.Run(() =>
                {
                    using (var waiter = testInst.Enter(60000))
                    {
                        if (waiter.Wait((ref int s) => Volatile.Read(ref s) > 0, ref state))
                        {
                            Interlocked.Exchange(ref result, 1);
                        }
                        else
                        {
                            Interlocked.Exchange(ref result, 2);
                        }
                    }
                });

                TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount);
                Assert.AreEqual(0, Volatile.Read(ref result));

                testInst.Pulse();
                Thread.Sleep(100);
                Assert.AreEqual(0, Volatile.Read(ref result));

                Interlocked.Increment(ref state);
                testInst.Pulse();
                TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref result));

                task.Wait();
            }
        }
Пример #4
0
        /// <summary>
        /// Slow path to add the item (waits on <see cref="_addMonitor"/>)
        /// </summary>
        /// <param name="item">New item</param>
        /// <param name="timeoutTracker">Timeout tracker</param>
        /// <param name="token">Token</param>
        /// <returns>Was added sucessufully</returns>
        private bool TryAddSlow(T item, TimeoutTracker timeoutTracker, CancellationToken token)
        {
            if (timeoutTracker.OriginalTimeout != 0 && _addMonitor.WaiterCount > 0)
            {
                Thread.Yield();
                if (_addMonitor.WaiterCount == 0 && TryAddFast(item))
                {
                    return(true);
                }
            }

            using (var waiter = _addMonitor.Enter(timeoutTracker.RemainingMilliseconds, token))
            {
                if (TryAddFast(item))
                {
                    return(true);
                }

                while (!waiter.IsTimeouted)
                {
                    waiter.Wait(WaitPollingTimeout);
                    if (TryAddFast(item))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
Пример #5
0
            public bool TryAdd(T value, int timeout, CancellationToken token)
            {
                bool result = false;

                using (var waiter = WaiterFull.Enter(timeout, token))
                {
                    if (waiter.Wait(s => { Assert.IsTrue(Monitor.IsEntered(s.WaiterFull)); return(s.ItemCount < s.MaxCount); }, this))
                    {
                        Assert.IsTrue(Monitor.IsEntered(WaiterFull));
                        Queue.Enqueue(value);
                        Interlocked.Increment(ref ItemCount);
                        result = true;
                    }
                    else
                    {
                        Assert.IsTrue(Monitor.IsEntered(WaiterFull));
                        result = false;
                    }
                }

                if (result)
                {
                    WaiterEmpty.Pulse();
                }


                return(result);
            }
Пример #6
0
            public bool TryTake(out T value, int timeout, CancellationToken token)
            {
                bool result = false;

                using (var waiter = WaiterEmpty.Enter(timeout, token))
                {
                    if (waiter.Wait(s => { Assert.IsTrue(Monitor.IsEntered(s.WaiterEmpty)); return(s.ItemCount > 0); }, this))
                    {
                        Assert.IsTrue(Monitor.IsEntered(WaiterEmpty));
                        bool dRes = Queue.TryDequeue(out value);
                        Assert.IsTrue(dRes);
                        Interlocked.Decrement(ref ItemCount);
                        result = true;
                    }
                    else
                    {
                        Assert.IsTrue(Monitor.IsEntered(WaiterEmpty));
                        value  = default(T);
                        result = false;
                    }
                }

                if (result)
                {
                    WaiterFull.Pulse();
                }

                return(result);
            }
Пример #7
0
        public void TestInterruptOnDispose()
        {
            using (var testInst = new MonitorObject())
            {
                int exitCount = 0;
                var task      = Task.Run(() =>
                {
                    try
                    {
                        using (var waiter = testInst.Enter())
                        {
                            waiter.Wait(_ => false, (object)null);
                        }
                    }
                    catch (OperationInterruptedException) { }
                    Interlocked.Increment(ref exitCount);
                });


                TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount);

                testInst.Dispose();
                TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref exitCount));

                task.Wait();
            }
        }
Пример #8
0
        public void TestAllThreadWakeUpOnSignalAll()
        {
            using (var testInst = new MonitorObject())
            {
                int         exitCount = 0;
                int         state     = 0;
                List <Task> tasks     = new List <Task>();
                for (int i = 0; i < 6; i++)
                {
                    var task = Task.Run(() =>
                    {
                        using (var waiter = testInst.Enter())
                        {
                            waiter.Wait(_ => { return(Volatile.Read(ref state) > 0); }, (object)null);
                            Interlocked.Increment(ref exitCount);
                        }
                    });
                    tasks.Add(task);
                }


                TimingAssert.AreEqual(10000, 6, () => testInst.WaiterCount);
                Interlocked.Increment(ref state);

                testInst.PulseAll();
                TimingAssert.AreEqual(10000, 0, () => testInst.WaiterCount);
                TimingAssert.AreEqual(10000, 6, () => Volatile.Read(ref exitCount));

                Task.WaitAll(tasks.ToArray());
            }
        }
Пример #9
0
        public void TestNotificationReceived()
        {
            using (var testInst = new MonitorObject())
            {
                int result = 0;
                var task   = Task.Run(() =>
                {
                    using (var waiter = testInst.Enter(60000))
                    {
                        if (waiter.Wait())
                        {
                            Interlocked.Exchange(ref result, 1);
                        }
                        else
                        {
                            Interlocked.Exchange(ref result, 2);
                        }
                    }
                });

                TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount);
                Assert.AreEqual(0, Volatile.Read(ref result));

                testInst.Pulse();
                TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref result));

                task.Wait();
            }
        }
Пример #10
0
        public void TestCancellationWorksInWaitNoParam()
        {
            using (var testInst = new MonitorObject())
            {
                int result = 0;
                CancellationTokenSource tokenSrc = new CancellationTokenSource();
                var task = Task.Run(() =>
                {
                    try
                    {
                        using (var waiter = testInst.Enter(60000, tokenSrc.Token))
                        {
                            while (!waiter.Wait())
                            {
                            }
                            Interlocked.Exchange(ref result, 1);
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        Interlocked.Exchange(ref result, 3);
                    }
                });

                testInst.Pulse();

                Thread.Sleep(100);
                Assert.AreEqual(0, Volatile.Read(ref result));

                tokenSrc.Cancel();
                TimingAssert.AreEqual(10000, 3, () => Volatile.Read(ref result));
            }
        }
Пример #11
0
        /// <summary>
        /// Slow path to take the item from queue (uses <see cref="_takeMonitor"/>)
        /// </summary>
        /// <param name="item">The item removed from queue</param>
        /// <param name="timeoutTracker">Timeout tracker</param>
        /// <param name="token">Token</param>
        /// <returns>True if the item was removed</returns>
        private bool TryTakeSlow(out T item, TimeoutTracker timeoutTracker, CancellationToken token)
        {
            bool canTakeConcurrently = _takeMonitor.WaiterCount == 0 || Volatile.Read(ref _itemCount) > _takeMonitor.WaiterCount + PlatformHelper.ProcessorCount;

            if (timeoutTracker.OriginalTimeout != 0 && !canTakeConcurrently)
            {
                Thread.Yield();
                canTakeConcurrently = _takeMonitor.WaiterCount == 0 || Volatile.Read(ref _itemCount) > _takeMonitor.WaiterCount + PlatformHelper.ProcessorCount;
            }

            if (canTakeConcurrently && TryTakeFast(out item))
            {
                return(true);
            }

            using (var waiter = _takeMonitor.Enter(timeoutTracker.RemainingMilliseconds, token))
            {
                if (TryTakeFast(out item))
                {
                    return(true);
                }

                while (!waiter.IsTimeouted)
                {
                    waiter.Wait(WaitPollingTimeout);
                    if (TryTakeFast(out item))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
Пример #12
0
        public void TestIsTimeoutedWorks()
        {
            using (var testInst = new MonitorObject())
            {
                int result     = 0;
                int cycleCount = 0;
                var task       = Task.Run(() =>
                {
                    using (var waiter = testInst.Enter(200))
                    {
                        while (!waiter.IsTimeouted)
                        {
                            waiter.Wait(10);
                            Interlocked.Increment(ref cycleCount);
                        }
                        Interlocked.Exchange(ref result, 1);
                    }
                });

                TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount);
                TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref result));
                Assert.IsTrue(Volatile.Read(ref cycleCount) > 0);

                task.Wait();
            }
        }
Пример #13
0
 public void TestLockEnterExit()
 {
     using (var inst = new MonitorObject())
     {
         Assert.AreEqual(0, inst.WaiterCount);
         using (var wait = inst.Enter())
         {
             Assert.AreEqual(1, inst.WaiterCount);
             Assert.IsTrue(Monitor.IsEntered(inst));
         }
         Assert.AreEqual(0, inst.WaiterCount);
     }
 }
Пример #14
0
        public void TestExceptionFromPredicatePassed()
        {
            using (var testInst = new MonitorObject())
            {
                int result = 0;
                int state  = 0;
                var task   = Task.Run(() =>
                {
                    using (var waiter = testInst.Enter(60000))
                    {
                        if (waiter.Wait((s) => { if (Volatile.Read(ref state) > 0)
                                                 {
                                                     throw new ApplicationException();
                                                 }
                                                 return(false); }, new object()))
                        {
                            Interlocked.Exchange(ref result, 1);
                        }
                        else
                        {
                            Interlocked.Exchange(ref result, 2);
                        }
                    }
                });

                TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount);
                Assert.AreEqual(0, Volatile.Read(ref result));

                testInst.Pulse();
                Thread.Sleep(1);
                Assert.AreEqual(0, Volatile.Read(ref result));

                Interlocked.Increment(ref state);
                testInst.Pulse();
                TimingAssert.AreEqual(10000, 0, () => testInst.WaiterCount);
                Assert.AreEqual(0, result);

                try
                {
                    task.Wait();
                }
                catch (AggregateException aE)
                {
                    if (aE.InnerExceptions.Count != 1 || !(aE.InnerExceptions[0] is ApplicationException))
                    {
                        throw;
                    }
                }
            }
        }
Пример #15
0
        // ============ Take ============



        /// <summary>
        /// Removes item from the head of the queue (slow path)
        /// </summary>
        /// <returns>Was taken sucessufully</returns>
        private bool TryTakeSlow(out T item, int timeout, CancellationToken token)
        {
            TimeoutTracker timeoutTracker = new TimeoutTracker(timeout);

            // Check WaiterCount to preserve fairness
            // Check ItemCount to prevent stucking inside lock on _takeMonitor
            bool canTakeConcurrently = _takeMonitor.WaiterCount == 0 || Volatile.Read(ref _itemCount) > _takeMonitor.WaiterCount + _nonFairItemThreshold;

            if (timeout != 0 && !canTakeConcurrently)
            {
                Thread.Yield();
                canTakeConcurrently = _takeMonitor.WaiterCount == 0 || Volatile.Read(ref _itemCount) > _takeMonitor.WaiterCount + _nonFairItemThreshold;
            }

            if (canTakeConcurrently)
            {
                var headSegment = TryGetNonCompletedHeadSegment();
                if (headSegment != null && headSegment.TryTake(out item))
                {
                    return(true);
                }

                if (timeout == 0 || timeoutTracker.IsTimeouted)
                {
                    item = default(T);
                    return(false);
                }
            }


            // Use waiting scheme
            using (var waiter = _takeMonitor.Enter(timeoutTracker.RemainingMilliseconds, token))
            {
                do
                {
                    var headSegment = TryGetNonCompletedHeadSegment();
                    if (headSegment != null && headSegment.TryTake(out item))
                    {
                        return(true);
                    }
                }while (waiter.Wait());
            }

            item = default(T);
            return(false);
        }
Пример #16
0
        // =============== Peek ===============


        /// <summary>
        /// Returns the item at the head of the queue without removing it (slow path)
        /// </summary>
        /// <returns>Was sucessufully</returns>
        private bool TryPeekSlow(out T item, int timeout, CancellationToken token)
        {
            TimeoutTracker timeoutTracker = new TimeoutTracker(timeout);

            if (timeout != 0 && _peekMonitor.WaiterCount > 0)
            {
                Thread.Yield();
            }

            // Check WaiterCount to preserve fairness
            // Check ItemCount to prevent stucking inside lock on _peekMonitor
            if (_peekMonitor.WaiterCount == 0 || Volatile.Read(ref _itemCount) > 0)
            {
                var headSegment = TryGetNonCompletedHeadSegment();
                if (headSegment != null && headSegment.TryPeek(out item))
                {
                    return(true);
                }

                if (timeout == 0 || timeoutTracker.IsTimeouted)
                {
                    item = default(T);
                    return(false);
                }
            }

            // Use waiting scheme
            using (var waiter = _peekMonitor.Enter(timeoutTracker.RemainingMilliseconds, token))
            {
                do
                {
                    var headSegment = TryGetNonCompletedHeadSegment();
                    if (headSegment != null && headSegment.TryPeek(out item))
                    {
                        return(true);
                    }
                }while (waiter.Wait());
            }

            item = default(T);
            return(false);
        }
Пример #17
0
        /// <summary>
        /// Adds new item to the tail of the queue (slow path)
        /// </summary>
        /// <returns>Was added sucessufully</returns>
        private bool TryAddSlow(T item, int timeout, CancellationToken token)
        {
            TimeoutTracker timeoutTracker = new TimeoutTracker(timeout);

            if (timeout != 0 && _addMonitor.WaiterCount > 0)
            {
                Thread.Yield();
            }

            // Check WaiterCount to preserve fairness
            // Check _segments.Count to prevent stucking inside lock
            if (_addMonitor.WaiterCount == 0 || _segments.Count <= _maxSegmentCount - _nonFairSegmentThreshold)
            {
                var tailSegment = TryGetNonFullTailSegment();
                if (tailSegment != null && tailSegment.TryAdd(item))
                {
                    return(true);
                }
                if (timeout == 0 || timeoutTracker.IsTimeouted)
                {
                    return(false);
                }
            }

            // Use waiting scheme
            using (var waiter = _addMonitor.Enter(timeoutTracker.RemainingMilliseconds, token))
            {
                do
                {
                    var tailSegment = TryGetNonFullTailSegment();
                    if (tailSegment != null && tailSegment.TryAdd(item))
                    {
                        return(true);
                    }

                    Debug.Assert(tailSegment == null || tailSegment.IsFull);
                }while (waiter.Wait());
            }

            return(false);
        }