/// <summary> /// Adds new item to the queue, even when the bounded capacity reached /// </summary> /// <param name="item">New item</param> public override void AddForced(T item) { CheckDisposed(); if (_addingMode == LevelingQueueAddingMode.PreferLiveData) { if (!_highLevelQueue.TryAdd(item, 0, default(CancellationToken))) { _lowLevelQueue.AddForced(item); if (_isBackgroundTransferingEnabled) { _backgoundTransfererExclusive.AllowBackgroundGate(); // Allow background transfering } } } else { bool addedToHighLevelQueue = false; if (_isBackgroundTransferingEnabled) { if (_lowLevelQueue.IsEmpty) { // Only in exclusive mode using (var gateGuard = _backgoundTransfererExclusive.EnterMain(Timeout.Infinite, default(CancellationToken))) // This should happen fast { TurboContract.Assert(gateGuard.IsAcquired, conditionString: "gateGuard.IsAcquired"); addedToHighLevelQueue = _lowLevelQueue.IsEmpty && _highLevelQueue.TryAdd(item, 0, default(CancellationToken)); } } } else { addedToHighLevelQueue = _lowLevelQueue.IsEmpty && _highLevelQueue.TryAdd(item, 0, default(CancellationToken)); } if (!addedToHighLevelQueue) { _lowLevelQueue.AddForced(item); if (_isBackgroundTransferingEnabled) { _backgoundTransfererExclusive.AllowBackgroundGate(); // Allow background transfering } } } NotifyItemAdded(); }
// ============================ private void PreserveOrderTest(IQueue <long> queue, int elemCount) { Barrier bar = new Barrier(2); CancellationTokenSource cancelled = new CancellationTokenSource(); List <int> takenElems = new List <int>(elemCount + 1); Action addAction = () => { int curElem = 0; Random rnd = new Random(Environment.TickCount + Thread.CurrentThread.ManagedThreadId); bar.SignalAndWait(); while (curElem < elemCount) { if (rnd.Next(100) == 0) { queue.AddForced(curElem); } else { Assert.IsTrue(queue.TryAdd(curElem, -1, default(CancellationToken))); } if (rnd.Next(100) == 0) { Thread.Yield(); } Thread.SpinWait(rnd.Next(100)); curElem++; } cancelled.Cancel(); }; Action takeAction = () => { Random rnd = new Random(Environment.TickCount + Thread.CurrentThread.ManagedThreadId); bar.SignalAndWait(); try { while (!cancelled.IsCancellationRequested) { long itemX = 0; Assert.IsTrue(queue.TryTake(out itemX, -1, cancelled.Token)); takenElems.Add((int)itemX); if (rnd.Next(100) == 0) { Thread.Yield(); } Thread.SpinWait(rnd.Next(400)); } } catch (OperationCanceledException) { } long item = 0; while (queue.TryTake(out item, 0, default(CancellationToken))) { takenElems.Add((int)item); } }; var sw = System.Diagnostics.Stopwatch.StartNew(); Task addTask = Task.Factory.StartNew(addAction, TaskCreationOptions.LongRunning); Task takeTask = Task.Factory.StartNew(takeAction, TaskCreationOptions.LongRunning); Task.WaitAll(addTask, takeTask); Assert.AreEqual(elemCount, takenElems.Count); for (int i = 0; i < takenElems.Count; i++) { if (i != takenElems[i]) { Assert.AreEqual(i, takenElems[i], $"i != takenElems[i], nextItem = {takenElems[i + 1]}"); } } }
// ============================ private void RunComplexTest(IQueue <long> q, int elemCount, int addSpin, int takeSpin, int thCount) { int atomicRandom = 0; int trackElemCount = elemCount; int addFinished = 0; Thread[] threadsTake = new Thread[thCount]; Thread[] threadsAdd = new Thread[thCount]; CancellationTokenSource tokSrc = new CancellationTokenSource(); List <int> global = new List <int>(elemCount); Action addAction = () => { Random rnd = new Random(Environment.TickCount + Interlocked.Increment(ref atomicRandom) * thCount * 2); while (true) { int item = Interlocked.Decrement(ref trackElemCount); if (item < 0) { break; } if (rnd.Next(100) == 0) { q.AddForced(item); } else { Assert.IsTrue(q.TryAdd(item, -1, default(CancellationToken))); } int sleepTime = rnd.Next(addSpin); long tmpItem = 0; if (q.TryPeek(out tmpItem, 0, default(CancellationToken)) && tmpItem % 1000 == 0) { sleepTime += 100; } if (sleepTime > 0) { Thread.SpinWait(sleepTime); } } Interlocked.Increment(ref addFinished); }; Action takeAction = () => { Random rnd = new Random(Environment.TickCount + Interlocked.Increment(ref atomicRandom) * thCount * 2); List <int> data = new List <int>(); try { while (Volatile.Read(ref addFinished) < thCount) { long tmp = 0; if (q.TryTake(out tmp, -1, tokSrc.Token)) { data.Add((int)tmp); } int sleepTime = rnd.Next(takeSpin); if (sleepTime > 0) { Thread.SpinWait(sleepTime); } } } catch (OperationCanceledException) { } long tmp2; while (q.TryTake(out tmp2, 0, default(CancellationToken))) { data.Add((int)tmp2); } lock (global) global.AddRange(data); }; for (int i = 0; i < threadsTake.Length; i++) { threadsTake[i] = new Thread(new ThreadStart(takeAction)); } for (int i = 0; i < threadsAdd.Length; i++) { threadsAdd[i] = new Thread(new ThreadStart(addAction)); } for (int i = 0; i < threadsTake.Length; i++) { threadsTake[i].Start(); } for (int i = 0; i < threadsAdd.Length; i++) { threadsAdd[i].Start(); } for (int i = 0; i < threadsAdd.Length; i++) { threadsAdd[i].Join(); } tokSrc.Cancel(); for (int i = 0; i < threadsTake.Length; i++) { threadsTake[i].Join(); } Assert.AreEqual(elemCount, global.Count); global.Sort(); for (int i = 0; i < elemCount; i++) { Assert.AreEqual(i, global[i]); } }
/// <summary> /// Adds new item to the high level queue, even when the bounded capacity reached /// </summary> /// <param name="item">New item</param> public void AddForcedToHighLevelQueue(T item) { CheckDisposed(); _highLevelQueue.AddForced(item); NotifyItemAdded(); }