Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Hive-3.4/sources/HeuristicLab.Clients.Hive.Slave/3.3/SlaveTask.cs @ 6727

Last change on this file since 6727 was 6725, checked in by ascheibe, 13 years ago

#1233 more renaming for more consistency

File size: 9.3 KB
RevLine 
[6371]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
[6203]22using System;
23using System.IO;
24using System.Threading;
[6456]25using HeuristicLab.Clients.Hive.SlaveCore.Properties;
[6357]26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.PluginInfrastructure.Sandboxing;
[6203]29
30namespace HeuristicLab.Clients.Hive.SlaveCore {
31
[6371]32  /// <summary>
[6725]33  ///  Manages a single task and it's appdomain.
[6371]34  /// </summary>
[6725]35  public class SlaveTask : MarshalByRefObject {
[6203]36    private Executor executor;
37    private AppDomain appDomain;
38    private Semaphore waitForStartBeforeKillSem;
39    private bool executorMonitoringRun;
40    private Thread executorMonitoringThread;
[6357]41    private PluginManager pluginManager;
42    private ILog log;
[6725]43    public Guid TaskId { get; private set; }
[6357]44    public bool IsPrepared { get; private set; }
[6203]45
[6357]46    private int coresNeeded;
47    public int CoresNeeded {
48      get { return coresNeeded; }
49      set { this.coresNeeded = value; }
50    }
[6203]51
[6357]52    public TimeSpan ExecutionTime {
53      get { return executor != null ? executor.ExecutionTime : TimeSpan.Zero; }
54    }
[6203]55
[6725]56    public SlaveTask(PluginManager pluginManager, int coresNeeded, ILog log) {
[6357]57      this.pluginManager = pluginManager;
58      this.coresNeeded = coresNeeded;
59      this.log = log;
[6203]60      waitForStartBeforeKillSem = new Semaphore(0, 1);
61      executorMonitoringRun = true;
[6357]62      IsPrepared = false;
[6203]63    }
64
[6725]65    public void StartJobAsync(Task task, TaskData taskData) {
[6357]66      try {
[6725]67        this.TaskId = task.Id;
68        Prepare(task);
69        StartTaskInAppDomain(taskData);
[6357]70      }
71      catch (Exception) {
[6371]72        // make sure to clean up if something went wrong
73        DisposeAppDomain();
[6357]74        throw;
75      }
[6216]76    }
[6203]77
[6725]78    public void PauseTask() {
[6357]79      if (!IsPrepared) throw new AppDomainNotCreatedException();
80      if (!executor.IsPausing && !executor.IsStopping) executor.Pause();
[6203]81    }
82
[6725]83    public void StopTask() {
[6357]84      if (!IsPrepared) throw new AppDomainNotCreatedException();
85      if (!executor.IsPausing && !executor.IsStopping) executor.Stop();
[6203]86    }
87
[6725]88    private void Prepare(Task task) {
89      string pluginDir = Path.Combine(pluginManager.PluginTempBaseDir, task.Id.ToString());
[6357]90      string configFileName;
[6725]91      pluginManager.PreparePlugins(task, out configFileName);
92      appDomain = CreateAppDomain(task, pluginDir, configFileName);
[6357]93      IsPrepared = true;
[6203]94    }
95
[6725]96    private AppDomain CreateAppDomain(Task task, String pluginDir, string configFileName) {
97      if (task.IsPrivileged) {
98        appDomain = SandboxManager.CreateAndInitPrivilegedSandbox(task.Id.ToString(), pluginDir, Path.Combine(pluginDir, configFileName));
[6371]99      } else {
[6725]100        appDomain = SandboxManager.CreateAndInitSandbox(task.Id.ToString(), pluginDir, Path.Combine(pluginDir, configFileName));
[6371]101      }
[6357]102      appDomain.UnhandledException += new UnhandledExceptionEventHandler(AppDomain_UnhandledException);
[6203]103
[6357]104      log.LogMessage("Creating AppDomain");
105      executor = (Executor)appDomain.CreateInstanceAndUnwrap(typeof(Executor).Assembly.GetName().Name, typeof(Executor).FullName);
[6203]106
[6725]107      executor.TaskId = task.Id;
108      executor.CoresNeeded = task.CoresNeeded;
109      executor.MemoryNeeded = task.MemoryNeeded;
[6357]110      return appDomain;
[6203]111    }
112
[6725]113    private void StartTaskInAppDomain(TaskData taskData) {
114      executor.Start(taskData.Data);
[6357]115      waitForStartBeforeKillSem.Release();
116      StartExecutorMonitoringThread();
[6203]117    }
118
[6357]119    public void DisposeAppDomain() {
[6725]120      log.LogMessage(string.Format("Shutting down Appdomain for Task {0}", TaskId));
[6357]121      StopExecutorMonitoringThread();
[6203]122
[6357]123      if (executor != null) {
124        executor.Dispose();
125      }
[6203]126
[6357]127      if (appDomain != null) {
128        appDomain.UnhandledException -= new UnhandledExceptionEventHandler(AppDomain_UnhandledException);
[6456]129        int repeat = Settings.Default.PluginDeletionRetries;
[6357]130        while (repeat > 0) {
131          try {
132            waitForStartBeforeKillSem.WaitOne();
133            AppDomain.Unload(appDomain);
134            waitForStartBeforeKillSem.Dispose();
135            repeat = 0;
136          }
137          catch (CannotUnloadAppDomainException) {
138            log.LogMessage("Could not unload AppDomain, will try again in 1 sec.");
[6456]139            Thread.Sleep(Settings.Default.PluginDeletionTimeout);
[6357]140            repeat--;
141            if (repeat == 0) {
142              throw; // rethrow and let app crash
[6203]143            }
144          }
145        }
146      }
[6725]147      pluginManager.DeletePluginsForJob(TaskId);
[6203]148      GC.Collect();
149    }
150
151    private void AppDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
[6357]152      DisposeAppDomain();
[6371]153      OnExceptionOccured(new Exception("Unhandled exception: " + e.ExceptionObject.ToString()));
[6203]154    }
155
[6725]156    public TaskData GetTaskData() {
157      return executor.GetTaskData();
[6357]158    }
[6203]159
160    #region ExecutorMonitorThread
161    private void StartExecutorMonitoringThread() {
162      executorMonitoringThread = new Thread(MonitorExecutor);
163      executorMonitoringThread.Start();
164    }
165
166    private void StopExecutorMonitoringThread() {
167      if (executorMonitoringThread != null) {
168        if (executorMonitoringRun) {
169          executorMonitoringRun = false;
[6371]170          executor.ExecutorCommandQueue.AddMessage(ExecutorMessageType.StopExecutorMonitoringThread);
[6203]171        }
172      }
173    }
174
175    /// <summary>
176    /// Because the executor is in an appdomain and is not able to call back
177    /// (because of security -> lease time for marshall-by-ref object is 5 min),
[6725]178    /// we have to poll the executor for events we have to react to (e.g. task finished...)   
[6203]179    /// </summary>
180    private void MonitorExecutor() {
181      while (executorMonitoringRun) {
182        //this blocks through the appdomain border, that's why the lease gets renewed
[6371]183        ExecutorMessage message = executor.ExecutorCommandQueue.GetMessage();
[6203]184
185        switch (message.MessageType) {
[6725]186          case ExecutorMessageType.TaskStarted:
187            OnTaskStarted();
[6357]188            break;
189
[6725]190          case ExecutorMessageType.TaskPaused:
[6357]191            executorMonitoringRun = false;
[6725]192            OnTaskPaused();
[6357]193            DisposeAppDomain();
194            break;
195
[6725]196          case ExecutorMessageType.TaskStopped:
[6203]197            executorMonitoringRun = false;
[6725]198            OnTaskStopped();
[6357]199            DisposeAppDomain();
[6203]200            break;
201
[6725]202          case ExecutorMessageType.TaskFailed:
[6203]203            executorMonitoringRun = false;
[6725]204            OnTaskFailed(new TaskFailedException(executor.CurrentExceptionStr));
[6357]205            DisposeAppDomain();
[6203]206            break;
207
208          case ExecutorMessageType.StopExecutorMonitoringThread:
209            executorMonitoringRun = false;
[6464]210            break;
211
212          case ExecutorMessageType.ExceptionOccured:
213            executorMonitoringRun = false;
214            DisposeAppDomain();
215            if (executor.CurrentException != null) {
216              OnExceptionOccured(executor.CurrentException);
217            } else {
[6725]218              OnExceptionOccured(new Exception(string.Format("Unknow exception occured in Executor for task {0}", TaskId)));
[6464]219            }
220            break;
[6203]221        }
222      }
223    }
224    #endregion
[6357]225
[6725]226    public event EventHandler<EventArgs<Guid>> TaskStarted;
227    private void OnTaskStarted() {
228      var handler = TaskStarted;
229      if (handler != null) handler(this, new EventArgs<Guid>(this.TaskId));
[6357]230    }
231
[6725]232    public event EventHandler<EventArgs<Guid>> TaskStopped;
233    private void OnTaskStopped() {
234      var handler = TaskStopped;
235      if (handler != null) handler(this, new EventArgs<Guid>(this.TaskId));
[6357]236    }
237
[6725]238    public event EventHandler<EventArgs<Guid>> TaskPaused;
239    private void OnTaskPaused() {
240      var handler = TaskPaused;
241      if (handler != null) handler(this, new EventArgs<Guid>(this.TaskId));
[6357]242    }
243
[6725]244    public event EventHandler<EventArgs<Guid>> TaskAborted;
245    private void OnTaskAborted() {
246      var handler = TaskAborted;
247      if (handler != null) handler(this, new EventArgs<Guid>(this.TaskId));
[6357]248    }
249
[6725]250    public event EventHandler<EventArgs<Guid, Exception>> TaskFailed;
251    private void OnTaskFailed(Exception exception) {
252      var handler = TaskFailed;
253      if (handler != null) handler(this, new EventArgs<Guid, Exception>(this.TaskId, exception));
[6357]254    }
255
256    public event EventHandler<EventArgs<Guid, Exception>> ExceptionOccured;
257    private void OnExceptionOccured(Exception exception) {
258      var handler = ExceptionOccured;
[6725]259      if (handler != null) handler(this, new EventArgs<Guid, Exception>(this.TaskId, exception));
[6357]260    }
[6203]261  }
262}
Note: See TracBrowser for help on using the repository browser.