public void TestEnqueueDequeueToTheLimit()
        {
            const int batchSize             = 13;
            BlockingBatchingQueue <int> col = new BlockingBatchingQueue <int>(batchSize: batchSize, boundedCapacityInBatches: 2);

            for (int i = 0; i < batchSize * col.BoundedCapacityInBatches; i++)
            {
                Assert.IsTrue(col.TryAdd(i));
            }

            Assert.IsFalse(col.TryAdd(int.MaxValue));

            List <int> takenItems = new List <int>();

            for (int i = 0; i < col.BoundedCapacityInBatches; i++)
            {
                Assert.IsTrue(col.TryTake(out int[] batch));
                takenItems.AddRange(batch);
            }

            Assert.IsFalse(col.TryTake(out _));

            for (int i = 0; i < takenItems.Count; i++)
            {
                Assert.AreEqual(i, takenItems[i]);
            }
        }
        public void TestCompleteCurrentBatch()
        {
            const int batchSize             = 10;
            BlockingBatchingQueue <int> col = new BlockingBatchingQueue <int>(batchSize: batchSize);

            Assert.AreEqual(0, col.Count);
            Assert.AreEqual(0, col.CompletedBatchCount);

            Assert.IsFalse(col.CompleteCurrentBatch());
            Assert.AreEqual(0, col.Count);
            Assert.AreEqual(0, col.CompletedBatchCount);


            int[] dequeuedItems = null;

            Assert.IsFalse(col.TryTake(out dequeuedItems));
            col.Add(0);
            col.Add(1);
            Assert.AreEqual(2, col.Count);
            Assert.AreEqual(0, col.CompletedBatchCount);


            Assert.IsTrue(col.CompleteCurrentBatch());
            Assert.AreEqual(2, col.Count);
            Assert.AreEqual(1, col.CompletedBatchCount);


            Assert.IsTrue(col.TryTake(out dequeuedItems));
            Assert.AreEqual(2, dequeuedItems.Length);
            for (int i = 0; i < dequeuedItems.Length; i++)
            {
                Assert.AreEqual(i, dequeuedItems[i]);
            }

            Assert.AreEqual(0, col.Count);
            Assert.AreEqual(0, col.CompletedBatchCount);
        }
        public void TestTimeoutWorks()
        {
            const int batchSize = 17;
            BlockingBatchingQueue <int> queue = new BlockingBatchingQueue <int>(batchSize: batchSize, boundedCapacityInBatches: 8);
            Barrier bar        = new Barrier(2);
            int     takeResult = 0;
            int     addResult  = 0;
            Task    task       = Task.Run(() =>
            {
                bar.SignalAndWait();
                int[] item = null;
                if (queue.TryTake(out item, 100))
                {
                    Interlocked.Exchange(ref takeResult, 1);
                }
                else
                {
                    Interlocked.Exchange(ref takeResult, 2);
                }

                while (queue.TryAdd(-1))
                {
                    ;
                }

                if (queue.TryAdd(100, 100))
                {
                    Interlocked.Exchange(ref addResult, 1);
                }
                else
                {
                    Interlocked.Exchange(ref addResult, 2);
                }
            });

            bar.SignalAndWait();

            TimingAssert.AreEqual(10000, 2, () => Volatile.Read(ref takeResult), "take");
            TimingAssert.AreEqual(10000, 2, () => Volatile.Read(ref addResult), "Add");

            task.Wait();
        }
        private void RunComplexTest(BlockingBatchingQueue <int> q, int elemCount, int thCount)
        {
            int atomicRandom = 0;

            int trackElemCount = elemCount;
            int addFinished    = 0;

            Thread[] threadsTake         = new Thread[thCount];
            Thread[] threadsAdd          = new Thread[thCount];
            Thread   completeBatchThread = null;

            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;
                    }

                    q.Add(item);

                    int sleepTime = rnd.Next(12);

                    if (sleepTime > 0)
                    {
                        SpinWaitHelper.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)
                    {
                        int[] tmp;
                        if (q.TryTake(out tmp, -1, tokSrc.Token))
                        {
                            data.AddRange(tmp);
                        }

                        int sleepTime = rnd.Next(12);
                        if (sleepTime > 0)
                        {
                            SpinWaitHelper.SpinWait(sleepTime);
                        }
                    }
                }
                catch (OperationCanceledException) { }

                int[] tmp2;
                while (q.TryTake(out tmp2))
                {
                    data.AddRange(tmp2);
                }

                q.CompleteCurrentBatch();
                while (q.TryTake(out tmp2))
                {
                    data.AddRange(tmp2);
                }

                lock (global)
                    global.AddRange(data);
            };

            Action completeBatchAction = () =>
            {
                Random rnd = new Random(Environment.TickCount + Interlocked.Increment(ref atomicRandom) * thCount * 2);
                while (Volatile.Read(ref addFinished) < thCount && !tokSrc.IsCancellationRequested)
                {
                    q.CompleteCurrentBatch();
                    Thread.Sleep(rnd.Next(2));
                }
            };


            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));
            }
            completeBatchThread = new Thread(new ThreadStart(completeBatchAction));

            for (int i = 0; i < threadsTake.Length; i++)
            {
                threadsTake[i].Start();
            }
            for (int i = 0; i < threadsAdd.Length; i++)
            {
                threadsAdd[i].Start();
            }
            completeBatchThread.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();
            }
            completeBatchThread.Join();


            Assert.AreEqual(elemCount, global.Count);
            global.Sort();

            for (int i = 0; i < elemCount; i++)
            {
                Assert.AreEqual(i, global[i]);
            }
        }
        private void ConcurrentPackageWithTimeoutTestCore(int addThreads, int itemCount, int batchSize, int boundedCapacityInBatches)
        {
            int atomicRandom = 0;

            BlockingBatchingQueue <int> col = new BlockingBatchingQueue <int>(batchSize: batchSize, boundedCapacityInBatches: boundedCapacityInBatches);
            Barrier bar = new Barrier(1 + 1 + addThreads);
            CancellationTokenSource cancelToken = new CancellationTokenSource();

            Thread[] threadsAdd  = new Thread[addThreads];
            Thread[] threadsTake = new Thread[1];

            List <int> takenItems   = new List <int>();
            int        itemsCounter = itemCount;

            Action addAction = () =>
            {
                Random rnd = new Random(Environment.TickCount + Interlocked.Increment(ref atomicRandom) * addThreads * 2);
                bar.SignalAndWait();

                while (true)
                {
                    int val = Interlocked.Decrement(ref itemsCounter);
                    if (val < 0)
                    {
                        break;
                    }

                    col.Add(val);

                    int delay = (int)(((double)val / itemCount) * 100);
                    if (delay > 0)
                    {
                        SpinWaitHelper.SpinWait(rnd.Next(delay));
                    }
                }
            };

            Action takeAction = () =>
            {
                var   token = cancelToken.Token;
                int[] items = null;
                bar.SignalAndWait();


                try
                {
                    while (!cancelToken.IsCancellationRequested)
                    {
                        if (col.TryTake(out items, 5, token))
                        {
                            takenItems.AddRange(items);
                        }
                        else
                        {
                            col.CompleteCurrentBatch();
                        }
                    }
                }
                catch (OperationCanceledException) { }

                while (col.TryTake(out items))
                {
                    takenItems.AddRange(items);
                }

                col.CompleteCurrentBatch();

                if (col.TryTake(out items))
                {
                    takenItems.AddRange(items);
                }
            };

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

            bar.SignalAndWait();

            for (int i = 0; i < threadsAdd.Length; i++)
            {
                threadsAdd[i].Join();
            }

            cancelToken.Cancel();

            for (int i = 0; i < threadsTake.Length; i++)
            {
                threadsTake[i].Join();
            }


            takenItems.Sort();
            for (int i = 0; i < takenItems.Count; i++)
            {
                Assert.AreEqual(i, takenItems[i]);
            }
        }