using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LeanCloud.Storage.Internal {
///
/// A helper class for enqueuing tasks
///
public class TaskQueue {
///
/// We only need to keep the tail of the queue. Cancelled tasks will
/// just complete normally/immediately when their turn arrives.
///
private Task tail;
private readonly object mutex = new object();
///
/// Gets a cancellable task that can be safely awaited and is dependent
/// on the current tail of the queue. This essentially gives us a proxy
/// for the tail end of the queue whose awaiting can be cancelled.
///
/// A cancellation token that cancels
/// the task even if the task is still in the queue. This allows the
/// running task to return immediately without breaking the dependency
/// chain. It also ensures that errors do not propagate.
/// A new task that should be awaited by enqueued tasks.
private Task GetTaskToAwait(CancellationToken cancellationToken) {
lock (mutex) {
Task toAwait = tail ?? Task.FromResult(true);
return toAwait.ContinueWith(task => { }, cancellationToken);
}
}
///
/// Enqueues a task created by . If the task is
/// cancellable (or should be able to be cancelled while it is waiting in the
/// queue), pass a cancellationToken.
///
/// The type of task.
/// A function given a task to await once state is
/// snapshotted (e.g. after capturing session tokens at the time of the save call).
/// Awaiting this task will wait for the created task's turn in the queue.
/// A cancellation token that can be used to
/// cancel waiting in the queue.
/// The task created by the taskStart function.
public T Enqueue(Func taskStart, CancellationToken cancellationToken)
where T : Task {
Task oldTail;
T task;
lock (mutex) {
oldTail = this.tail ?? Task.FromResult(true);
// The task created by taskStart is responsible for waiting the
// task passed to it before doing its work (this gives it an opportunity
// to do startup work or save state before waiting for its turn in the queue
task = taskStart(GetTaskToAwait(cancellationToken));
// The tail task should be dependent on the old tail as well as the newly-created
// task. This prevents cancellation of the new task from causing the queue to run
// out of order.
this.tail = Task.WhenAll(oldTail, task);
}
return task;
}
public object Mutex { get { return mutex; } }
}
}