#region License Information
/* HeuristicLab
* Copyright (C) 2002-2011 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.Collections.Generic;
using System.Linq;
using System.Threading;
using HeuristicLab.Common;
using HeuristicLab.Core;
namespace HeuristicLab.Clients.Hive.SlaveCore {
///
/// Holds a list of slave jobs and manages access to this list.
/// Forwards events from SlaveJob and forwards commands to SlaveJob.
///
public class JobManager {
private static ReaderWriterLockSlim slaveJobsLocker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
private Dictionary slaveJobs;
private ILog log;
private PluginManager pluginManager;
public int JobCount {
get {
slaveJobsLocker.EnterReadLock();
try {
return slaveJobs.Count;
}
finally { slaveJobsLocker.ExitReadLock(); }
}
}
public Guid[] JobIds {
get {
slaveJobsLocker.EnterReadLock();
try {
return slaveJobs.Keys.ToArray();
}
finally { slaveJobsLocker.ExitReadLock(); }
}
}
public JobManager(PluginManager pluginCache, ILog log) {
this.pluginManager = pluginCache;
this.log = log;
this.slaveJobs = new Dictionary();
}
#region Job Control methods
public void StartJobAsync(Job job, JobData jobData) {
SlaveJob slaveJob = null;
slaveJobsLocker.EnterUpgradeableReadLock();
try {
if (slaveJobs.ContainsKey(job.Id)) {
SlaveStatusInfo.IncrementExceptionOccured();
throw new JobAlreadyRunningException(job.Id);
} else {
slaveJob = new SlaveJob(pluginManager, job.CoresNeeded, log);
AddSlaveJob(job, slaveJob);
SlaveStatusInfo.IncrementJobsFetched();
}
}
finally { slaveJobsLocker.ExitUpgradeableReadLock(); }
if (slaveJob != null) {
try {
slaveJob.StartJobAsync(job, jobData);
}
catch (Exception) {
RemoveSlaveJob(job.Id, slaveJob); // clean up and rethrow
slaveJob.DisposeAppDomain();
throw;
}
}
}
public void PauseJobAsync(Guid jobId) {
slaveJobsLocker.EnterUpgradeableReadLock();
try {
if (!slaveJobs.ContainsKey(jobId)) throw new JobNotRunningException(jobId);
SlaveJob slaveJob = slaveJobs[jobId];
slaveJob.PauseJob();
}
finally { slaveJobsLocker.ExitUpgradeableReadLock(); }
}
public void StopJobAsync(Guid jobId) {
slaveJobsLocker.EnterUpgradeableReadLock();
try {
if (!slaveJobs.ContainsKey(jobId)) throw new JobNotRunningException(jobId);
SlaveJob slaveJob = slaveJobs[jobId];
slaveJob.StopJob();
}
finally { slaveJobsLocker.ExitUpgradeableReadLock(); }
}
public void AbortJob(Guid jobId) {
SlaveJob slaveJob = null;
slaveJobsLocker.EnterUpgradeableReadLock();
try {
if (!slaveJobs.ContainsKey(jobId)) throw new JobNotRunningException(jobId);
slaveJob = slaveJobs[jobId];
if (!slaveJob.IsPrepared) throw new AppDomainNotCreatedException();
RemoveSlaveJob(jobId, slaveJob);
}
finally { slaveJobsLocker.ExitUpgradeableReadLock(); }
slaveJob.DisposeAppDomain();
SlaveStatusInfo.IncrementJobsAborted();
OnJobAborted(slaveJob);
}
public void PauseAllJobsAsync() {
slaveJobsLocker.EnterUpgradeableReadLock();
try {
foreach (var slaveJob in slaveJobs.Values) {
slaveJob.PauseJob();
}
}
finally { slaveJobsLocker.ExitUpgradeableReadLock(); }
}
public void StopAllJobsAsync() {
slaveJobsLocker.EnterUpgradeableReadLock();
try {
foreach (var slaveJob in slaveJobs.Values) {
slaveJob.StopJob();
}
}
finally { slaveJobsLocker.ExitUpgradeableReadLock(); }
}
public void AbortAllJobs() {
slaveJobsLocker.EnterUpgradeableReadLock();
try {
foreach (var slaveJob in slaveJobs.Values.ToArray()) {
AbortJob(slaveJob.JobId);
}
}
finally { slaveJobsLocker.ExitUpgradeableReadLock(); }
}
#endregion
#region Add/Remove SlaveJob
private void AddSlaveJob(Job job, SlaveJob slaveJob) {
slaveJobsLocker.EnterWriteLock();
try {
slaveJobs.Add(job.Id, slaveJob);
RegisterSlaveJobEvents(slaveJob);
}
finally { slaveJobsLocker.ExitWriteLock(); }
}
private void RemoveSlaveJob(Guid jobId, SlaveJob slaveJob) {
slaveJobsLocker.EnterWriteLock();
try {
slaveJobs.Remove(jobId);
DeregisterSlaveJobEvents(slaveJob);
}
finally { slaveJobsLocker.ExitWriteLock(); }
}
#endregion
#region SlaveJob Events
private void RegisterSlaveJobEvents(SlaveJob slaveJob) {
slaveJob.JobStarted += new EventHandler>(slaveJob_JobStarted);
slaveJob.JobPaused += new EventHandler>(slaveJob_JobPaused);
slaveJob.JobStopped += new EventHandler>(slaveJob_JobStopped);
slaveJob.JobFailed += new EventHandler>(slaveJob_JobFailed);
slaveJob.ExceptionOccured += new EventHandler>(slaveJob_ExceptionOccured);
}
private void DeregisterSlaveJobEvents(SlaveJob slaveJob) {
slaveJob.JobStarted -= new EventHandler>(slaveJob_JobStarted);
slaveJob.JobPaused -= new EventHandler>(slaveJob_JobPaused);
slaveJob.JobStopped -= new EventHandler>(slaveJob_JobStopped);
slaveJob.JobFailed -= new EventHandler>(slaveJob_JobFailed);
slaveJob.ExceptionOccured -= new EventHandler>(slaveJob_ExceptionOccured);
}
private void slaveJob_JobStarted(object sender, EventArgs e) {
SlaveJob slaveJob;
slaveJobsLocker.EnterUpgradeableReadLock();
try {
slaveJob = slaveJobs[e.Value];
}
finally { slaveJobsLocker.ExitUpgradeableReadLock(); }
SlaveStatusInfo.IncrementJobsStarted();
OnJobStarted(slaveJob);
}
private void slaveJob_JobPaused(object sender, EventArgs e) {
SlaveJob slaveJob;
slaveJobsLocker.EnterUpgradeableReadLock();
try {
slaveJob = slaveJobs[e.Value];
RemoveSlaveJob(e.Value, slaveJob);
}
finally { slaveJobsLocker.ExitUpgradeableReadLock(); }
JobData jobData = null;
try {
jobData = slaveJob.GetJobData();
if (jobData == null) throw new SerializationException();
SlaveStatusInfo.IncrementJobsFinished();
OnJobPaused(slaveJob, jobData);
}
catch (Exception ex) {
RemoveSlaveJob(e.Value, slaveJob);
SlaveStatusInfo.IncrementJobsFailed();
OnJobFailed(slaveJob, jobData, ex);
}
}
private void slaveJob_JobStopped(object sender, EventArgs e) {
SlaveJob slaveJob;
slaveJobsLocker.EnterUpgradeableReadLock();
try {
slaveJob = slaveJobs[e.Value];
RemoveSlaveJob(e.Value, slaveJob);
}
finally { slaveJobsLocker.ExitUpgradeableReadLock(); }
JobData jobData = null;
try {
jobData = slaveJob.GetJobData();
if (jobData == null) throw new SerializationException();
SlaveStatusInfo.IncrementJobsFinished();
OnJobStopped(slaveJob, jobData);
}
catch (Exception ex) {
RemoveSlaveJob(e.Value, slaveJob);
SlaveStatusInfo.IncrementJobsFailed();
OnJobFailed(slaveJob, jobData, ex);
}
}
private void slaveJob_JobFailed(object sender, EventArgs e) {
SlaveJob slaveJob;
slaveJobsLocker.EnterUpgradeableReadLock();
try {
slaveJob = slaveJobs[e.Value];
RemoveSlaveJob(e.Value, slaveJob);
}
finally { slaveJobsLocker.ExitUpgradeableReadLock(); }
JobData jobData = null;
try {
jobData = slaveJob.GetJobData();
if (jobData == null) throw new SerializationException();
}
catch { /* jobData will be null */ }
SlaveStatusInfo.IncrementJobsFailed();
OnJobFailed(slaveJob, jobData, e.Value2);
}
private void slaveJob_ExceptionOccured(object sender, EventArgs e) {
SlaveJob slaveJob;
slaveJobsLocker.EnterUpgradeableReadLock();
try {
slaveJob = slaveJobs[e.Value];
RemoveSlaveJob(e.Value, slaveJob);
}
finally { slaveJobsLocker.ExitUpgradeableReadLock(); }
SlaveStatusInfo.IncrementExceptionOccured();
OnExceptionOccured(slaveJob, e.Value2);
}
#endregion
#region EventHandler
public event EventHandler> JobStarted;
private void OnJobStarted(SlaveJob slaveJob) {
var handler = JobStarted;
if (handler != null) handler(this, new EventArgs(slaveJob));
}
public event EventHandler> JobStopped;
private void OnJobStopped(SlaveJob slaveJob, JobData jobData) {
var handler = JobStopped;
if (handler != null) handler(this, new EventArgs(slaveJob, jobData));
}
public event EventHandler> JobPaused;
private void OnJobPaused(SlaveJob slaveJob, JobData jobData) {
var handler = JobPaused;
if (handler != null) handler(this, new EventArgs(slaveJob, jobData));
}
public event EventHandler>> JobFailed;
private void OnJobFailed(SlaveJob slaveJob, JobData jobData, Exception exception) {
var handler = JobFailed;
if (handler != null) handler(this, new EventArgs>(new Tuple(slaveJob, jobData, exception)));
}
public event EventHandler> ExceptionOccured;
private void OnExceptionOccured(SlaveJob slaveJob, Exception exception) {
var handler = ExceptionOccured;
if (handler != null) handler(this, new EventArgs(slaveJob, exception));
}
public event EventHandler> JobAborted;
private void OnJobAborted(SlaveJob slaveJob) {
var handler = JobAborted;
if (handler != null) handler(this, new EventArgs(slaveJob));
}
#endregion
public Dictionary GetExecutionTimes() {
slaveJobsLocker.EnterReadLock();
try {
return slaveJobs.ToDictionary(x => x.Key, x => x.Value.ExecutionTime);
}
finally { slaveJobsLocker.ExitReadLock(); }
}
}
}