/// <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); }
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(); } }
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(); } }
/// <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); }
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); }
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); }
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(); } }
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()); } }
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(); } }
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)); } }
/// <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); }
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(); } }
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); } }
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; } } } }
// ============ 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); }
// =============== 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); }
/// <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); }