TaskEventLoop
The default event loop.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AngleSharp
{
internal sealed class TaskEventLoop : IEventLoop
{
private sealed class TaskEventLoopEntry : ICancellable
{
private readonly CancellationTokenSource _cts;
private readonly Action<CancellationToken> _action;
private Task _task;
public bool IsCompleted {
get {
if (_task != null)
return _task.IsCompleted;
return false;
}
}
public bool IsRunning {
get {
if ((_task == null || _task.Status != TaskStatus.Running) && _task.Status != TaskStatus.WaitingForActivation && _task.Status != TaskStatus.WaitingToRun)
return _task.Status == TaskStatus.WaitingForChildrenToComplete;
return true;
}
}
public TaskEventLoopEntry(Action<CancellationToken> action)
{
_cts = new CancellationTokenSource();
_action = action;
}
public void Run(Action callback)
{
if (_task == null)
_task = TaskEx.Run(delegate {
_action(_cts.Token);
callback();
}, _cts.Token);
}
public void Cancel()
{
_cts.Cancel();
}
}
private readonly Dictionary<TaskPriority, Queue<TaskEventLoopEntry>> _queues;
private TaskEventLoopEntry _current;
public TaskEventLoop()
{
_queues = new Dictionary<TaskPriority, Queue<TaskEventLoopEntry>>();
_current = null;
}
public ICancellable Enqueue(Action<CancellationToken> task, TaskPriority priority)
{
TaskEventLoopEntry taskEventLoopEntry = new TaskEventLoopEntry(task);
lock (this) {
Queue<TaskEventLoopEntry> value = null;
if (!_queues.TryGetValue(priority, out value)) {
value = new Queue<TaskEventLoopEntry>();
_queues.Add(priority, value);
}
if (_current != null) {
value.Enqueue(taskEventLoopEntry);
return taskEventLoopEntry;
}
SetCurrent(taskEventLoopEntry);
return taskEventLoopEntry;
}
}
public void Spin()
{
lock (this) {
TaskEventLoopEntry current = _current;
if (current == null || !current.IsRunning)
SetCurrent(Dequeue(TaskPriority.Critical) ?? Dequeue(TaskPriority.Microtask) ?? Dequeue(TaskPriority.Normal) ?? Dequeue(TaskPriority.None));
}
}
public void CancelAll()
{
lock (this) {
foreach (KeyValuePair<TaskPriority, Queue<TaskEventLoopEntry>> queue in _queues) {
Queue<TaskEventLoopEntry> value = queue.Value;
while (value.Count > 0) {
value.Dequeue().Cancel();
}
}
_queues.Clear();
_current?.Cancel();
}
}
private void SetCurrent(TaskEventLoopEntry entry)
{
_current = entry;
entry?.Run(Continue);
}
private void Continue()
{
lock (this) {
_current = null;
}
Spin();
}
private TaskEventLoopEntry Dequeue(TaskPriority priority)
{
if (_queues.ContainsKey(priority) && _queues[priority].Count != 0)
return _queues[priority].Dequeue();
return null;
}
}
}