Welcome to mirror list, hosted at ThFree Co, Russian Federation.

AsyncWorkQueue.cs « Xamarin.PropertyEditing - github.com/xamarin/Xamarin.PropertyEditing.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
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;
		}
	}
}