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

Last change on this file since 16993 was 16993, checked in by dpiringe, 2 years ago

#2924:

  • added IEnumerable<T> GetInstances<T>(params object[] args) where T: class and IEnumerable<object> GetInstances(Type type, params object[] args) method to IApplicationManager and implemented them in LightweightApplicationManager -> to instantiate types with specific constructor arguments
  • added RunnerState State { get; } property in IRunnerHost, was already in RunnerHost
  • added user authentication for NativeRunnerHost
  • added optional check for a running docker daemon and available image for type DockerRunnerHost + Exception DockerException
  • added caching of the saved IApplication in ApplicationRunner to prevent a new instance every get call
  • removed System.ServiceModel.Primitives NuGet package
  • lots of formatting
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      process.BeginOutputReadLine();
89      process.BeginErrorReadLine();
90
91      if (!runner.QuietMode) {
92        process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
93        process.ErrorDataReceived += (s, e) => Console.WriteLine(e.Data);
94      }
95      State = RunnerState.Running;
96      if (await task) State = RunnerState.Cancelled;
97      else State = RunnerState.Stopped;
98
99    }
100
101    /// <summary>
102    /// Pauses the runners. The childprocess gets notified with the help of the standard input and a serialized RunnerJob.
103    /// To pause the runner, the state must be "Running".
104    /// </summary>
105    public void Pause() {
106      if (State != RunnerState.Running) throw new InvalidOperationException("Runner must be in state 'Running'!");
107      Runner.Pause(process.StandardInput.BaseStream);
108      State = RunnerState.Paused;
109    }
110
111    /// <summary>
112    /// Resumes a paused runner. The childprocess gets notified with the help of the standard input and a serialized RunnerJob.
113    /// To resume the runner, the state must be "Paused".
114    /// </summary>
115    public void Resume() {
116      if (State != RunnerState.Paused) throw new InvalidOperationException("Runner must be in state 'Paused'!");
117      Runner.Resume(process.StandardInput.BaseStream);
118      State = RunnerState.Running;
119    }
120
121    /// <summary>
122    /// Creates a new LinkedTokenSource and a TaskCompletionSource.
123    /// When the specified token gets cancelled, a cancel requests gets send to the childprocess.
124    /// Afterwards the main process waits for the exit of the child process and sets a result of the TaskCompletionSource.
125    /// When the child process gets finished without requested cancellation, the linked token gets cancelled and a result set.
126    /// </summary>
127    private Task<bool> RegisterCancellation(CancellationToken token) {
128      if (process != null && State == RunnerState.Starting) {
129        var cts = CancellationTokenSource.CreateLinkedTokenSource(token);
130        var tcs = new TaskCompletionSource<bool>();
131
132        process.Exited += (s, e) => cts.Cancel();
133        cts.Token.Register(() => {
134          if (!process.HasExited) {
135            Runner.Cancel(process.StandardInput.BaseStream);
136            process.WaitForExit();
137          }
138          tcs.SetResult(token.IsCancellationRequested);
139        });
140        return tcs.Task;
141      }
142      return null;
143    }
144  }
145}
Note: See TracBrowser for help on using the repository browser.