//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.SqlTools.ServiceLayer.TaskServices
{
///
/// A singleton class to manager the current long running operations
///
public class SqlTaskManager : IDisposable
{
private static SqlTaskManager instance = new SqlTaskManager();
private static readonly object lockObject = new object();
private bool isDisposed;
private readonly ConcurrentDictionary tasks = new ConcurrentDictionary();
public event EventHandler> TaskAdded;
public event EventHandler> TaskRemoved;
///
/// Constructor to create an instance for test purposes use only
///
internal SqlTaskManager()
{
}
///
/// Singleton instance
///
public static SqlTaskManager Instance
{
get
{
return instance;
}
}
///
/// Task connections
///
internal ReadOnlyCollection Tasks
{
get
{
lock (lockObject)
{
return new ReadOnlyCollection(tasks.Values.ToList());
}
}
}
///
/// Clear completed tasks
///
internal void ClearCompletedTasks()
{
ValidateNotDisposed();
lock (lockObject)
{
var tasksToRemove = (from task in tasks.Values
where task.IsCompleted
select task).ToList();
foreach (var task in tasksToRemove)
{
RemoveCompletedTask(task);
}
}
}
///
/// Creates a new task
///
/// Task Metadata
/// The function to run the operation
///
public SqlTask CreateTask(TaskMetadata taskMetadata, Func> taskToRun)
{
ValidateNotDisposed();
var newtask = new SqlTask(taskMetadata, taskToRun );
lock (lockObject)
{
tasks.AddOrUpdate(newtask.TaskId, newtask, (key, oldValue) => newtask);
}
OnTaskAdded(new TaskEventArgs(newtask));
return newtask;
}
public void Dispose()
{
Dispose(true);
}
void Dispose(bool disposing)
{
if (isDisposed)
{
return;
}
if (disposing)
{
lock (lockObject)
{
foreach (var task in tasks.Values)
{
task.Dispose();
}
tasks.Clear();
}
}
isDisposed = true;
}
///
/// Returns true if there's any completed task
///
///
internal bool HasCompletedTasks()
{
lock (lockObject)
{
return tasks.Values.Any(task => task.IsCompleted);
}
}
private void OnTaskAdded(TaskEventArgs e)
{
var handler = TaskAdded;
if (handler != null)
{
handler(this, e);
}
}
private void OnTaskRemoved(TaskEventArgs e)
{
var handler = TaskRemoved;
if (handler != null)
{
handler(this, e);
}
}
///
/// Cancel a task
///
///
public void CancelTask(Guid taskId)
{
SqlTask taskToCancel;
lock (lockObject)
{
tasks.TryGetValue(taskId, out taskToCancel);
}
if(taskToCancel != null)
{
taskToCancel.Cancel();
}
}
///
/// Internal for test purposes only.
/// Removes all tasks regardless of status from the model.
/// This is used as a test aid since Monitor is a singleton class.
///
internal void Reset()
{
foreach (var task in tasks.Values)
{
RemoveTask(task);
}
}
internal void RemoveCompletedTask(SqlTask task)
{
if (task.IsCompleted)
{
RemoveTask(task);
}
}
private void RemoveTask(SqlTask task)
{
SqlTask removedTask;
tasks.TryRemove(task.TaskId, out removedTask);
}
void ValidateNotDisposed()
{
if (isDisposed)
{
throw new ObjectDisposedException(typeof(SqlTaskManager).FullName);
}
}
}
}