/// <summary> /// Initializes a new instance of <see cref="ProcessSupervisor"/> /// </summary> /// <param name="workingDirectory"> /// The working directory to start the process in. /// </param> /// <param name="processPath"> /// The path to the process. /// </param> /// <param name="processRunType"> /// The process run type. /// </param> /// <param name="arguments"> /// Arguments to be passed to the process. /// </param> /// <param name="environmentVariables"> /// Environment variables that are set before the process starts. /// </param> public ProcessSupervisor( ProcessRunType processRunType, string workingDirectory, string processPath, string arguments = null, StringDictionary environmentVariables = null, bool captureStdErr = false) { _workingDirectory = workingDirectory; _processPath = processPath; _arguments = arguments ?? string.Empty; _environmentVariables = environmentVariables; _captureStdErr = captureStdErr; _logger = LogProvider.GetLogger($"ProcessSupervisor-{processPath}"); _processStateMachine .Configure(State.NotStarted) .Permit(Trigger.Start, State.Running); _startErrorTrigger = _processStateMachine.SetTriggerParameters <Exception>(Trigger.StartError); _stopTrigger = _processStateMachine.SetTriggerParameters <TimeSpan?>(Trigger.Stop); _processStateMachine .Configure(State.Running) .OnEntryFrom(Trigger.Start, OnStart) .PermitIf( Trigger.ProcessExit, State.ExitedSuccessfully, () => processRunType == ProcessRunType.SelfTerminating && _process.ExitCode == 0, "SelfTerminating && ExitCode==0") .PermitIf( Trigger.ProcessExit, State.ExitedWithError, () => processRunType == ProcessRunType.SelfTerminating && _process.ExitCode != 0, "SelfTerminating && ExitCode!=0") .PermitIf( Trigger.ProcessExit, State.ExitedUnexpectedly, () => processRunType == ProcessRunType.NonTerminating, "NonTerminating") .Permit(Trigger.Stop, State.Stopping) .Permit(Trigger.StartError, State.StartFailed); _processStateMachine .Configure(State.StartFailed) .OnEntryFrom(_startErrorTrigger, OnStartError); _processStateMachine .Configure(State.Stopping) .Permit(Trigger.ProcessExit, State.ExitedSuccessfully) .OnEntryFromAsync(_stopTrigger, OnStop); _processStateMachine .Configure(State.StartFailed) .Permit(Trigger.Start, State.Running); _processStateMachine .Configure(State.ExitedSuccessfully) .Permit(Trigger.Start, State.Running); _processStateMachine .Configure(State.ExitedUnexpectedly) .Permit(Trigger.Start, State.Running); _processStateMachine.OnTransitioned(transition => { _logger.Info($"State transition from {transition.Source} to {transition.Destination}"); StateChanged?.Invoke(transition.Destination); }); }
/// <summary> /// Initializes a new instance of <see cref="ProcessSupervisor"/> /// </summary> /// <param name="workingDirectory"> /// The working directory to start the process in. /// </param> /// <param name="processPath"> /// The path to the process. /// </param> /// <param name="processRunType"> /// The process run type. /// </param> /// <param name="loggerFactory"> /// A logger factory. /// </param> /// <param name="arguments"> /// Arguments to be passed to the process. /// </param> /// <param name="environmentVariables"> /// Environment variables that are set before the process starts. /// </param> /// <param name="captureStdErr"> /// A flag to indicated whether to capture standard error output. /// </param> public ProcessSupervisor( ILoggerFactory loggerFactory, ProcessRunType processRunType, string workingDirectory, string processPath, string arguments = null, StringDictionary environmentVariables = null, bool captureStdErr = false) { _loggerFactory = loggerFactory; _workingDirectory = workingDirectory; _processPath = processPath; _arguments = arguments ?? string.Empty; _environmentVariables = environmentVariables; _captureStdErr = captureStdErr; _logger = loggerFactory.CreateLogger($"{nameof(LittleForker)}.{nameof(ProcessSupervisor)}-{processPath}"); _processStateMachine .Configure(State.NotStarted) .Permit(Trigger.Start, State.Running); _startErrorTrigger = _processStateMachine.SetTriggerParameters <Exception>(Trigger.StartError); _stopTrigger = _processStateMachine.SetTriggerParameters <TimeSpan?>(Trigger.Stop); _processStateMachine .Configure(State.Running) .OnEntryFrom(Trigger.Start, OnStart) .PermitIf( Trigger.ProcessExit, State.ExitedSuccessfully, () => processRunType == ProcessRunType.SelfTerminating && _process.HasExited && _process.ExitCode == 0, "SelfTerminating && ExitCode==0") .PermitIf( Trigger.ProcessExit, State.ExitedWithError, () => processRunType == ProcessRunType.SelfTerminating && _process.HasExited && _process.ExitCode != 0, "SelfTerminating && ExitCode!=0") .PermitIf( Trigger.ProcessExit, State.ExitedUnexpectedly, () => processRunType == ProcessRunType.NonTerminating && _process.HasExited, "NonTerminating and died.") .Permit(Trigger.Stop, State.Stopping) .Permit(Trigger.StartError, State.StartFailed); _processStateMachine .Configure(State.StartFailed) .OnEntryFrom(_startErrorTrigger, OnStartError); _processStateMachine .Configure(State.Stopping) .OnEntryFromAsync(_stopTrigger, OnStop) .PermitIf(Trigger.ProcessExit, State.ExitedSuccessfully, () => processRunType == ProcessRunType.NonTerminating && !_killed && _process.HasExited && _process.ExitCode == 0, "NonTerminating and shut down cleanly") .PermitIf(Trigger.ProcessExit, State.ExitedWithError, () => processRunType == ProcessRunType.NonTerminating && !_killed && _process.HasExited && _process.ExitCode != 0, "NonTerminating and shut down with non-zero exit code") .PermitIf(Trigger.ProcessExit, State.ExitedKilled, () => processRunType == ProcessRunType.NonTerminating && _killed && _process.HasExited && _process.ExitCode != 0, "NonTerminating and killed."); _processStateMachine .Configure(State.StartFailed) .Permit(Trigger.Start, State.Running); _processStateMachine .Configure(State.ExitedSuccessfully) .Permit(Trigger.Start, State.Running); _processStateMachine .Configure(State.ExitedUnexpectedly) .Permit(Trigger.Start, State.Running); _processStateMachine .Configure(State.ExitedKilled) .Permit(Trigger.Start, State.Running); _processStateMachine.OnTransitioned(transition => { _logger.LogInformation($"State transition from {transition.Source} to {transition.Destination}"); StateChanged?.Invoke(transition.Destination); }); }