Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Problems.ExternalEvaluation/3.3/Drivers/EvaluationProcessChannel.cs @ 7005

Last change on this file since 7005 was 6470, checked in by epitzer, 13 years ago

Automatically generate TCPChannel, ProcessChannel and ServiceClient names form parameters (#1526).

File size: 6.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2011 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.Default.CompositeSerializers.Storable;
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  [StorableClass]
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(bool 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      }
114      catch {
115        Close();
116        throw;
117      }
118    }
119
120    public override IMessage Receive(IBuilder builder) {
121      try {
122        return streamingChannel.Receive(builder);
123      }
124      catch {
125        Close();
126        throw;
127      }
128    }
129
130    public override void Close() {
131      base.Close();
132      if (process != null) {
133        if (!process.HasExited) {
134          streamingChannel.Close();
135          if (!process.HasExited) {
136            try {
137              process.CloseMainWindow();
138              process.WaitForExit(1000);
139              process.Close();
140            }
141            catch { }
142          }
143          // for some reasons the event process_Exited does not fire
144          OnProcessExited();
145        }
146        process = null;
147      }
148    }
149
150    #endregion
151
152    #region Event handlers (process)
153    private void process_Exited(object sender, EventArgs e) {
154      if (IsInitialized) {
155        if (streamingChannel.IsInitialized) streamingChannel.Close();
156        IsInitialized = false;
157        process = null;
158      }
159      OnProcessExited();
160    }
161    #endregion
162
163    #region Events
164    public event EventHandler ExecutableChanged;
165    protected void OnExecutableChanged() {
166      EventHandler handler = ExecutableChanged;
167      if (handler != null) handler(this, EventArgs.Empty);
168    }
169
170    public event EventHandler ArgumentsChanged;
171    protected void OnArgumentsChanged() {
172      EventHandler handler = ArgumentsChanged;
173      if (handler != null) handler(this, EventArgs.Empty);
174    }
175
176    public event EventHandler ProcessStarted;
177    private void OnProcessStarted() {
178      EventHandler handler = ProcessStarted;
179      if (handler != null) handler(this, EventArgs.Empty);
180    }
181
182    public event EventHandler ProcessExited;
183    private void OnProcessExited() {
184      EventHandler handler = ProcessExited;
185      if (handler != null) handler(this, EventArgs.Empty);
186    }
187    #endregion
188
189    #region Auxiliary Methods
190    private void UpdateName() {
191      name = string.Format("ProcessChannel {0} {1}", Path.GetFileNameWithoutExtension(executable), arguments);
192      OnNameChanged();
193    }
194    #endregion
195  }
196}
Note: See TracBrowser for help on using the repository browser.