Files
sqltoolsservice/src/Microsoft.SqlTools.ServiceLayer/TaskServices/SqlTaskManager.cs
Leila Lali 39dedd88e0 generic way to support scriptable operations (#438)
* implemented a generic way to support scriptable operations
2017-08-18 16:12:00 -07:00

296 lines
9.3 KiB
C#

//
// 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;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.TaskServices
{
/// <summary>
/// A singleton class to manager the current long running operations
/// </summary>
public class SqlTaskManager : IDisposable
{
private static SqlTaskManager instance = new SqlTaskManager();
private static readonly object lockObject = new object();
private bool isDisposed;
private readonly ConcurrentDictionary<Guid, SqlTask> tasks = new ConcurrentDictionary<Guid, SqlTask>();
public event EventHandler<TaskEventArgs<SqlTask>> TaskAdded;
public event EventHandler<TaskEventArgs<SqlTask>> TaskRemoved;
/// <summary>
/// Constructor to create an instance for test purposes use only
/// </summary>
internal SqlTaskManager()
{
}
/// <summary>
/// Singleton instance
/// </summary>
public static SqlTaskManager Instance
{
get
{
return instance;
}
}
/// <summary>
/// Task connections
/// </summary>
internal ReadOnlyCollection<SqlTask> Tasks
{
get
{
lock (lockObject)
{
return new ReadOnlyCollection<SqlTask>(tasks.Values.ToList());
}
}
}
/// <summary>
/// Clear completed tasks
/// </summary>
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);
}
}
}
/// <summary>
/// Creates a new task
/// </summary>
/// <param name="taskMetadata">Task Metadata</param>
/// <param name="taskToRun">The function to run the operation</param>
/// <param name="taskToCancel">The function to cancel the operation</param>
/// <returns>The new sql task</returns>
public SqlTask CreateTask(TaskMetadata taskMetadata, Func<SqlTask, Task<TaskResult>> taskToRun, Func<SqlTask, Task<TaskResult>> taskToCancel)
{
return CreateTask<SqlTask>(taskMetadata, taskToRun, taskToCancel);
}
/// <summary>
/// Creates a new task
/// </summary>
/// <param name="taskMetadata">Task Metadata</param>
/// <returns>The new sql task</returns>
public SqlTask CreateTask<T>(TaskMetadata taskMetadata) where T : SqlTask, new()
{
Validate.IsNotNull(nameof(taskMetadata), taskMetadata);
return CreateTask<T>(taskMetadata, TaskOperationHelper.ExecuteTaskAsync, TaskOperationHelper.CancelTaskAsync);
}
/// <summary>
/// Creates a new task
/// </summary>
/// <param name="taskMetadata">Task Metadata</param>
/// <param name="taskToRun">The function to run the operation</param>
/// <param name="taskToCancel">The function to cancel the operation</param>
/// <returns>The new sql task</returns>
public SqlTask CreateTask<T>(TaskMetadata taskMetadata, Func<SqlTask, Task<TaskResult>> taskToRun, Func<SqlTask, Task<TaskResult>> taskToCancel) where T : SqlTask, new()
{
ValidateNotDisposed();
var newTask = new T();
newTask.Init(taskMetadata, taskToRun, taskToCancel);
if (taskMetadata != null && taskMetadata.TaskOperation != null)
{
taskMetadata.TaskOperation.SqlTask = newTask;
}
lock (lockObject)
{
tasks.AddOrUpdate(newTask.TaskId, newTask, (key, oldValue) => newTask);
}
OnTaskAdded(new TaskEventArgs<SqlTask>(newTask));
return newTask;
}
/// <summary>
/// Creates a new task
/// </summary>
/// <param name="taskMetadata">Task Metadata</param>
/// <param name="taskToRun">The function to run the operation</param>
/// <returns>The new sql task</returns>
public SqlTask CreateTask(TaskMetadata taskMetadata, Func<SqlTask, Task<TaskResult>> taskToRun)
{
return CreateTask<SqlTask>(taskMetadata, taskToRun);
}
/// <summary>
/// Creates a new task
/// </summary>
/// <param name="taskMetadata">Task Metadata</param>
/// <param name="taskToRun">The function to run the operation</param>
/// <returns></returns>
public SqlTask CreateTask<T>(TaskMetadata taskMetadata, Func<SqlTask, Task<TaskResult>> taskToRun) where T : SqlTask, new()
{
return CreateTask<T>(taskMetadata, taskToRun, null);
}
/// <summary>
/// Creates a new task and starts the task
/// </summary>
/// <param name="taskMetadata">Task Metadata</param>
/// <param name="taskToRun">The function to run the operation</param>
/// <param name="taskToCancel">The function to cancel the operation</param>
/// <returns></returns>
public SqlTask CreateAndRun(TaskMetadata taskMetadata, Func<SqlTask, Task<TaskResult>> taskToRun, Func<SqlTask, Task<TaskResult>> taskToCancel)
{
return CreateAndRun<SqlTask>(taskMetadata, taskToRun, taskToCancel);
}
public SqlTask CreateAndRun<T>(TaskMetadata taskMetadata) where T : SqlTask, new()
{
var sqlTask = CreateTask<T>(taskMetadata);
sqlTask.Run();
return sqlTask;
}
/// <summary>
/// Creates a new task and starts the task
/// </summary>
/// <param name="taskMetadata">Task Metadata</param>
/// <param name="taskToRun">The function to run the operation</param>
/// <param name="taskToCancel">The function to cancel the operation</param>
/// <returns></returns>
public SqlTask CreateAndRun<T>(TaskMetadata taskMetadata, Func<SqlTask, Task<TaskResult>> taskToRun, Func<SqlTask, Task<TaskResult>> taskToCancel) where T : SqlTask, new()
{
var sqlTask = CreateTask<T>(taskMetadata, taskToRun, taskToCancel);
sqlTask.Run();
return sqlTask;
}
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;
}
/// <summary>
/// Returns true if there's any completed task
/// </summary>
/// <returns></returns>
internal bool HasCompletedTasks()
{
lock (lockObject)
{
return tasks.Values.Any(task => task.IsCompleted);
}
}
private void OnTaskAdded(TaskEventArgs<SqlTask> e)
{
var handler = TaskAdded;
if (handler != null)
{
handler(this, e);
}
}
private void OnTaskRemoved(TaskEventArgs<SqlTask> e)
{
var handler = TaskRemoved;
if (handler != null)
{
handler(this, e);
}
}
/// <summary>
/// Cancel a task
/// </summary>
/// <param name="taskId"></param>
public void CancelTask(Guid taskId)
{
SqlTask taskToCancel;
lock (lockObject)
{
tasks.TryGetValue(taskId, out taskToCancel);
}
if (taskToCancel != null)
{
taskToCancel.Cancel();
}
}
/// <summary>
/// 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.
/// </summary>
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);
}
}
}
}