static class · System.Threading.Tasks
Scheduler
public static class Scheduler
Runs tasks on this thread, one at a time: those ready (in order), then the timers that are due; when nothing is ready it sleeps until the next timer.
- Kind
- static class
- Namespace
- System.Threading.Tasks
- Assembly
- System.Core
- Access
- public, static, abstract
- Source file
Tasks.ant
Inheritance
- Base classes
-
AnyScheduler
Properties
Whether a thread sleeps on Changed (read holding Sync).
Methods
Whether the caller is the scheduler's thread (the program's first).
Runs task in its turn (Run()). On another thread (holding Sync): it goes to the scheduler's inbox.
Started()
#
A Task.Run body starts, and has finished.
Finished()
#
Waits (another thread than the scheduler's) until task has finished.
Done in its turn (after what is ready now).
Done after milliseconds.
Runs ready tasks and timers until task has finished (or nothing is left that could finish it).
The timer that is due first.
Completes the timers whose time has come (the first due first): whether there was one.
Timers()
#
Completes the timers that are due; when none is, sleeps until the first one. Once threads complete tasks, the sleep is on Changed (a task completed on another thread wakes it): the timer only bounds it.
Private and internal members (12)
Once Task.Run was used: tasks made ready on other threads (under Sync), and how many Task.Run bodies are running.
Threads asleep on Changed (under Sync): a change wakes them only if there are some.
Sleep()
#
Sleeps on Changed (holding Sync).
Drain()
#
Moves what other threads made ready to the ready list.
Inherited members (5)
GetTypemethod from AnyUndescribedmethod from AnyToStringmethod from AnyHashmethod from Anydestroymethod from Any
Source
Scheduler as Antril, decompiled from its Larva HIR.
// System.Threading.Tasks.Scheduler — System.Core (decompiled from Larva HIR)
import System;
import System.Threading;
into namespace System.Threading.Tasks
{
/// Runs tasks on this thread, one at a time: those ready (in order), then the timers that are due; when nothing is
/// ready it sleeps until the next timer.
public static class Scheduler
{
List<Task> ready;
i32 first;
List<Task> timers;
List<i64> due;
/// Once Task.Run was used: tasks made ready on other threads (under Sync), and how many Task.Run bodies are running.
bool threaded;
Mutex sync;
Condition changed;
List<Task> inbox;
i32 running;
/// Threads asleep on Changed (under Sync): a change wakes them only if there are some.
i32 sleepers;
/// Sleeps on Changed (holding Sync).
Sleep()
{
sleepers++;
changed.Wait(sync);
sleepers--;
}
/// Moves what other threads made ready to the ready list.
[NoInline]
Drain()
{
sync.Lock();
{
ListIterator<Task> _iterator0 = inbox.Iterator();
loop (_iterator0.Next())
{
Task task = _iterator0.Current;
ready.Add(task);
}
}
inbox.Clear();
sync.Unlock();
}
public:
/// Whether the caller is the scheduler's thread (the program's first).
OnItsThread() : bool => Thread.CurrentId == 1;
/// Runs `task` in its turn (`Run()`). On another thread (holding Sync): it goes to the scheduler's inbox.
Ready(Task task)
{
if (threaded && !OnItsThread())
inbox.Add(task);
else
ready.Add(task);
}
/// A Task.Run body starts, and has finished.
Started()
{
sync.Lock();
threaded = true;
running++;
sync.Unlock();
}
Finished()
{
sync.Lock();
running--;
bool sleeping = sleepers > 0;
sync.Unlock();
if (sleeping)
changed.Broadcast();
}
/// Waits (another thread than the scheduler's) until `task` has finished.
BlockUntil(Task task)
{
loop (i32 spin = 0; spin < 1000 && !task.IsCompleted; spin++)
Thread.SpinWait(20);
loop (i32 spin = 0; spin < 32 && !task.IsCompleted; spin++)
Thread.Yield();
sync.Lock();
loop (!task.IsCompleted)
Sleep();
sync.Unlock();
}
/// Done in its turn (after what is ready now).
Soon(Task task)
{
ready.Add(new CompleteTask(task));
}
/// Done after `milliseconds`.
After(Task task, i32 milliseconds)
{
timers.Add(task);
due.Add(Environment.TickCount64 + (i64)milliseconds);
}
/// Runs ready tasks and timers until `task` has finished (or nothing is left that could finish it).
RunUntil(Task task)
{
i32 ran = 0;
loop (!task.IsCompleted)
{
if (threaded && inbox.Count > 0)
Drain();
if (first < ready.Count)
{
Task next = ready[first];
first++;
bool passEnded = first == ready.Count;
if (passEnded)
{
ready.Clear();
first = 0;
}
next.Run();
ran++;
if (timers.Count > 0 && (passEnded || ran >= 32))
{
ran = 0;
Due(Environment.TickCount64);
}
continue;
}
ran = 0;
if (threaded && timers.Count == 0)
{
loop (i32 spin = 0; spin < 1000 && inbox.Count == 0 && !task.IsCompleted; spin++)
Thread.SpinWait(20);
loop (i32 spin = 0; spin < 32 && inbox.Count == 0 && !task.IsCompleted; spin++)
Thread.Yield();
sync.Lock();
loop (inbox.Count == 0 && !task.IsCompleted && running > 0)
Sleep();
bool stuck = inbox.Count == 0 && !task.IsCompleted;
sync.Unlock();
if (stuck)
throw new InvalidOperationError("Task.Wait: the task waits for something that never finishes");
continue;
}
if (timers.Count == 0)
throw new InvalidOperationError("Task.Wait: the task waits for something that never finishes");
Timers();
}
}
/// The timer that is due first.
Soonest() : i32
{
i32 soonest = 0;
loop (i32 t = 1..timers.Count)
{
if (due[t] < due[soonest])
soonest = t;
}
return soonest;
}
/// Completes the timers whose time has come (the first due first): whether there was one.
Due(i64 now) : bool
{
bool any = false;
loop (timers.Count > 0)
{
i32 soonest = Soonest();
if (due[soonest] > now)
break;
Task timer = timers[soonest];
timers.RemoveAt(soonest);
due.RemoveAt(soonest);
timer.Complete();
any = true;
}
return any;
}
/// Completes the timers that are due; when none is, sleeps until the first one. Once threads complete tasks, the
/// sleep is on Changed (a task completed on another thread wakes it): the timer only bounds it.
Timers()
{
i64 now = Environment.TickCount64;
if (Due(now))
return;
i64 soon = due[Soonest()];
if (soon <= now)
{
Due(soon);
return;
}
if (!threaded)
{
Thread.Sleep((i32)(soon - now));
Due(soon);
return;
}
sync.Lock();
if (inbox.Count == 0)
{
sleepers++;
changed.Wait(sync, (i32)(soon - now));
sleepers--;
}
sync.Unlock();
Due(Environment.TickCount64);
}
bool Threaded
{
get => threaded;
}
Mutex Sync
{
get => sync;
}
Condition Changed
{
get => changed;
}
/// Whether a thread sleeps on Changed (read holding Sync).
bool Sleeping
{
get => sleepers > 0;
}
}
}