blob: 9d47e4a8e6231ae092465e8988983f57946dc770 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Xamarin.PropertyEditing
{
/// <remarks>
/// 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.
/// </remarks>
internal class AsyncWorkQueue
{
public Task<IDisposable> 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<AsyncValueWorker> workers = new LinkedList<AsyncValueWorker>();
private void CompleteWork (AsyncValueWorker worker)
{
LinkedListNode<AsyncValueWorker> left = worker.Node.Previous, right = worker.Node.Next;
AsyncValueWorker toFree = left?.Value ?? right?.Value;
List<AsyncValueWorker> 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<AsyncValueWorker> ();
related.Add (left.Value);
}
if (ReferenceEquals (right?.Value.Requester, toFree?.Requester)) {
if (related == null)
related = new List<AsyncValueWorker> ();
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<AsyncValueWorker> (this);
}
public object Requester
{
get;
}
public LinkedListNode<AsyncValueWorker> Node
{
get;
}
public TaskCompletionSource<IDisposable> Completion
{
get;
} = new TaskCompletionSource<IDisposable> ();
public void Dispose ()
{
if (this.isDisposed)
return;
this.isDisposed = true;
this.queue.CompleteWork (this);
}
private bool isDisposed;
private readonly AsyncWorkQueue queue;
}
}
}
|