示例#1
0
        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);
        }
示例#2
0
        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;
            }
        }