async Task <ILocalMessagePostingToken> P_PublishMessageAsync(ILocalMessage msg, bool disposeMessageAtEndOfPosting) { msg.EnsureNotNull(nameof(msg)); // ILocalMessagePostingToken postingToken; IRunControl runControl = ReadDA(ref _runControl); IList <ILocalSubscription> subscriptions = ReadDA(ref _subscriptionsSpinLock).Invoke(() => ReadDA(ref _subscriptionsList).Where(i => i.IsActive).ToArray()); subscriptions = await P_PublicationFilterAsync(publisherRunControl : runControl, msg : msg, sourceSubscriptions : subscriptions).ConfigureAwait(false); if (subscriptions.Count > 0 && !runControl.HasStopRequested) { var postingTokenStricted = new LocalPostingToken(message: msg, postingCount: subscriptions.Count, disposeMessageAtEndOfPosting: disposeMessageAtEndOfPosting); postingToken = postingTokenStricted; var postingQueueEntry = default(LocalPostingQueueEntry); try { postingQueueEntry = new LocalPostingQueueEntry(postingToken: postingTokenStricted, postingSubscriptions: subscriptions); var postingQueueLength = ReadDA(ref _postingQueueSpinLock) .Invoke( () => { var locPostingQueue = ReadDA(ref _postingQueue); locPostingQueue.Enqueue(postingQueueEntry); return(locPostingQueue.Count); }); #if TRG_NETFRAMEWORK if (postingQueueLength >= __PostingQueueLengthWarning) { try { throw new InvalidOperationException( message: $"Длина очереди доставки сообщений достигла предела предупреждения '{__PostingQueueLengthWarning:d}' и составляет '{postingQueueLength:d}'.{Environment.NewLine}Слишком большая очередь доставки сообщений свидетельствует о некорректной работе компонентов, выполняющих доставку сообщений, либо о слишком большом потоке сообщений.{Environment.NewLine}Данное событие не прерывает работу, если оно успешно записано в системый журнал событий."); } catch (Exception firstException) { WindowsEventLogUtilities.WriteFaultToEventLog(fault: firstException, failFastOnError: true, faultFormattingOptions: ExceptionInfoFormattingOptions.Full); } } #else // TODO_HIGH: Implement logging (as for TRG_NETFRAMEWORK). // // ... #endif // Уведомление воркеров (рабочих элементов, обрабатывающих доставку сообщений). // var postingWorkers = ReadDA(ref _postingWorkers); for (var i = 0; i < postingWorkers.Length; i++) { ILocalPostingWorker postingWorker = ReadDA(ref postingWorkers[i]); postingWorker.BreakLoopIdle(); #if TRG_NETFRAMEWORK if (!postingWorker.IsLoopAlive) { if (!runControl.HasStopRequested) { try { throw new InvalidOperationException( message: $"Один из рабочих элементов (#{(i + 1):d} из {postingWorkers.Length:d}), выполняющий доставку сообщений, находится в нерабочем состоянии.{Environment.NewLine}Данное событие не прерывает работу, если оно успешно записано в системый журнал событий."); } catch (Exception firstException) { WindowsEventLogUtilities.WriteFaultToEventLog(fault: firstException, failFastOnError: true, faultFormattingOptions: ExceptionInfoFormattingOptions.Full); } } } // TimeSpan?currentPostingDuration; if ((currentPostingDuration = postingWorker.CurrentlyPostingDuration) >= __PostingOperationDurationWarning) { var currentPostingSubscription = postingWorker.CurrentlyPostingSubscription; try { throw new InvalidOperationException( message: $"Время выполнения операции доставки, выполняемой одним из рабочих элементов (#{(i + 1):d} из {postingWorkers.Length:d}), достигло предела предупреждения '{__PostingOperationDurationWarning.ToString("c")}' и составляет '{currentPostingDuration.Value.ToString("c")}'.{Environment.NewLine}\tПодписка, согласно которой выполняется доставка:{currentPostingSubscription.FmtStr().GNLI2()}{Environment.NewLine}Данное событие не прерывает работу, если оно успешно записано в системый журнал событий.{Environment.NewLine}Указанное событие сообщает о том, что один из рабочих элементов уже слишком долго выполняет операцию доставки, что не является нормальным."); } catch (Exception firstException) { WindowsEventLogUtilities.WriteFaultToEventLog(fault: firstException, failFastOnError: true, faultFormattingOptions: ExceptionInfoFormattingOptions.Full); } } #else // TODO_HIGH: Implement logging (as for TRG_NETFRAMEWORK). // // ... #endif } } catch (Exception exception) { postingQueueEntry?.Dispose(exception); throw; } } else { postingToken = new LocalPostingToken(message: msg, disposeMessageAtEndOfPosting: disposeMessageAtEndOfPosting); if (disposeMessageAtEndOfPosting) { msg.Dispose(); } } return(postingToken); }
static void P_Loop(P_LoopStartState startState) { startState.EnsureNotNull(nameof(startState)); // var worker = startState.Worker; try { worker.P_IsLoopAlive = true; startState.LoopStartedEvent.Set(); // var ct = worker.P_CancellationToken; var breakIdleEvent = worker.P_BreakLoopIdleEvent; var queue = worker.P_Queue; var queueSpinLock = worker.P_QueueSpinLock; var cancellationEvent = ct.WaitHandle; var events = new[] { cancellationEvent, breakIdleEvent }; for (; !ct.IsCancellationRequested;) { var currentQueueEntry = queueSpinLock .Invoke( () => { if (queue.Count > 0) { return(queue.Peek()); } else { return(null); } }); if (currentQueueEntry == null) { // Очередь пуста. Переход в состояние простоя (ожидания сигнала либо отмены, либо изменения очереди). // WaitHandle signaledEvent; try { worker.P_IsLoopIdling = true; // signaledEvent = events.WaitAny(Timeout.Infinite); } catch (Exception exception) { if (exception is ThreadInterruptedException || (exception is ObjectDisposedException && ct.IsCancellationRequested)) { signaledEvent = cancellationEvent; } else { throw; } } finally { worker.P_IsLoopIdling = false; } if (signaledEvent == cancellationEvent || ct.IsCancellationRequested) { break; } else { continue; } } else if (ct.IsCancellationRequested) { break; } else { // Обработка элемента (доставка подписчикам). // for (; !ct.IsCancellationRequested;) { if (currentQueueEntry.TryGetNextPostingSubscription(out var postingToken, out var postingSubscription)) { if (postingSubscription.IsActive) { // Доставка подписчику. // var itemPostingState = new P_LoopSingleItemPostingState(worker: worker, subscription: postingSubscription); try { itemPostingState.OnStarted(); P_DoSingleMessagePostingAsync(token: postingToken, subscription: postingSubscription, ct: ct).Wait(); itemPostingState.OnCompleted(); } catch { try { itemPostingState.OnCompleted(); } catch { } // #if TRG_NETFRAMEWORK WindowsEventLogUtilities .WriteFaultToEventLog( fault: exception, failFastOnError: true, faultFormattingOptions: ExceptionInfoFormattingOptions.Full, message: $"Ошибка обработки доставки сообщения.{Environment.NewLine}\tБлок перехвата ошибки:{($"{__ThisTypeFullName}.{nameof(P_Loop)}").FmtStr().GNLI2()}"); #else // TODO_HIGH: Implement error logging (or something else). // // ... #endif } } } else { // Элемент уже полностью обработан. // if (!currentQueueEntry.IsDisposeRequested) { currentQueueEntry.Dispose(); } queueSpinLock .Invoke( () => { if (queue.Count > 0 && ReferenceEquals(currentQueueEntry, queue.Peek())) { queue.Dequeue(); } }); break; } } } } } finally { worker.P_IsLoopAlive = false; } }