private async Task TryProcessNextQueuedApplicationMessageAsync(CancellationToken cancellationToken)
        {
            try
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return;
                }

                var dequeueResult = await _messageQueue.TryDequeueAsync(cancellationToken).ConfigureAwait(false);

                var queuedApplicationMessage = dequeueResult.Item;

                var sender             = queuedApplicationMessage.Sender;
                var applicationMessage = queuedApplicationMessage.ApplicationMessage;

                var interceptorContext = await InterceptApplicationMessageAsync(sender, applicationMessage).ConfigureAwait(false);

                if (interceptorContext != null)
                {
                    if (interceptorContext.CloseConnection)
                    {
                        if (sender != null)
                        {
                            await sender.StopAsync().ConfigureAwait(false);
                        }
                    }

                    if (interceptorContext.ApplicationMessage == null || !interceptorContext.AcceptPublish)
                    {
                        return;
                    }

                    applicationMessage = interceptorContext.ApplicationMessage;
                }

                await _eventDispatcher.HandleApplicationMessageReceivedAsync(sender?.ClientId, applicationMessage).ConfigureAwait(false);

                if (applicationMessage.Retain)
                {
                    await _retainedMessagesManager.HandleMessageAsync(sender?.ClientId, applicationMessage).ConfigureAwait(false);
                }

                foreach (var clientSession in _sessions.Values)
                {
                    clientSession.EnqueueApplicationMessage(
                        applicationMessage,
                        sender?.ClientId,
                        false);
                }
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                _logger.Error(exception, "Unhandled exception while processing next queued application message.");
            }
        }
        async Task TryProcessNextQueuedApplicationMessage()
        {
            try
            {
                MqttPendingApplicationMessage pendingApplicationMessage;
                try
                {
                    pendingApplicationMessage = _messageQueue.Take();
                }
                catch (ArgumentNullException)
                {
                    return;
                }
                catch (ObjectDisposedException)
                {
                    return;
                }

                var clientConnection   = pendingApplicationMessage.Sender;
                var senderClientId     = clientConnection?.ClientId ?? _options.ClientId;
                var applicationMessage = pendingApplicationMessage.ApplicationMessage;

                var interceptor = _options.ApplicationMessageInterceptor;
                if (interceptor != null)
                {
                    var interceptorContext =
                        await InterceptApplicationMessageAsync(interceptor, clientConnection, applicationMessage)
                        .ConfigureAwait(false);

                    if (interceptorContext != null)
                    {
                        if (interceptorContext.CloseConnection)
                        {
                            if (clientConnection != null)
                            {
                                await clientConnection.StopAsync(MqttClientDisconnectReason.NormalDisconnection)
                                .ConfigureAwait(false);
                            }
                        }

                        if (interceptorContext.ApplicationMessage == null || !interceptorContext.AcceptPublish)
                        {
                            return;
                        }

                        applicationMessage = interceptorContext.ApplicationMessage;
                    }
                }

                await _eventDispatcher.SafeNotifyApplicationMessageReceivedAsync(senderClientId, applicationMessage)
                .ConfigureAwait(false);

                if (applicationMessage.Retain)
                {
                    await _retainedMessagesManager.HandleMessageAsync(senderClientId, applicationMessage)
                    .ConfigureAwait(false);
                }

                var deliveryCount = 0;
                List <MqttClientSession> sessions;
                lock (_clientSessions)
                {
                    sessions = _clientSessions.Values.ToList();
                }

                foreach (var clientSession in sessions)
                {
                    var checkSubscriptionsResult = clientSession.SubscriptionsManager.CheckSubscriptions(
                        applicationMessage.Topic,
                        applicationMessage.QualityOfServiceLevel,
                        senderClientId);

                    if (!checkSubscriptionsResult.IsSubscribed)
                    {
                        continue;
                    }

                    _logger.Verbose("Client '{0}': Queued application message with topic '{1}'.",
                                    clientSession.ClientId, applicationMessage.Topic);

                    var queuedApplicationMessage = new MqttQueuedApplicationMessage
                    {
                        ApplicationMessage = applicationMessage,
                        SubscriptionQualityOfServiceLevel = checkSubscriptionsResult.QualityOfServiceLevel,
                        SubscriptionIdentifiers           = checkSubscriptionsResult.SubscriptionIdentifiers
                    };

                    if (checkSubscriptionsResult.RetainAsPublished)
                    {
                        // Transfer the original retain state from the publisher.
                        // This is a MQTTv5 feature.
                        queuedApplicationMessage.IsRetainedMessage = applicationMessage.Retain;
                    }

                    clientSession.ApplicationMessagesQueue.Enqueue(queuedApplicationMessage);
                    deliveryCount++;
                }

                if (deliveryCount == 0)
                {
                    var undeliveredMessageInterceptor = _options.UndeliveredMessageInterceptor;
                    if (undeliveredMessageInterceptor == null)
                    {
                        return;
                    }

                    // The delegate signature is the same as for regular message interceptor. So the call is fine and just uses a different interceptor.
                    await InterceptApplicationMessageAsync(undeliveredMessageInterceptor, clientConnection,
                                                           applicationMessage).ConfigureAwait(false);
                }
            }
            catch (Exception exception)
            {
                _logger.Error(exception, "Unhandled exception while processing next queued application message.");
            }
        }
        async Task TryProcessNextQueuedApplicationMessageAsync(CancellationToken cancellationToken)
        {
            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                MqttPendingApplicationMessage queuedApplicationMessage;
                try
                {
                    queuedApplicationMessage = _messageQueue.Take(cancellationToken);
                }
                catch (ArgumentNullException)
                {
                    return;
                }
                catch (ObjectDisposedException)
                {
                    return;
                }

                var clientConnection   = queuedApplicationMessage.Sender;
                var senderClientId     = clientConnection?.ClientId ?? _options.ClientId;
                var applicationMessage = queuedApplicationMessage.ApplicationMessage;

                var interceptor = _options.ApplicationMessageInterceptor;
                if (interceptor != null)
                {
                    var interceptorContext = await InterceptApplicationMessageAsync(interceptor, clientConnection, applicationMessage).ConfigureAwait(false);

                    if (interceptorContext != null)
                    {
                        if (interceptorContext.CloseConnection)
                        {
                            if (clientConnection != null)
                            {
                                await clientConnection.StopAsync(MqttClientDisconnectReason.NormalDisconnection).ConfigureAwait(false);
                            }
                        }

                        if (interceptorContext.ApplicationMessage == null || !interceptorContext.AcceptPublish)
                        {
                            return;
                        }

                        applicationMessage = interceptorContext.ApplicationMessage;
                    }
                }

                await _eventDispatcher.SafeNotifyApplicationMessageReceivedAsync(senderClientId, applicationMessage).ConfigureAwait(false);

                if (applicationMessage.Retain)
                {
                    await _retainedMessagesManager.HandleMessageAsync(senderClientId, applicationMessage).ConfigureAwait(false);
                }

                var deliveryCount = 0;
                List <MqttClientSession> sessions;
                lock (_sessions)
                {
                    sessions = _sessions.Values.ToList();
                }

                foreach (var clientSession in sessions)
                {
                    var isSubscribed = clientSession.EnqueueApplicationMessage(applicationMessage, senderClientId, false);
                    if (isSubscribed)
                    {
                        deliveryCount++;
                    }
                }

                if (deliveryCount == 0)
                {
                    var undeliveredMessageInterceptor = _options.UndeliveredMessageInterceptor;
                    if (undeliveredMessageInterceptor == null)
                    {
                        return;
                    }

                    // The delegate signature is the same as for regular message interceptor. So the call is fine and just uses a different interceptor.
                    await InterceptApplicationMessageAsync(undeliveredMessageInterceptor, clientConnection, applicationMessage).ConfigureAwait(false);
                }
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                _logger.Error(exception, "Unhandled exception while processing next queued application message.");
            }
        }
        async Task TryProcessNextQueuedApplicationMessageAsync(CancellationToken cancellationToken)
        {
            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                var queuedApplicationMessage = _messageQueue.Take(cancellationToken);
                var sender             = queuedApplicationMessage.Sender;
                var senderClientId     = sender?.ClientId ?? _options.ClientId;
                var applicationMessage = queuedApplicationMessage.ApplicationMessage;

                var interceptor = _options.ApplicationMessageInterceptor;
                if (interceptor != null)
                {
                    var interceptorContext = await InterceptApplicationMessageAsync(interceptor, sender, applicationMessage).ConfigureAwait(false);

                    if (interceptorContext != null)
                    {
                        if (interceptorContext.CloseConnection)
                        {
                            if (sender != null)
                            {
                                await sender.StopAsync(MqttDisconnectReasonCode.NormalDisconnection).ConfigureAwait(false);
                            }
                        }

                        if (interceptorContext.ApplicationMessage == null || !interceptorContext.AcceptPublish)
                        {
                            return;
                        }

                        applicationMessage = interceptorContext.ApplicationMessage;
                    }
                }

                await _eventDispatcher.SafeNotifyApplicationMessageReceivedAsync(senderClientId, applicationMessage).ConfigureAwait(false);

                if (applicationMessage.Retain)
                {
                    await _retainedMessagesManager.HandleMessageAsync(senderClientId, applicationMessage).ConfigureAwait(false);
                }

                var deliveryCount = 0;

                lock (_sessions)
                {
                    foreach (var clientSession in _sessions.Values)
                    {
                        var isSubscribed = clientSession.EnqueueApplicationMessage(applicationMessage, senderClientId, false);
                        if (isSubscribed)
                        {
                            deliveryCount++;
                        }
                    }
                }

                if (deliveryCount == 0)
                {
                    var undeliveredMessageInterceptor = _options.UndeliveredMessageInterceptor;
                    if (undeliveredMessageInterceptor == null)
                    {
                        return;
                    }

                    await undeliveredMessageInterceptor.InterceptApplicationMessagePublishAsync(new MqttApplicationMessageInterceptorContext(senderClientId, sender?.Session?.Items, applicationMessage));
                }
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                _logger.Error(exception, "Unhandled exception while processing next queued application message.");
            }
        }