public async Task MultithreadingInsertsDontCrash()
        {
            int insertThreads  = 4;
            int itemsPerThread = 100;

            _queue = new AsyncBatchQueue <int>(11);

            List <Task> insertTasks = Enumerable.Range(1, insertThreads)
                                      .Select(
                _ => Task.Run(
                    () =>
            {
                for (int i = 0; i < itemsPerThread; i++)
                {
                    _queue.Add(42);
                }
            }))
                                      .ToList();

            await Task.WhenAll(insertTasks).ConfigureAwait(true);

            _queue.Flush();

            int itemsTaken = 0;

            while (_queue.Count > 0)
            {
                itemsTaken += (await _queue.TakeAsync().ConfigureAwait(true)).Count;
            }

            itemsTaken.Should().Be(insertThreads * itemsPerThread);
        }
 private void AddAllItemsButOne(int batchSize)
 {
     for (int itemIndex = 0; itemIndex < batchSize - 1; itemIndex++)
     {
         _queue.Add(itemIndex);
     }
 }
		public async Task FlushesWhenBatchSizeIsReached()
		{
			int[] array = { 0, 1, 42 };
			int index = 0;

			_queue = new AsyncBatchQueue<int>( array.Length );
			for ( ; index < array.Length - 1; index++ )
				_queue.Add( array[ index ] );

			var takeTask = _queue.TakeAsync();
			Assert.IsFalse( takeTask.IsCompleted );

			_queue.Add( array[ index ] );
			var batch = await takeTask;

			CollectionAssert.AreEqual( array, batch.ToList() );
		}
		public async Task TimerFlushesPendingItems()
		{
			TimeSpan flushPeriod = TimeSpan.FromMilliseconds( 500 );
			_queue = new AsyncBatchQueue<int>( 9999, flushPeriod );
			_queue.Add( 42 );

			await Task.Delay( flushPeriod + flushPeriod );
			var batch = await _queue.TakeAsync();
			CollectionAssert.AreEqual( new[] { 42 }, batch.ToList() );
		}
        public async Task FlushesWhenBatchSizeIsReached()
        {
            int[] array = { 0, 1, 42 };
            int   index = 0;

            _queue = new AsyncBatchQueue <int>(array.Length);
            for ( ; index < array.Length - 1; index++)
            {
                _queue.Add(array[index]);
            }

            var takeTask = _queue.TakeAsync();

            takeTask.IsCompleted.Should().BeFalse();

            _queue.Add(array[index]);
            var batch = await takeTask.ConfigureAwait(true);

            batch.Should().BeEqualTo(array);
        }
        public async Task NoRaceBetweenFlushOnAddAndOnDemand()
        {
            const int attempts  = 100 * 1000;
            const int batchSize = 5;

            _queue = new AsyncBatchQueue <int>(batchSize);

            for (int attemptNumber = 0; attemptNumber < attempts; attemptNumber++)
            {
                AddAllItemsButOne(batchSize);

                using (ManualResetEvent trigger = new ManualResetEvent(initialState: false))
                {
                    Task addTask = Task.Run
                                   (
                        () =>
                    {
                        trigger.WaitOne();
                        _queue.Add(666);
                    }
                                   );

                    Task flushTask = Task.Run
                                     (
                        () =>
                    {
                        trigger.WaitOne();
                        _queue.Flush();
                    }
                                     );

                    trigger.Set();
                    await addTask.ConfigureAwait(true);

                    await flushTask.ConfigureAwait(true);

                    IReadOnlyList <int> batch = await _queue.TakeAsync().ConfigureAwait(true);

                    List <int> allItems = batch.ToList();

                    // This happens if Flush occurred before Add, which means there's another item from Add left unflushed.
                    // Gotta flush once more to extract it.
                    if (batch.Count < batchSize)
                    {
                        _queue.Flush();
                        IReadOnlyList <int> secondBatch = await _queue.TakeAsync().ConfigureAwait(true);

                        allItems.AddRange(secondBatch);
                    }

                    allItems.Count.Should().BeLessOrEqualTo(batchSize, $"Double flush detected at attempt #{attemptNumber}. Items: {String.Join( ", ", allItems )}");
                }
            }
        }
        public async Task TimerFlushesPendingItems()
        {
            TimeSpan flushPeriod = TimeSpan.FromMilliseconds(500);
            var      timerQueue  = new AsyncBatchQueue <int>(9999).WithFlushEvery(flushPeriod);

            timerQueue.Add(42);

            await Task.Delay(flushPeriod + flushPeriod).ConfigureAwait(true);

            var batch = await timerQueue.TakeAsync().ConfigureAwait(true);

            batch.Should().BeEqualTo(new[] { 42 });
        }
		public async Task ManualFlushWorks()
		{
			int[] array = { 0, 1, 42 };

			_queue = new AsyncBatchQueue<int>( 50 );
			foreach ( var item in array )
				_queue.Add( item );

			_queue.Flush();
			var batch = await _queue.TakeAsync();

			CollectionAssert.AreEqual( array, batch.ToList() );
		}
        public async Task ManualFlushWorks()
        {
            int[] array = { 0, 1, 42 };

            _queue = new AsyncBatchQueue <int>(50);
            foreach (var item in array)
            {
                _queue.Add(item);
            }

            _queue.Flush();
            var batch = await _queue.TakeAsync().ConfigureAwait(true);

            batch.Should().BeEqualTo(array);
        }
		public async Task MultithreadingInsertsDontCrash()
		{
			int insertThreads = 4;
			int itemsPerThread = 100;

			_queue = new AsyncBatchQueue<int>( 11 );

			List<Task> insertTasks = Enumerable.Range( 1, insertThreads )
				.Select(
					_ => Task.Run(
						() =>
						{
							for ( int i = 0; i < itemsPerThread; i++ )
								_queue.Add( 42 );
						} ) )
				.ToList();

			await Task.WhenAll( insertTasks );
			_queue.Flush();

			int itemsTaken = 0;
			while ( _queue.Count > 0 )
				itemsTaken += ( await _queue.TakeAsync() ).Count;

			Assert.AreEqual( insertThreads * itemsPerThread, itemsTaken );
		}