source: trunk/HeuristicLab.Clients.Hive.Slave/3.3/Executor.cs @ 17445

Last change on this file since 17445 was 17445, checked in by jkarder, 2 years ago

#3060: catch unhandled exceptions thrown by IOptimizer.StartAsync()

File size: 7.3 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 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.Threading;
24using HeuristicLab.Clients.Hive.SlaveCore.Properties;
25using HeuristicLab.Common;
26using HeuristicLab.Core;
27using HeuristicLab.Hive;
28
29namespace HeuristicLab.Clients.Hive.SlaveCore {
30  /// <summary>
31  /// The executor runs in the appdomain and handles the execution of an Hive task.
32  /// </summary>
33  public class Executor : MarshalByRefObject, IDisposable {
34    private bool wasTaskAborted = false;
35    private AutoResetEvent pauseStopSem = new AutoResetEvent(false);
36    private AutoResetEvent startTaskSem = new AutoResetEvent(false); // block start method call
37    private AutoResetEvent taskStartedSem = new AutoResetEvent(false); // make pause or stop wait until start is finished
38    private ExecutorQueue executorQueue;
39    private bool taskDataInvalid = false; // if true, the jobdata is not sent when the task is failed
40    private ITask task;
41    private DateTime creationTime;
42
43    public Guid TaskId { get; set; }
44    public int CoresNeeded { get; set; }
45    public int MemoryNeeded { get; set; }
46    public bool IsStopping { get; set; }
47    public bool IsPausing { get; set; }
48    public bool HasFailed { get; set; }
49
50    public Exception CurrentException;
51    public String CurrentExceptionStr {
52      get {
53        if (CurrentException != null) {
54          return CurrentException.ToString();
55        } else {
56          return string.Empty;
57        }
58      }
59    }
60
61    public ExecutorQueue ExecutorCommandQueue {
62      get { return executorQueue; }
63    }
64
65    private ExecutionState ExecutionState {
66      get { return task != null ? task.ExecutionState : HeuristicLab.Core.ExecutionState.Stopped; }
67    }
68
69    public TimeSpan ExecutionTime {
70      get { return task != null ? task.ExecutionTime : new TimeSpan(0, 0, 0); }
71    }
72
73    public Executor() {
74      IsStopping = false;
75      IsPausing = false;
76      executorQueue = new ExecutorQueue();
77    }
78
79    public void Start(byte[] serializedJob) {
80      try {
81        creationTime = DateTime.Now;
82        task = PersistenceUtil.Deserialize<ITask>(serializedJob);
83
84        RegisterJobEvents();
85
86        task.Start();
87        if (!startTaskSem.WaitOne(Settings.Default.ExecutorSemTimeouts) && !HasFailed) {
88          throw new TimeoutException("Timeout when starting the task. TaskStarted event was not fired.");
89        }
90      }
91      catch (Exception e) {
92        HandleStartStopPauseError(e);
93      }
94      finally {
95        taskStartedSem.Set();
96      }
97    }
98
99    public void Pause() {
100      IsPausing = true;
101      // wait until task is started. if this does not happen, the Task is null an we give up
102      taskStartedSem.WaitOne(Settings.Default.ExecutorSemTimeouts);
103      if (task == null) {
104        HandleStartStopPauseError(new Exception("Pausing task " + this.TaskId + ": Task is null"));
105        return;
106      }
107
108      if (task.ExecutionState == ExecutionState.Started) {
109        try {
110          task.Pause();
111          //we need to block the pause...
112          if (!pauseStopSem.WaitOne(Settings.Default.ExecutorSemTimeouts)) {
113            throw new Exception("Pausing task " + this.TaskId + " timed out.");
114          }
115        }
116        catch (Exception ex) {
117          HandleStartStopPauseError(ex);
118        }
119      }
120    }
121
122    public void Stop() {
123      IsStopping = true;
124      // wait until task is started. if this does not happen, the Task is null an we give up
125      taskStartedSem.WaitOne(Settings.Default.ExecutorSemTimeouts);
126      wasTaskAborted = true;
127
128      if (task == null) {
129        HandleStartStopPauseError(new Exception("Stopping task " + this.TaskId + ": Task is null"));
130        return;
131      }
132
133      if ((ExecutionState == ExecutionState.Started) || (ExecutionState == ExecutionState.Paused)) {
134        try {
135          task.Stop();
136          if (!pauseStopSem.WaitOne(Settings.Default.ExecutorSemTimeouts)) {
137            throw new Exception("Stopping task " + this.TaskId + " timed out.");
138          }
139        }
140        catch (Exception ex) {
141          HandleStartStopPauseError(ex);
142        }
143      }
144    }
145
146    private void RegisterJobEvents() {
147      task.TaskStopped += new EventHandler(Task_TaskStopped);
148      task.TaskFailed += new EventHandler(Task_TaskFailed);
149      task.TaskPaused += new EventHandler(Task_TaskPaused);
150      task.TaskStarted += new EventHandler(Task_TaskStarted);
151    }
152
153    private void DeregisterJobEvents() {
154      task.TaskStopped -= new EventHandler(Task_TaskStopped);
155      task.TaskFailed -= new EventHandler(Task_TaskFailed);
156      task.TaskPaused -= new EventHandler(Task_TaskPaused);
157      task.TaskStarted -= new EventHandler(Task_TaskStarted);
158    }
159
160    #region Task Events
161    private void Task_TaskFailed(object sender, EventArgs e) {
162      HasFailed = true;
163      IsStopping = true;
164      EventArgs<Exception> ex = (EventArgs<Exception>)e;
165      CurrentException = ex.Value;
166      executorQueue.AddMessage(ExecutorMessageType.TaskFailed);
167      startTaskSem.Set(); // cancel waiting for startup
168    }
169
170    private void Task_TaskStopped(object sender, EventArgs e) {
171      IsStopping = true;
172      if (wasTaskAborted) {
173        pauseStopSem.Set();
174      }
175      executorQueue.AddMessage(ExecutorMessageType.TaskStopped);
176    }
177
178    private void Task_TaskPaused(object sender, EventArgs e) {
179      IsPausing = true;
180      pauseStopSem.Set();
181      executorQueue.AddMessage(ExecutorMessageType.TaskPaused);
182    }
183
184    private void Task_TaskStarted(object sender, EventArgs e) {
185      startTaskSem.Set();
186      executorQueue.AddMessage(ExecutorMessageType.TaskStarted);
187    }
188    #endregion
189
190    public TaskData GetTaskData() {
191      if (taskDataInvalid) return null;
192
193      if (task != null && task.ExecutionState == ExecutionState.Started) {
194        throw new InvalidStateException("Task is still running");
195      }
196
197      TaskData taskData = null;
198      if (task == null) {
199        if (CurrentException == null) {
200          CurrentException = new Exception("Task with id " + this.TaskId + " is null, sending empty task");
201        }
202      } else {
203        taskData = new TaskData();
204        taskData.Data = PersistenceUtil.Serialize(task);
205        taskData.TaskId = TaskId;
206      }
207      return taskData;
208    }
209
210    public void Dispose() {
211      if (task != null)
212        DeregisterJobEvents();
213      task = null;
214    }
215
216    private void HandleStartStopPauseError(Exception e) {
217      taskDataInvalid = true;
218      Task_TaskFailed(this, new EventArgs<Exception>(e));
219    }
220  }
221}
Note: See TracBrowser for help on using the repository browser.