summaryrefslogtreecommitdiff
path: root/modules/mono/glue/cs_files/GodotTaskScheduler.cs
blob: f587645a499bb4914454957bfca55114fd9231d6 (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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Godot
{
	public class GodotTaskScheduler : TaskScheduler
	{
		private GodotSynchronizationContext Context { get; set; }
		private readonly LinkedList<Task> _tasks = new LinkedList<Task>();

		public GodotTaskScheduler()
		{
			Context = new GodotSynchronizationContext();
		}

		protected sealed override void QueueTask(Task task)
		{
			lock (_tasks)
			{
				_tasks.AddLast(task);
			}
		}

		protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
		{
			if (SynchronizationContext.Current != Context)
			{
				return false;
			}

			if (taskWasPreviouslyQueued)
			{
				TryDequeue(task);
			}

			return base.TryExecuteTask(task);
		}

		protected sealed override bool TryDequeue(Task task)
		{
			lock (_tasks)
			{
				return _tasks.Remove(task);
			}
		}

		protected sealed override IEnumerable<Task> GetScheduledTasks()
		{
			lock (_tasks)
			{
				return _tasks.ToArray();
			}
		}

		public void Activate()
		{
			SynchronizationContext.SetSynchronizationContext(Context);
			ExecuteQueuedTasks();
			Context.ExecutePendingContinuations();
		}

		private void ExecuteQueuedTasks()
		{
			while (true)
			{
				Task task;

				lock (_tasks)
				{
					if (_tasks.Any())
					{
						task = _tasks.First.Value;
						_tasks.RemoveFirst();
					}
					else
					{
						break;
					}
				}

				if (task != null)
				{
					if (!TryExecuteTask(task))
					{
						throw new InvalidOperationException();
					}
				}
			}
		}
	}
}