Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PersistenceReintegration/HeuristicLab.Problems.ExternalEvaluation/3.4/Drivers/EvaluationProcessChannel.cs @ 15428

Last change on this file since 15428 was 15018, checked in by gkronber, 8 years ago

#2520 introduced StorableConstructorFlag type for StorableConstructors

File size: 6.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Diagnostics;
24using System.IO;
25using Google.ProtocolBuffers;
26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Persistence;
29
30namespace HeuristicLab.Problems.ExternalEvaluation {
31  [Item("EvaluationProcessChannel", "A channel that launches an external application in a new process and communicates with that process via stdin and stdout.")]
32  [StorableType("1a8626d1-3e6f-49f0-99d6-083f6d88f07c")]
33  public class EvaluationProcessChannel : EvaluationChannel {
34
35    #region Fields & Properties
36    private Process process;
37    [Storable]
38    private string executable;
39    public string Executable {
40      get { return executable; }
41      set {
42        if (IsInitialized) throw new InvalidOperationException(Name + ": Cannot change the executable path as the process has already been started.");
43        if (value == executable) return;
44        executable = value;
45        UpdateName();
46        OnExecutableChanged();
47      }
48    }
49    [Storable]
50    private string arguments;
51    public string Arguments {
52      get { return arguments; }
53      set {
54        if (IsInitialized) throw new InvalidOperationException(Name + ": Cannot change the arguments as the process has already been started.");
55        if (value == arguments) return;
56        arguments = value;
57        UpdateName();
58        OnArgumentsChanged();
59      }
60    }
61    private EvaluationStreamChannel streamingChannel;
62    #endregion
63
64    #region Construction & Cloning
65    [StorableConstructor]
66    protected EvaluationProcessChannel(StorableConstructorFlag deserializing) : base(deserializing) { }
67    protected EvaluationProcessChannel(EvaluationProcessChannel original, Cloner cloner)
68      : base(original, cloner) {
69      executable = original.executable;
70      arguments = original.arguments;
71      UpdateName();
72    }
73    public override IDeepCloneable Clone(Cloner cloner) {
74      return new EvaluationProcessChannel(this, cloner);
75    }
76
77    public EvaluationProcessChannel() : this(String.Empty, String.Empty) { }
78    public EvaluationProcessChannel(string executable, string arguments)
79      : base() {
80      this.executable = executable;
81      this.arguments = arguments;
82      UpdateName();
83    }
84    [StorableHook(HookType.AfterDeserialization)]
85    private void AfterDeserialization() {
86      UpdateName();
87    }
88    #endregion
89
90    #region IExternalEvaluationChannel Members
91    public override void Open() {
92      if (!String.IsNullOrEmpty(executable.Trim())) {
93        base.Open();
94        process = new Process();
95        process.StartInfo = new ProcessStartInfo(executable, arguments);
96        process.StartInfo.UseShellExecute = false;
97        process.StartInfo.RedirectStandardInput = true;
98        process.StartInfo.RedirectStandardOutput = true;
99        process.EnableRaisingEvents = true; // required to be notified of exit
100        process.Start();
101        Stream processStdOut = process.StandardOutput.BaseStream;
102        Stream processStdIn = process.StandardInput.BaseStream;
103        OnProcessStarted();
104        process.Exited += new EventHandler(process_Exited);
105        streamingChannel = new EvaluationStreamChannel(processStdOut, processStdIn);
106        streamingChannel.Open();
107      } else throw new InvalidOperationException(Name + ": Cannot open the process channel because the executable is not defined.");
108    }
109
110    public override void Send(IMessage message) {
111      try {
112        streamingChannel.Send(message);
113      } catch {
114        Close();
115        throw;
116      }
117    }
118
119    public override IMessage Receive(IBuilder builder, ExtensionRegistry extensions) {
120      try {
121        return streamingChannel.Receive(builder, extensions);
122      } catch {
123        Close();
124        throw;
125      }
126    }
127
128    public override void Close() {
129      base.Close();
130      if (process != null) {
131        if (!process.HasExited) {
132          streamingChannel.Close();
133          if (!process.HasExited) {
134            try {
135              process.CloseMainWindow();
136              process.WaitForExit(1000);
137              process.Close();
138            } catch { }
139          }
140          // for some reasons the event process_Exited does not fire
141          OnProcessExited();
142        }
143        process = null;
144      }
145    }
146
147    #endregion
148
149    #region Event handlers (process)
150    private void process_Exited(object sender, EventArgs e) {
151      if (IsInitialized) {
152        if (streamingChannel.IsInitialized) streamingChannel.Close();
153        IsInitialized = false;
154        process = null;
155      }
156      OnProcessExited();
157    }
158    #endregion
159
160    #region Events
161    public event EventHandler ExecutableChanged;
162    protected void OnExecutableChanged() {
163      EventHandler handler = ExecutableChanged;
164      if (handler != null) handler(this, EventArgs.Empty);
165    }
166
167    public event EventHandler ArgumentsChanged;
168    protected void OnArgumentsChanged() {
169      EventHandler handler = ArgumentsChanged;
170      if (handler != null) handler(this, EventArgs.Empty);
171    }
172
173    public event EventHandler ProcessStarted;
174    private void OnProcessStarted() {
175      EventHandler handler = ProcessStarted;
176      if (handler != null) handler(this, EventArgs.Empty);
177    }
178
179    public event EventHandler ProcessExited;
180    private void OnProcessExited() {
181      EventHandler handler = ProcessExited;
182      if (handler != null) handler(this, EventArgs.Empty);
183    }
184    #endregion
185
186    #region Auxiliary Methods
187    private void UpdateName() {
188      name = string.Format("ProcessChannel {0} {1}", Path.GetFileNameWithoutExtension(executable), arguments);
189      OnNameChanged();
190    }
191    #endregion
192  }
193}
Note: See TracBrowser for help on using the repository browser.