//-------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: SerialTaskQueue.cs // //-------------------------------------------------------------------------- using System.Collections.Generic; using System.Diagnostics; namespace System.Threading.Tasks { /// Represents a queue of tasks to be started and executed serially. public class SerialTaskQueue { /// The ordered queue of tasks to be executed. Also serves as a lock protecting all shared state. private Queue _tasks = new Queue(); /// The task currently executing, or null if there is none. private Task _taskInFlight; /// Enqueues the task to be processed serially and in order. /// The function that generates a non-started task. public void Enqueue(Func taskGenerator) { EnqueueInternal(taskGenerator); } /// Enqueues the non-started task to be processed serially and in order. /// The task. public Task Enqueue(Task task) { EnqueueInternal(task); return task; } /// Gets a Task that represents the completion of all previously queued tasks. public Task Completed() { return Enqueue(new Task(() => { })); } /// Enqueues the task to be processed serially and in order. /// The task or functino that generates a task. /// The task must not be started and must only be started by this instance. private void EnqueueInternal(object taskOrFunction) { // Validate the task if (taskOrFunction == null) throw new ArgumentNullException("task"); lock(_tasks) { // If there is currently no task in flight, we'll start this one if (_taskInFlight == null) StartTask_CallUnderLock(taskOrFunction); // Otherwise, just queue the task to be started later else _tasks.Enqueue(taskOrFunction); } } /// Called when a Task completes to potentially start the next in the queue. /// The task that completed. private void OnTaskCompletion(Task ignored) { lock (_tasks) { // The task completed, so nothing is currently in flight. // If there are any tasks in the queue, start the next one. _taskInFlight = null; if (_tasks.Count > 0) StartTask_CallUnderLock(_tasks.Dequeue()); } } /// Starts the provided task (or function that returns a task). /// The next task or function that returns a task. private void StartTask_CallUnderLock(object nextItem) { Task next = nextItem as Task; if (next == null) next = ((Func)nextItem)(); if (next.Status == TaskStatus.Created) next.Start(); _taskInFlight = next; next.ContinueWith(OnTaskCompletion); } } }