#region License Information
/* HeuristicLab
* Copyright (C) 2002-2012 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 System.Threading.Tasks;
using HeuristicLab.Common;
using HeuristicLab.Hive;
namespace HeuristicLab.Clients.Hive {
///
/// Downloads and deserializes jobs. It avoids too many jobs beeing downloaded or deserialized at the same time to avoid memory problems
///
public class ConcurrentTaskDownloader : IDisposable where T : class, ITask {
private bool abort = false;
// use semaphore to ensure only few concurrenct connections and few SerializedJob objects in memory
private Semaphore downloadSemaphore;
private Semaphore deserializeSemaphore;
private IHiveServiceLocator locator;
public ConcurrentTaskDownloader(int concurrentDownloads, int concurrentDeserializations) : this(concurrentDownloads, concurrentDeserializations, HiveServiceLocator.Instance) {
}
public ConcurrentTaskDownloader(int concurrentDownloads, int concurrentDeserializations, IHiveServiceLocator locator) {
downloadSemaphore = new Semaphore(concurrentDownloads, concurrentDownloads);
this.locator = locator;
deserializeSemaphore = new Semaphore(concurrentDeserializations, concurrentDeserializations);
}
public void DownloadTaskData(Task t, Action onFinishedAction) {
Task> task = Task>.Factory.StartNew(DownloadTaskData, t)
.ContinueWith((y) => DeserializeTask(y.Result));
task.ContinueWith((x) => OnTaskFinished(x, onFinishedAction), TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith((x) => OnTaskFailed(x, onFinishedAction), TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted);
}
public void DownloadTaskDataAndTask(Guid taskId, Action onFinishedAction) {
Task> task = Task.Factory.StartNew(DownloadTask, taskId)
.ContinueWith((x) => DownloadTaskData(x.Result))
.ContinueWith((y) => DeserializeTask(y.Result));
task.ContinueWith((x) => OnTaskFinished(x, onFinishedAction), TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith((x) => OnTaskFailed(x, onFinishedAction), TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted);
}
private void OnTaskFinished(Task> task, Action onFinishedAction) {
onFinishedAction(task.Result.Item1, task.Result.Item2);
}
private void OnTaskFailed(Task> task, Action onFinishedAction) {
task.Exception.Flatten().Handle((e) => { return true; });
OnExceptionOccured(task.Exception.Flatten());
onFinishedAction(task.Result.Item1, null);
}
private Task DownloadTask(object taskId) {
Task t = null;
HiveClient.TryAndRepeat(() => {
t = locator.CallHiveService(s => s.GetTask((Guid)taskId));
}, Settings.Default.MaxRepeatServiceCalls, "Failed to download task.");
return t;
}
protected Tuple DownloadTaskData(object taskId) {
return DownloadTaskData((Task)taskId);
}
protected Tuple DownloadTaskData(Task task) {
downloadSemaphore.WaitOne();
TaskData result = null;
try {
if (abort) return null;
HiveClient.TryAndRepeat(() => {
result = locator.CallHiveService(s => s.GetTaskData(task.Id));
}, Settings.Default.MaxRepeatServiceCalls, "Failed to download task data.");
}
finally {
downloadSemaphore.Release();
}
return new Tuple(task, result);
}
protected Tuple DeserializeTask(Tuple taskData) {
deserializeSemaphore.WaitOne();
try {
if (abort || taskData.Item2 == null || taskData.Item1 == null) return null;
var deserializedJob = PersistenceUtil.Deserialize(taskData.Item2.Data);
taskData.Item2.Data = null; // reduce memory consumption.
return new Tuple(taskData.Item1, deserializedJob);
}
finally {
deserializeSemaphore.Release();
}
}
public event EventHandler> ExceptionOccured;
private void OnExceptionOccured(Exception exception) {
var handler = ExceptionOccured;
if (handler != null) handler(this, new EventArgs(exception));
}
#region IDisposable Members
public void Dispose() {
deserializeSemaphore.Dispose();
downloadSemaphore.Dispose();
}
#endregion
}
}