// // 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); } } } }