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 TestDisposeInterruptWaitersOnAdd()
        {
            BlockingBatchingQueue <int> queue = new BlockingBatchingQueue <int>(batchSize: 2, boundedCapacityInBatches: 1);

            queue.Add(1);
            queue.Add(2);
            Barrier bar = new Barrier(2);

            Task disposeTask = Task.Run(() =>
            {
                bar.SignalAndWait();
                Thread.Sleep(10);
                queue.Dispose();
            });

            try
            {
                bar.SignalAndWait();
                bool added = queue.TryAdd(3, 10000);
                Assert.Fail();
            }
            catch (OperationInterruptedException)
            {
            }
            catch (ObjectDisposedException)
            {
            }
            catch (Exception)
            {
                Assert.Fail("Unexpected exception");
            }


            disposeTask.Wait();
        }
        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 SimpleConcurrentTestCore(int batchSize, int boundedCapacityInBatches)
        {
            BlockingBatchingQueue <long> col = new BlockingBatchingQueue <long>(batchSize: batchSize, boundedCapacityInBatches: boundedCapacityInBatches);
            Barrier bar = new Barrier(4);
            CancellationTokenSource cancelToken = new CancellationTokenSource();

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

            Task addTask = Task.Run(() =>
            {
                Random rnd = new Random();
                bar.SignalAndWait();
                long data = 0;
                while (!cancelToken.IsCancellationRequested)
                {
                    if (col.TryAdd(data))
                    {
                        data++;
                    }
                    if (rnd.Next(100) == 1)
                    {
                        col.CompleteCurrentBatch();
                    }
                }
            });

            Task takeTask = Task.Run(() =>
            {
                bar.SignalAndWait();

                while (!cancelToken.IsCancellationRequested)
                {
                    if (col.TryTake(out long[] itemsT))
                    {
                        takenItems.AddRange(itemsT);
                    }

                    if (takenItems.Count > int.MaxValue / 2)
                    {
                        cancelToken.Cancel();
                    }
                }
            });

            Task enumerateTask = Task.Run(() =>
            {
                bar.SignalAndWait();

                while (!cancelToken.IsCancellationRequested)
                {
                    int count     = 0;
                    long prevItem = -1;
                    foreach (long item in col)
                    {
                        count++;
                        if (prevItem > 0)
                        {
                            Assert.AreEqual(prevItem + 1, item);
                        }

                        prevItem = item;
                    }
                    Thread.Sleep(count > 100 ? 0 : 1);
                }
            });

            bar.SignalAndWait();
            Thread.Sleep(300);
            cancelToken.Cancel();

            Task.WaitAll(addTask, takeTask, enumerateTask);

            while (col.TryTake(out long[] itemsF))
            {
                takenItems.AddRange(itemsF);
            }

            col.CompleteCurrentBatch();

            if (col.TryTake(out long[] itemsFF))
            {
                takenItems.AddRange(itemsFF);
            }

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