public void SetupReceivers(ReceiveSettings[] receivers, Action <string, Exception, CancellationToken> criticalErrorAction) { var messagePumps = new Dictionary <string, IMessageReceiver>(receivers.Length); foreach (var receiver in receivers) { if (receiver.UsePublishSubscribe) { throw new NotImplementedException("MSMQ does not support native pub/sub."); } // The following check avoids creating some sub-queues, if the endpoint sub queue has the capability to exceed the max length limitation for queue format name. CheckEndpointNameComplianceForMsmq.Check(receiver.ReceiveAddress); QueuePermissions.CheckQueue(receiver.ReceiveAddress); var pump = new MessagePump( transactionMode => SelectReceiveStrategy(transactionMode, transportSettings.TransactionScopeOptions.TransactionOptions), transportSettings.MessageEnumeratorTimeout, criticalErrorAction, transportSettings, receiver); messagePumps.Add(pump.Id, pump); } Receivers = messagePumps; }
// This is static to prevent the method from accessing feilds in the pump since that causes variable capturing and cause extra allocations static async Task InnerReceiveMessages(MessagePump messagePump) { try { await messagePump.receiveStrategy.ReceiveMessage(messagePump.messageProcessingCancellationTokenSource.Token).ConfigureAwait(false); messagePump.receiveCircuitBreaker.Success(); } catch (OperationCanceledException) when(messagePump.messageProcessingCancellationTokenSource.IsCancellationRequested) { // Graceful shutdown } catch (Exception ex) { Logger.Warn("MSMQ receive operation failed", ex); await messagePump.receiveCircuitBreaker.Failure(ex).ConfigureAwait(false); } finally { messagePump.concurrencyLimiter.Release(); } }
// This is static to prevent the method from accessing fields in the pump since that causes variable capturing and cause extra allocations static async Task ReceiveMessagesSwallowExceptionsAndReleaseConcurrencyLimiter(MessagePump messagePump, SemaphoreSlim localConcurrencyLimiter, CancellationToken messagePumpCancellationToken) { #pragma warning disable PS0021 // Highlight when a try block passes multiple cancellation tokens - justification: // The message processing cancellation token is being used for the receive strategies, // since we want those only to be canceled when the public token passed to Stop() is canceled. // The message pump token is being used elsewhere, because we want those operations to be canceled as soon as Stop() is called. // The catch clauses on the inner try are correctly filtered on the message processing cancellation token and // the catch clause on the outer try is correctly filtered on the message pump cancellation token. try #pragma warning restore PS0021 // Highlight when a try block passes multiple cancellation tokens { try { // we are switching token here so we need to catch cancellation await messagePump.receiveStrategy.ReceiveMessage(messagePump.messageProcessingCancellationTokenSource.Token).ConfigureAwait(false); messagePump.receiveCircuitBreaker.Success(); } catch (Exception ex) when(ex.IsCausedBy(messagePump.messageProcessingCancellationTokenSource.Token)) { Logger.Warn("MSMQ receive operation canceled", ex); } catch (Exception ex) { Logger.Warn("MSMQ receive operation failed", ex); await messagePump.receiveCircuitBreaker.Failure(ex, messagePumpCancellationToken).ConfigureAwait(false); } } #pragma warning disable PS0019 // When catching System.Exception, cancellation needs to be properly accounted for - justification: see PS0021 suppression justification catch (Exception ex) when(ex.IsCausedBy(messagePumpCancellationToken)) #pragma warning restore PS0019 // When catching System.Exception, cancellation needs to be properly accounted for { // private token, sender is being stopped, log the exception in case the stack trace is ever needed for debugging Logger.Debug("Operation canceled while stopping message pump.", ex); } finally { localConcurrencyLimiter.Release(); } }