Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2924_DotNetCoreMigration/HeuristicLab.PluginInfrastructure/3.3/Isolation/RunnerHost.cs @ 16984

Last change on this file since 16984 was 16984, checked in by dpiringe, 5 years ago

#2924:

  • merged projects HeuristicLab.PluginInfrastructure.Runner and HeuristicLab.PluginInfrastructure
  • applied changes of code reviews (13.05.2019 and 22.05.2019) -> the old Runner is now RunnerHost and uses a Runner, which is executed on the child process
  • added Type GetType(string) to IApplicationManager and implemented it for LightweightApplicationManager
  • removed IActivator and IActivatorContext
  • deleted unused types like PluginDescriptionIterator
File size: 5.4 KB
Line 
1using System;
2using System.Diagnostics;
3using System.IO;
4using System.Threading;
5using System.Threading.Tasks;
6
7namespace HeuristicLab.PluginInfrastructure {
8  /// <summary>
9  /// Enum for the different RunnerStates.
10  /// </summary>
11  public enum RunnerState {
12    Created,
13    Cancelled,
14    Starting,
15    Running,
16    Paused,
17    Stopping,
18    Stopped
19  }
20
21  /// <summary>
22  /// Abstract <c>RunnerBase</c> class, which implements the IRunner interface.
23  /// Every specific runner implementation should inherit this class.
24  /// </summary>
25  public abstract class RunnerHost : IRunnerHost {
26
27    #region Vars
28    protected Process process;
29    #endregion
30
31    #region Properties
32    /// <summary>
33    /// The programm, which should be used. For example 'docker'.
34    /// </summary>
35    protected string Program { get; private set; }
36    /// <summary>
37    /// The specific start arguments for the programm.
38    /// </summary>
39    protected string StartArgument { get; private set; }
40    protected string UserName { get; private set; }
41    protected string Password { get; private set; }
42    protected string Domain { get; private set; }
43
44    /// <summary>
45    /// The runner state.
46    /// </summary>
47    public RunnerState State { get; protected set; } = RunnerState.Created;
48    #endregion
49
50    #region Constructors
51    protected RunnerHost(string program, string startArgument, string userName, string password, string domain) {
52      Program = program;
53      StartArgument = startArgument + " --StartAsRunnerHost";
54      UserName = userName;
55      Password = password;
56      Domain = domain;
57    }
58    #endregion
59
60    public virtual void Run(IRunner runner) => RunAsync(runner, null).Wait();
61    public virtual async Task RunAsync(IRunner runner, CancellationToken? token = null) {
62      if (State != RunnerState.Created)
63        throw new InvalidOperationException("Runner must be in state 'Created'.");
64      State = RunnerState.Starting;
65      process = new Process {
66        StartInfo = new ProcessStartInfo {
67          FileName = Program,
68          Arguments = StartArgument,
69          UseShellExecute = false,
70          RedirectStandardOutput = true,
71          RedirectStandardInput = true,
72          RedirectStandardError = true,
73          CreateNoWindow = false,
74          UserName = string.IsNullOrEmpty(UserName) ? null : UserName,
75          PasswordInClearText = string.IsNullOrEmpty(Password) ? null : Password,
76          Domain = string.IsNullOrEmpty(Domain) ? null: Domain,
77          WorkingDirectory = Directory.GetCurrentDirectory()
78        },
79        EnableRaisingEvents = true
80      };
81      process.Start();
82      // registers a task for cancellation, prevents the use of polling (while-loop)
83      var task = RegisterCancellation(token.HasValue ? token.Value : CancellationToken.None);
84
85      // write config to standardinput, runner listens on this and deserializes the config
86      Runner.Serialize(runner, process.StandardInput.BaseStream);
87
88      if (!runner.QuietMode) {
89        process.BeginOutputReadLine();
90        process.BeginErrorReadLine();
91        process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
92        process.ErrorDataReceived += (s, e) => Console.WriteLine(e.Data);
93      }
94      State = RunnerState.Running;
95      if (await task) State = RunnerState.Cancelled;
96      else State = RunnerState.Stopped;
97
98    }
99
100    /// <summary>
101    /// Pauses the runners. The childprocess gets notified with the help of the standard input and a serialized RunnerJob.
102    /// To pause the runner, the state must be "Running".
103    /// </summary>
104    public void Pause() {
105      if (State != RunnerState.Running) throw new InvalidOperationException("Runner must be in state 'Running'!");
106      Runner.Pause(process.StandardInput.BaseStream);
107      State = RunnerState.Paused;
108    }
109
110    /// <summary>
111    /// Resumes a paused runner. The childprocess gets notified with the help of the standard input and a serialized RunnerJob.
112    /// To resume the runner, the state must be "Paused".
113    /// </summary>
114    public void Resume() {
115      if (State != RunnerState.Paused) throw new InvalidOperationException("Runner must be in state 'Paused'!");
116      Runner.Resume(process.StandardInput.BaseStream);
117      State = RunnerState.Running;
118    }
119
120    /// <summary>
121    /// Creates a new LinkedTokenSource and a TaskCompletionSource.
122    /// When the specified token gets cancelled, a cancel requests gets send to the childprocess.
123    /// Afterwards the main process waits for the exit of the child process and sets a result of the TaskCompletionSource.
124    /// When the child process gets finished without requested cancellation, the linked token gets cancelled and a result set.
125    /// </summary>
126    /// <param name="token"></param>
127    /// <returns></returns>
128    private Task<bool> RegisterCancellation(CancellationToken token) {
129      if (process != null && State == RunnerState.Starting) {
130        var cts = CancellationTokenSource.CreateLinkedTokenSource(token);
131        var tcs = new TaskCompletionSource<bool>();
132
133        process.Exited += (s, e) => cts.Cancel();
134        cts.Token.Register(() => {
135          if (!process.HasExited) {
136            Runner.Cancel(process.StandardInput.BaseStream);
137            process.WaitForExit();
138          }
139          tcs.SetResult(token.IsCancellationRequested);
140        });
141        return tcs.Task;
142      }
143      return null;
144    }
145  }
146}
Note: See TracBrowser for help on using the repository browser.