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

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

        public GodotTaskScheduler()
        {
            Context = new GodotSynchronizationContext();
            SynchronizationContext.SetSynchronizationContext(Context);
        }

        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 TryExecuteTask(task);
        }

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

        protected sealed override IEnumerable<Task> GetScheduledTasks()
        {
            lock (_tasks)
            {
                foreach (Task task in _tasks)
                    yield return task;
            }
        }

        public void Activate()
        {
            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();
                    }
                }
            }
        }
    }
}