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 : IEventLoopEntry
{
private readonly Task _task;
private readonly CancellationTokenSource _cts;
private bool _started;
private DateTime _created;
public bool IsRunning {
get {
if (_task.Status != TaskStatus.Running && _task.Status != TaskStatus.WaitingForActivation)
return _task.Status == TaskStatus.WaitingToRun;
return true;
}
}
public DateTime? Started {
get {
if (!IsRunning)
return null;
return _created;
}
}
public TaskEventLoopEntry(Action<CancellationToken> action)
{
_cts = new CancellationTokenSource();
_task = new Task(delegate {
action(_cts.Token);
}, _cts.Token);
}
public void Run(Action callback)
{
if (!_started) {
_created = DateTime.Now;
_task.Start();
_task.ContinueWith(delegate {
callback();
});
_started = true;
}
}
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 IEventLoopEntry 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;
}
_current = taskEventLoopEntry;
RunCurrent();
return taskEventLoopEntry;
}
}
public void Spin()
{
lock (this) {
if (_current != null && _current.IsRunning)
return;
_current = (Dequeue(TaskPriority.Critical) ?? Dequeue(TaskPriority.Microtask) ?? Dequeue(TaskPriority.Normal) ?? Dequeue(TaskPriority.None));
}
RunCurrent();
}
public void CancelAll()
{
lock (this) {
foreach (KeyValuePair<TaskPriority, Queue<TaskEventLoopEntry>> queue in _queues) {
Queue<TaskEventLoopEntry> value = queue.Value;
foreach (TaskEventLoopEntry item in value) {
item.Cancel();
}
value.Clear();
}
_queues.Clear();
if (_current != null)
_current.Cancel();
}
}
private void RunCurrent()
{
_current?.Run(delegate {
lock (this) {
_current = null;
}
Spin();
});
}
private TaskEventLoopEntry Dequeue(TaskPriority priority)
{
if (_queues.ContainsKey(priority) && _queues[priority].Count != 0)
return _queues[priority].Dequeue();
return null;
}
}
}