#region License Information
/* HeuristicLab
* Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Threading;
using HeuristicLab.Clients.Hive.SlaveCore.Properties;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Hive;
namespace HeuristicLab.Clients.Hive.SlaveCore {
///
/// The executor runs in the appdomain and handles the execution of an Hive task.
///
public class Executor : MarshalByRefObject, IDisposable {
private bool wasTaskAborted = false;
private AutoResetEvent pauseStopSem = new AutoResetEvent(false);
private AutoResetEvent startTaskSem = new AutoResetEvent(false); // block start method call
private AutoResetEvent taskStartedSem = new AutoResetEvent(false); // make pause or stop wait until start is finished
private ExecutorQueue executorQueue;
private bool taskDataInvalid = false; // if true, the jobdata is not sent when the task is failed
private ITask task;
private DateTime creationTime;
public Guid TaskId { get; set; }
public int CoresNeeded { get; set; }
public int MemoryNeeded { get; set; }
public bool IsStopping { get; set; }
public bool IsPausing { get; set; }
public Exception CurrentException;
public String CurrentExceptionStr {
get {
if (CurrentException != null) {
return CurrentException.ToString();
} else {
return string.Empty;
}
}
}
public ExecutorQueue ExecutorCommandQueue {
get { return executorQueue; }
}
private ExecutionState ExecutionState {
get { return task != null ? task.ExecutionState : HeuristicLab.Core.ExecutionState.Stopped; }
}
public TimeSpan ExecutionTime {
get { return task != null ? task.ExecutionTime : new TimeSpan(0, 0, 0); }
}
public Executor() {
IsStopping = false;
IsPausing = false;
executorQueue = new ExecutorQueue();
}
public void Start(byte[] serializedJob) {
try {
creationTime = DateTime.Now;
task = PersistenceUtil.Deserialize(serializedJob);
RegisterJobEvents();
task.Start();
if (!startTaskSem.WaitOne(Settings.Default.ExecutorSemTimeouts)) {
throw new TimeoutException("Timeout when starting the task. TaskStarted event was not fired.");
}
}
catch (Exception e) {
HandleStartStopPauseError(e);
}
finally {
taskStartedSem.Set();
}
}
public void Pause() {
IsPausing = true;
// wait until task is started. if this does not happen, the Task is null an we give up
taskStartedSem.WaitOne(Settings.Default.ExecutorSemTimeouts);
if (task == null) {
HandleStartStopPauseError(new Exception("Pausing task " + this.TaskId + ": Task is null"));
return;
}
if (task.ExecutionState == ExecutionState.Started) {
try {
task.Pause();
//we need to block the pause...
if (!pauseStopSem.WaitOne(Settings.Default.ExecutorSemTimeouts)) {
throw new Exception("Pausing task " + this.TaskId + " timed out.");
}
}
catch (Exception ex) {
HandleStartStopPauseError(ex);
}
}
}
public void Stop() {
IsStopping = true;
// wait until task is started. if this does not happen, the Task is null an we give up
taskStartedSem.WaitOne(Settings.Default.ExecutorSemTimeouts);
wasTaskAborted = true;
if (task == null) {
HandleStartStopPauseError(new Exception("Stopping task " + this.TaskId + ": Task is null"));
return;
}
if ((ExecutionState == ExecutionState.Started) || (ExecutionState == ExecutionState.Paused)) {
try {
task.Stop();
if (!pauseStopSem.WaitOne(Settings.Default.ExecutorSemTimeouts)) {
throw new Exception("Stopping task " + this.TaskId + " timed out.");
}
}
catch (Exception ex) {
HandleStartStopPauseError(ex);
}
}
}
private void RegisterJobEvents() {
task.TaskStopped += new EventHandler(Task_TaskStopped);
task.TaskFailed += new EventHandler(Task_TaskFailed);
task.TaskPaused += new EventHandler(Task_TaskPaused);
task.TaskStarted += new EventHandler(Task_TaskStarted);
}
private void DeregisterJobEvents() {
task.TaskStopped -= new EventHandler(Task_TaskStopped);
task.TaskFailed -= new EventHandler(Task_TaskFailed);
task.TaskPaused -= new EventHandler(Task_TaskPaused);
task.TaskStarted -= new EventHandler(Task_TaskStarted);
}
#region Task Events
private void Task_TaskFailed(object sender, EventArgs e) {
IsStopping = true;
EventArgs ex = (EventArgs)e;
CurrentException = ex.Value;
executorQueue.AddMessage(ExecutorMessageType.TaskFailed);
}
private void Task_TaskStopped(object sender, EventArgs e) {
IsStopping = true;
if (wasTaskAborted) {
pauseStopSem.Set();
}
executorQueue.AddMessage(ExecutorMessageType.TaskStopped);
}
private void Task_TaskPaused(object sender, EventArgs e) {
IsPausing = true;
pauseStopSem.Set();
executorQueue.AddMessage(ExecutorMessageType.TaskPaused);
}
private void Task_TaskStarted(object sender, EventArgs e) {
startTaskSem.Set();
executorQueue.AddMessage(ExecutorMessageType.TaskStarted);
}
#endregion
public TaskData GetTaskData() {
if (taskDataInvalid) return null;
if (task != null && task.ExecutionState == ExecutionState.Started) {
throw new InvalidStateException("Task is still running");
}
TaskData taskData = null;
if (task == null) {
if (CurrentException == null) {
CurrentException = new Exception("Task with id " + this.TaskId + " is null, sending empty task");
}
} else {
taskData = new TaskData();
taskData.Data = PersistenceUtil.Serialize(task);
taskData.TaskId = TaskId;
}
return taskData;
}
public void Dispose() {
if (task != null)
DeregisterJobEvents();
task = null;
}
private void HandleStartStopPauseError(Exception e) {
taskDataInvalid = true;
Task_TaskFailed(this, new EventArgs(e));
}
}
}