using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Xamarin.PropertyEditing
{
///
/// This is NOT thread safe. It serves purely to keep async-waiting UI events in order without blocking the UI thread.
/// The added capability here past just waiting on a task and swapping it with the new wait is child requests will pass
/// through.
///
internal class AsyncWorkQueue
{
public Task RequestAsyncWork (object requester)
{
var worker = new AsyncValueWorker (requester, this);
this.workers.AddLast (worker.Node);
if (this.workers.Count == 1 || ReferenceEquals (requester, this.activeRequester)) {
worker.Completion.SetResult (worker);
this.activeRequester = requester;
}
return worker.Completion.Task;
}
private object activeRequester;
private readonly LinkedList workers = new LinkedList();
private void CompleteWork (AsyncValueWorker worker)
{
LinkedListNode left = worker.Node.Previous, right = worker.Node.Next;
AsyncValueWorker toFree = left?.Value ?? right?.Value;
List related = null;
// If a worker is still in the queue, it's still working. All children/siblings of the
// current requester must be finished before it can move on.
while (left != null || right != null) {
if (ReferenceEquals (left?.Value.Requester, worker.Requester) || ReferenceEquals (right?.Value.Requester, worker.Requester)) {
toFree = null;
break;
}
if (ReferenceEquals (left?.Value.Requester, toFree?.Requester)) {
if (related == null)
related = new List ();
related.Add (left.Value);
}
if (ReferenceEquals (right?.Value.Requester, toFree?.Requester)) {
if (related == null)
related = new List ();
related.Add (right.Value);
}
left = left?.Previous;
right = right?.Next;
}
this.workers.Remove (worker);
if (toFree != null) {
this.activeRequester = toFree.Requester;
toFree.Completion.SetResult (toFree);
if (related != null) {
// Once the active requester changes, we need to sure its children/siblings are all allowed
// same as if it had been the active one first.
foreach (var w in related) {
w.Completion.TrySetResult (w);
}
}
}
}
private class AsyncValueWorker
: IDisposable
{
public AsyncValueWorker (object requester, AsyncWorkQueue queue)
{
this.queue = queue;
Requester = requester;
Node = new LinkedListNode (this);
}
public object Requester
{
get;
}
public LinkedListNode Node
{
get;
}
public TaskCompletionSource Completion
{
get;
} = new TaskCompletionSource ();
public void Dispose ()
{
if (this.isDisposed)
return;
this.isDisposed = true;
this.queue.CompleteWork (this);
}
private bool isDisposed;
private readonly AsyncWorkQueue queue;
}
}
}