private async Task DoBatchSendNow()
        {
            await _sendingSemaphore.WaitAsync();

            try
            {
                BrokeredMessage[] toSend;
                lock (_mutex)
                {
                    toSend = _outboundQueue.Take(_maximumBatchSize).ToArray();
                    _outboundQueue.RemoveRange(0, toSend.Length);
                }
                if (toSend.None())
                {
                    return;
                }

                await SendBatch(toSend);

                GlobalMessageCounters.IncrementSentMessageCount(toSend.Length);
            }
            finally
            {
                _sendingSemaphore.Release();
            }
        }
        private async Task Worker(Func <BrokeredMessage, Task> callback, Task cancellationTask)
        {
            while (_running)
            {
                try
                {
                    int workerThreads;
                    int completionPortThreads;
                    ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
                    var batchSize = Math.Min(_throttle.CurrentCount, completionPortThreads);
                    var messages  = await FetchBatch(batchSize, cancellationTask);

                    if (!_running)
                    {
                        return;
                    }
                    if (messages.None())
                    {
                        continue;
                    }

                    GlobalMessageCounters.IncrementReceivedMessageCount(messages.Length);

                    var tasks = messages
                                .Select(m => Task.Run(async() =>
                    {
                        try
                        {
                            await _throttle.WaitAsync(_cancellationTokenSource.Token);
                            await callback(m);
                        }
                        finally
                        {
                            _throttle.Release();
                        }
                    }))
                                .ToArray();

                    if (_throttle.CurrentCount == 0)
                    {
                        await Task.WhenAny(tasks);
                    }
                }
                catch (OperationCanceledException)
                {
                    // will be thrown when someone calls .Stop() on us
                }
                catch (Exception exc)
                {
                    _logger.Error(exc, "Worker exception in {0} for {1}", GetType().Name, this);
                }
            }
        }
        private async Task Worker(Func <NimbusMessage, Task> callback)
        {
            var cancellationTokenSource   = _cancellationTokenSource;
            var consecutiveExceptionCount = 0;

            while (true)
            {
                if (!_running)
                {
                    break;
                }
                if (cancellationTokenSource.IsCancellationRequested)
                {
                    break;
                }

                try
                {
                    var message = await Fetch(cancellationTokenSource.Token);

                    if (message == null)
                    {
                        if (!cancellationTokenSource.IsCancellationRequested)
                        {
                            // if we're shutting down, we're fine if we received a null message - we asked to quit, after all...
                            _logger.Debug($"Call to {nameof(Fetch)} returned null on {{QueueOrTopic}}. Retrying...", ToString());
                        }
                        continue;
                    }

                    consecutiveExceptionCount = 0;

                    _logger.Debug("Waiting for handler semaphores...");
                    await _localHandlerThrottle.WaitAsync(cancellationTokenSource.Token);

                    await _globalHandlerThrottle.Wait(cancellationTokenSource.Token);

                    _logger.Debug("Acquired handler semaphores...");

#pragma warning disable 4014
                    Task.Run(async() =>
                    {
                        try
                        {
                            await callback(message);
                            GlobalMessageCounters.IncrementReceivedMessageCount(1);
                        }
                        finally
                        {
                            _globalHandlerThrottle.Release();
                            _localHandlerThrottle.Release();
                        }
                    }).ConfigureAwaitFalse();
#pragma warning restore 4014
                }
                catch (OperationCanceledException)
                {
                    // will be thrown when someone calls .Stop() on us
                    break;
                }
                catch (Exception exc)
                {
                    consecutiveExceptionCount++;
                    var delay = CalculateDelayFor(consecutiveExceptionCount);

                    _logger.Error(exc,
                                  "Worker exception ({ConsecutiveExceptionCount} consecutive) in {Type} for {QueueOrSubscriptionPath}. Delaying for {Duration}.",
                                  consecutiveExceptionCount,
                                  GetType().Name,
                                  this,
                                  delay);

                    try
                    {
                        await Task.Delay(delay, cancellationTokenSource.Token);
                    }
                    catch (OperationCanceledException)
                    {
                    }
                }
            }
        }