void P_LogAttemptContinuation(Task <IRunControlAttemptSuccess> completion) { completion.EnsureNotNull(nameof(completion)); // RunControlAttemptLoggingData loggingData; if (completion.IsCanceled || completion.IsFaulted) { var endTimestamp = StopwatchUtilities.GetTimestampAsTimeSpan(); var attemptState = itrlck.Get(location: ref _attemptState); if (!(attemptState is null)) { // Если состояние попытки не было установлено операцией запуска/остановки, то такая попытка считается "пустой". См. код Start/Stop. // if (completion.IsCanceled) { loggingData = new RunControlAttemptLoggingData(runControl: RunControl, isStart: attemptState.IsStart, attemptNumber: attemptState.AttemptNumber, succeededAttemptCountBefore: attemptState.SucceededAttemptCountBefore, status: OperationCompletionStatusCode.Cancel, duration: endTimestamp.Subtract(ts: BeginTimestamp), correlationId: attemptState.CorrelationId); } else { Exception completionException = default; try { completion.GetAwaiter().GetResult(); } catch (Exception locException) { completionException = locException; } loggingData = new RunControlAttemptLoggingData(runControl: RunControl, isStart: attemptState.IsStart, attemptNumber: attemptState.AttemptNumber, succeededAttemptCountBefore: attemptState.SucceededAttemptCountBefore, status: OperationCompletionStatusCode.Fault, exception: completionException, duration: endTimestamp.Subtract(ts: BeginTimestamp), correlationId: attemptState.CorrelationId); } } else { loggingData = null; } }
// TODO: Put strings into the resources. // public RunControlAttemptSuccess SetSucceeded(bool isMaster) { var success = new RunControlAttemptSuccess(duration: StopwatchUtilities.GetTimestampAsTimeSpan().Subtract(ts: BeginTimestamp), isMaster: isMaster, props: AttemptState); if (_completionProxy.TrySetResult(result: success)) { return(success); } else { throw new EonException(message: $"Final state has already been set earlier.{Environment.NewLine}\tComponent:{RunControl.FmtStr().GNLI2()}"); } }
// TODO: Put strings into the resources. // Task <IRunControlAttemptSuccess> P_StopAsync(IContext ctx = default, bool finiteStop = default, AsyncOperationCallOption option = default) { try { if (!(option == AsyncOperationCallOption.ExistingOrNew || option == AsyncOperationCallOption.DefaultOrNew)) { throw new ArgumentOutOfRangeException(paramName: nameof(option)); } // return(TaskUtilities.RunOnDefaultScheduler(factory: doStopAsync)); } catch (Exception exception) { return(TaskUtilities.FromError <IRunControlAttemptSuccess>(exception)); } // async Task <IRunControlAttemptSuccess> doStopAsync() { ctx.ThrowIfCancellationRequested(); finiteStop = finiteStop || (Options & RunControlOptions.SingleStart) == RunControlOptions.SingleStart; var correlationId = ctx?.FullCorrelationId; var ctxCt = ctx.Ct(); var existingStopOp = default(RunControlStopOperationState <TComponent>); var newStopOp = default(RunControlStopOperationState <TComponent>); var component = TryReadDA(ref _component); var beginTimestamp = StopwatchUtilities.GetTimestampAsTimeSpan(); var tryCatchFinallyAffectsNewStopOp = true; try { var setStopOpResult = itrlck .Update( location: ref _existingStopOp, transform: (locCurrent, locPrevious) => { if (locCurrent is null || (finiteStop && !locCurrent.FiniteStop)) { return(locPrevious ?? (newStopOp = new RunControlStopOperationState <TComponent>(runControl: this, beginTimestamp: locCurrent?.BeginTimestamp ?? beginTimestamp, ct: ctxCt, finiteStop: finiteStop))); } else { return(locCurrent); } });
// TODO: Put strings into the resources. // public Task <IRunControlAttemptSuccess> StartAsync(IContext ctx = default) { try { var component = Component; if ((Options & RunControlOptions.ForbidStart) == RunControlOptions.ForbidStart) { throw new EonException(message: $"Опциями элемента управления запуском/остановкой компонента, запрещено выполнение операции запуска.{Environment.NewLine}\tКомпонент:{component.FmtStr().GNLI2()}"); } else { return(TaskUtilities.RunOnDefaultScheduler(factory: doStartAsync)); } } catch (Exception exception) { return(Task.FromException <IRunControlAttemptSuccess>(exception: exception)); } // async Task <IRunControlAttemptSuccess> doStartAsync() { ctx.ThrowIfCancellationRequested(); var ctxCt = ctx.Ct(); var existingStartOp = default(RunControlStartOperationState <TComponent>); var newStartOp = default(RunControlStartOperationState <TComponent>); var component = Component; var begimTimestamp = StopwatchUtilities.GetTimestampAsTimeSpan(); try { UpdDABool( location: ref _existingStartOp, transform: (locCurrent, locPrevious) => { if (locCurrent is null) { return(locPrevious ?? (newStartOp = new RunControlStartOperationState <TComponent>(runControl: this, beginTimestamp: begimTimestamp, ct: ctxCt))); } else { return(locCurrent); } }, current: out existingStartOp); if (ReferenceEquals(newStartOp, existingStartOp)) { // Поток (задача), который непосредственно выполняет запуск. // if (!itrlck.IncrementBool(location: ref _startOpAttemptNumber, maxInclusive: int.MaxValue, result: out var attemptNumber)) { throw new EonException(message: $"Невозможно присвоить операции последовательный номер попытки. Счётчик попыток достиг предельного значения.{Environment.NewLine}\tКомпонент:{component.FmtStr().GNLI2()}"); } // Если выполняется операция остановки, то дождаться её завершения. // var existingStopOp = itrlck.Get(ref _existingStopOp); if (!(existingStopOp is null)) { try { await existingStopOp.Completion.WaitCompletionAsync(ct : ctxCt).ConfigureAwait(false); } catch (Exception exception) { if (exception.HasSingleBaseExceptionOf <OperationCanceledException>(out var operationCancellationException)) { itrlck.SetNullBool(ref _existingStartOp, comparand: newStartOp); newStartOp.TrySetCanceled(ct: operationCancellationException.CancellationToken); } throw; } try { await existingStopOp.Completion.ConfigureAwait(false); } catch (Exception exception) when(exception.HasSingleBaseExceptionOf <OperationCanceledException>()) { throw new EonException( message: $"Невозможно выполнить запуск компонента, так как ранее для компонента была инициирована операция остановки, которая была прервана или отменена.{Environment.NewLine}\tКомпонент:{component.FmtStr().GNLI2()}"); } catch (Exception exception) { throw new EonException( message: $"Невозможно выполнить запуск компонента, так как ранее для компонента была инициирована операция остановки, которая завершилась сбоем. Детали сбоя см. во внутреннем стеке данного исключения.{Environment.NewLine}\tКомпонент:{component.FmtStr().GNLI2()}", innerException: exception); } if ((Options & RunControlOptions.MultipleStart) == RunControlOptions.MultipleStart) { if (existingStopOp.FiniteStop) { newStartOp.TrySetCanceled(); throw new OperationCanceledException( message: $"Запуск компонента отменён, так как ранее поступил запрос остановки компонента без возможности перезапуска.{Environment.NewLine}\tКомпонент:{component.FmtStr().GNLI2()}"); } else if (!itrlck.SetNullBool(ref _existingStopOp, comparand: existingStopOp)) { throw new EonException( message: $"Не удалось привести управляющий элемент запуска/остановки компонента к предстартовому состоянию.{Environment.NewLine}\tКомпонент:{component.FmtStr().GNLI2()}"); } } else { newStartOp.TrySetCanceled(); throw new OperationCanceledException( message: $"Запуск компонента отменён, так как ранее поступил запрос остановки компонента и опциями элемента, управляющего запуском/остановкой компонента, не определена возможность многократного запуска компонента.{Environment.NewLine}\tКомпонент:{component.FmtStr().GNLI2()}"); } } // Запуск. // IContext locCtx = default; try { try { locCtx = P_CreateStartCtx(startOp: newStartOp, outerCtx: ctx); newStartOp.AttemptState = ReadDA(ref _attemptStateFactory) (args: new RunControlAttemptStateFactoryArgs( runControl: this, completion: newStartOp.Completion, context: locCtx, isStart: true, attemptNumber: attemptNumber, succeededAttemptCountBefore: vlt.Read(ref _startOpSucceededCount))); } catch (Exception exception) { if (exception.HasSingleBaseExceptionOf <OperationCanceledException>(baseException: out var operationCancellationException)) { itrlck.SetNullBool(location: ref _existingStartOp, comparand: newStartOp); newStartOp.TrySetCanceled(ct: operationCancellationException.CancellationToken); } throw; } // Процедура предзапуска. // try { locCtx.ThrowIfCancellationRequested(); await P_BeforeStartAsync(state : newStartOp.AttemptState).ConfigureAwait(false); } catch (Exception exception) { itrlck.SetNullBool(ref _existingStartOp, comparand: newStartOp); if (exception.HasSingleBaseExceptionOf <OperationCanceledException>(out var operationCancellationException)) { newStartOp.TrySetCanceled(ct: operationCancellationException.CancellationToken); throw; } else { throw new EonException(message: $"Сбой во время процедуры предзапуска компонента.{Environment.NewLine}\tКомпонент:{component.FmtStr().GNLI2()}", innerException: exception); } } // Процедура запуска. // try { locCtx.ThrowIfCancellationRequested(); var startTask = ReadDA(ref _startTaskFactory)(arg : newStartOp.AttemptState); if (startTask is null) { throw new EonException(message: $"Невозможно выполнить операцию запуска компонента, так как функция создания задачи выполнения запуска возвратила недопустимый результат '{startTask.FmtStr().G()}'.{Environment.NewLine}\tКомпонент:{component.FmtStr().GNLI2()}"); } await startTask.ConfigureAwait(false); } catch (Exception exception) { if (exception.HasSingleBaseExceptionOf <OperationCanceledException>(out var operationCancellationException)) { itrlck.SetNullBool(ref _existingStartOp, comparand: newStartOp); newStartOp.TrySetCanceled(ct: operationCancellationException.CancellationToken); throw; } else { throw new EonException(message: $"Сбой во время запуска компонента.{Environment.NewLine}\tКомпонент:{component.FmtStr().GNLI2()}", innerException: exception); } } itrlck.IncrementBool(location: ref _startOpSucceededCount, maxInclusive: int.MaxValue); var result = newStartOp.SetSucceeded(isMaster: true); P_TryFireAfterStartAsynchronously(result: result); return(result); } finally { locCtx?.Dispose(); } } else { newStartOp?.TrySetCanceled(synchronously: true); // TODO_HIGH: Возможно, нужно обрабатывать такую ситуацию, когда данный вызов получит результат в виде отменённой операции, потому что уже выполняющий другой вызов был отменён. Такая ситуация с точки зрения вызывающий стороны не совсем очевидна и предсказуема. // + нужно реализовать трансляцию хода выполнения из уже выполняющегося вызова в данный. // if (ctxCt == existingStartOp.Ct) { // Один и тот же токен отмены из разных вызовов. // return(new RunControlAttemptSuccess(other: await existingStartOp.Completion.ConfigureAwait(false), isMaster: false)); } else { return(new RunControlAttemptSuccess(other: await existingStartOp.Completion.WaitResultAsync(ct: ctxCt).ConfigureAwait(false), isMaster: false)); } } } catch (Exception exception) { if (ReferenceEquals(existingStartOp, newStartOp) && !(newStartOp is null)) { if (newStartOp.TrySetException(exception: exception, synchronously: true)) { // Здесь будет вызвано исключение. // await newStartOp.Completion; } throw; } else { throw; } } finally { if (!ReferenceEquals(existingStartOp, newStartOp)) { newStartOp?.TrySetCanceled(synchronously: true); } } } }
// TODO: Put strings into the resources. // Task <IXAppScopeInstance> P_ResetComponentAsync(Func <IContext, bool> breakCondition = default, ITriggerSignalProperties triggerSignalProps = default, bool doFailureResponse = default, IContext ctx = default) { try { if (breakCondition is null) { breakCondition = (locContext) => { locContext.ThrowIfCancellationRequested(); return(!IsActive || HasDeactivationRequested); } } ; // var startTimestamp = StopwatchUtilities.GetTimestampAsTimeSpan(); IContext locCtx = default; try { var resetNumber = Interlocked.Increment(ref _resetCounter); var failureResponseDlg = ReadIA(ref _resetFailureResponseDelegate, isNotRequired: true, locationName: nameof(_resetFailureResponseDelegate)); var state = new P_ResetState(); locCtx = CreateScopedContext(outerCtx: ctx, localTag: state); var logMessagePrologue = $"{(resetNumber == 0 ? "Установка компонента." : $"Замена (переустановка) компонента. Порядковый номер замены: {resetNumber:d}.")} ИД корреляции: {locCtx.FullCorrelationId}.{(triggerSignalProps is null ? string.Empty : $"{Environment.NewLine}\tСобытие-инициатор:{triggerSignalProps.FmtStr().GNLI2()}")}"; if (locCtx.IsCancellationRequested()) { return(Task.FromCanceled <IXAppScopeInstance>(cancellationToken: locCtx.Ct())); } var resetTask = TaskUtilities.RunOnDefaultScheduler(factory: async() => await doResetAsync(locCtx: locCtx).ConfigureAwait(false)); // Вывод результатов ресета в лог. // #if !DO_NOT_USE_EON_LOGGING_API resetTask .ContinueWith( continuationAction: locTask => { var locDuration = StopwatchUtilities.GetTimestampAsTimeSpan().Subtract(ts: startTimestamp); if (locTask.IsFaulted) { this .IssueError( messagePrologue: logMessagePrologue, message: $"Длительность:{locDuration.ToString("c").FmtStr().GNLI()}{Environment.NewLine}Сбой.", error: locTask.Exception, includeErrorInIssueFaultException: true, severityLevel: locTask.Exception.GetMostHighSeverityLevel(baseLevel: SeverityLevel.Medium)); locTask.Exception.MarkAsObserved(); } else if (locTask.IsCanceled) { this .IssueWarning( messagePrologue: logMessagePrologue, message: $"Длительность:{locDuration.ToString("c").FmtStr().GNLI()}{Environment.NewLine}Отменено или прервано.", severityLevel: SeverityLevel.Medium); } else { this .IssueInformation( messagePrologue: logMessagePrologue, message: $"Длительность:{locDuration.ToString("c").FmtStr().GNLI()}{Environment.NewLine}Успешно выполнено.{Environment.NewLine}\tНовый экземпляр компонента:{locTask.Result.FmtStr().GNLI2()}", severityLevel: SeverityLevel.Medium); } }, continuationOptions: TaskContinuationOptions.ExecuteSynchronously); #endif // Обработка сбоя ресета. // Task failureResponseTask; if (doFailureResponse && !(failureResponseDlg is null)) { failureResponseTask = resetTask .ContinueWith( continuationFunction: locTask => { if (!(state.PreviousComponent is null)) { // Корректирующие действие сбоя ресета выполняется только в случае именно замены компонента. // return (doFailureCorrectiveAsync( locFailure: locTask.Exception, responseDlg: failureResponseDlg, locCtx: locCtx, logMessagePrologue: logMessagePrologue)); } else { return(TaskUtilities.FromCanceled()); } }, continuationOptions: TaskContinuationOptions.PreferFairness | TaskContinuationOptions.OnlyOnFaulted) .Unwrap(); // Вывод результатов обработки сбоя в лог. // #if !DO_NOT_USE_EON_LOGGING_API failureResponseTask .ContinueWith( continuationAction: locTask => { if (locTask.IsFaulted) { this .IssueError( messagePrologue: logMessagePrologue, message: $"Корректирующее действие при сбое замены (переустановки) компонента.{Environment.NewLine}Сбой корректирующего действия.", error: locTask.Exception, includeErrorInIssueFaultException: true, severityLevel: locTask.Exception.GetMostHighSeverityLevel(baseLevel: SeverityLevel.High)); } else { this .IssueInformation( messagePrologue: logMessagePrologue, message: $"Корректирующее действие при сбое замены (переустановки) компонента.{Environment.NewLine}Корректирующее действие успешно выполнено.", severityLevel: SeverityLevel.Medium); } }, continuationOptions: TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled); #endif }