337 lines
12 KiB
C#
337 lines
12 KiB
C#
//------------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Description: $filename$
|
|
//
|
|
// Minimal round-robin style without priorities scheduler.
|
|
//
|
|
// AffinityScheduler favors thread that have recently become unblocked
|
|
// and tries to avoid reading the clock or reseting the timer as
|
|
// much as possible.
|
|
//
|
|
// The minimal scheduler maintains two queues of threads that can
|
|
// be scheduled. The unblockedThreads queue contains threads which
|
|
// have become unblocked during this scheduling quantum; mostly,
|
|
// these are threads that were unblocked by the running thread.
|
|
// The runnableThreads queue contains all other threads that are
|
|
// currently runnable. If the current thread blocks, AffinityScheduler
|
|
// will schedule threads from the unblockedThread queue, without
|
|
// reseting the timer. When the timer finaly fires, AffinityScheduler
|
|
// moves all unblockedThreads to the end of the runnableThreads
|
|
// queue and schedules the next runnableThread.
|
|
//------------------------------------------------------------------------------
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Diagnostics;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
|
|
using Microsoft.Bartok.Options;
|
|
|
|
using Microsoft.Singularity;
|
|
using Microsoft.Singularity.Io;
|
|
using Microsoft.Singularity.Scheduling;
|
|
|
|
namespace Microsoft.Singularity.Scheduling
|
|
{
|
|
///
|
|
/// <summary>
|
|
/// Summary description for AffinityScheduler.
|
|
/// </summary>
|
|
///
|
|
[NoCCtor]
|
|
[CLSCompliant(false)]
|
|
public class AffinityScheduler : Scheduler
|
|
{
|
|
public AffinityScheduler()
|
|
{
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Initialize min scheduler
|
|
/// </summary>
|
|
///
|
|
public override void Initialize() {
|
|
|
|
// HACK use 30ms for now to give time for debug output.
|
|
// If busy, don't run for more than 10ms on same task.
|
|
TimeSpan minSlice = TimeSpan.FromMilliseconds(10);
|
|
|
|
schedulers = new BaseScheduler [Processor.processorTable.Length];
|
|
|
|
// Create the idle threads and put them on the idleThreads loop.
|
|
for (int i = 0; i < Processor.processorTable.Length; i++) {
|
|
|
|
// Create scheduler
|
|
schedulers[i] = BaseSchedulerManager.CreateScheduler(minSlice, i);
|
|
}
|
|
|
|
// Wait until first dispatcher will call into the scheduler
|
|
numberOfActiveDispatchers = 0;
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Finalize scheduler object
|
|
/// </summary>
|
|
///
|
|
public override void Finalize()
|
|
{
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Notify scheduler about new dispatcher
|
|
/// </summary>
|
|
///
|
|
public override void OnDispatcherInitialize(int dispatcherId)
|
|
{
|
|
// Increment number of active dispatchers
|
|
int dispatchers = Interlocked.Increment (ref numberOfActiveDispatchers);
|
|
|
|
// Check if a now we really MP enabled
|
|
if (dispatchers == Processor.ExpectedProcessors) {
|
|
isMPEnabled = true;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Attach thread to scheduler: thread specific initializtion
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread to attach </param>
|
|
/// <param name="constructorCalled">Have we called thread constructor </param>
|
|
///
|
|
public override void OnThreadStateInitialize(Thread thread, bool constructorCalled)
|
|
{
|
|
// Only initialize thread-local state! No locks held and interrupts on.
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Start thread - put a thread on unblocked queue so it can be run
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread to start </param>
|
|
///
|
|
public override void OnThreadStart(Thread thread)
|
|
{
|
|
// TODO: this needs to check whether the processor is running yet.
|
|
int threadID = thread.GetThreadId();
|
|
// NOTE Affinity may be legally set to any int. NoAffinity(-1) is special.
|
|
// All other ints must be converted to a legal index into the processor table
|
|
int initial = thread.Affinity == Thread.NoAffinity
|
|
? threadID
|
|
: Math.Abs(thread.Affinity);
|
|
int cpu = isMPEnabled ? initial % numberOfActiveDispatchers : 0;
|
|
thread.Affinity = cpu;
|
|
schedulers[cpu].OnThreadStart(thread);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Block thread - put a thread on block queue and retrieve next thread to run
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread that blocks </param>
|
|
/// <param name="stop">Amount of time for the thread to be blocked</param>
|
|
///
|
|
public override void OnThreadBlocked(Thread thread, SchedulerTime stop)
|
|
{
|
|
schedulers[thread.Affinity].OnThreadBlocked(thread, stop);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Unblock thread - resume thread by putting it on unblock queue. This method
|
|
/// can be invoked by threads running on other processors
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread to resume </param>
|
|
///
|
|
[NoHeapAllocation]
|
|
public override void OnThreadUnblocked(Thread thread)
|
|
{
|
|
schedulers[thread.Affinity].OnThreadUnblocked(thread);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Yield thread - suspend thread based on time slice
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread that is yielding </param>
|
|
///
|
|
[NoHeapAllocation]
|
|
public override void OnThreadYield(Thread thread)
|
|
{
|
|
schedulers[thread.Affinity].OnThreadYield(thread);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Stop thread execution
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread that is stopping </param>
|
|
///
|
|
public override void OnThreadStop(Thread thread)
|
|
{
|
|
schedulers[thread.Affinity].OnThreadStop(thread);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Increment frozen counter
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread for which to increment freeze counter </param>
|
|
///
|
|
public override void OnThreadFreezeIncrement(Thread thread)
|
|
{
|
|
schedulers[thread.Affinity].OnThreadFreezeIncrement(thread);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Decrement frozen counter
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread for which to update freeze counter </param>
|
|
///
|
|
public override void OnThreadFreezeDecrement(Thread thread)
|
|
{
|
|
schedulers[thread.Affinity].OnThreadFreezeDecrement(thread);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Dispatch timer interrupt
|
|
/// </summary>
|
|
///
|
|
/// <param name = "affinity">Processor affinity</param>
|
|
/// <param name = "now">Current time</param>
|
|
/// <param name ="deferredWakeupQueue">
|
|
/// Queue used to put threads to be waken up. Latter on dispatcher will add threads
|
|
/// to scheduler's runnable queue
|
|
/// </param>
|
|
///
|
|
[CLSCompliant(false)]
|
|
[NoHeapAllocation]
|
|
public override TimeSpan OnTimerInterrupt(int affinity, SchedulerTime now)
|
|
{
|
|
return schedulers[affinity].OnTimerInterrupt(affinity, now);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Add thread to runnable queue. This methods is called by dispatcher at the
|
|
/// point when both dispatcher lock and interrupts are disabled
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread to add to runnable queue </param>
|
|
/// <param name="hint">Place to where enqueue thread in a runnable queue</param>
|
|
///
|
|
[Inline]
|
|
[NoHeapAllocation]
|
|
public override void AddRunnableThread(Thread thread)
|
|
{
|
|
schedulers[thread.Affinity].AddRunnableThread(thread);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Run scheduling policy to decide the next thread to run
|
|
/// </summary>
|
|
///
|
|
/// <param name="schedulerAffinity">The affinity of the scheduler</param>
|
|
/// <param name="runningThreadAffinity">The running thread returned should be
|
|
/// set to this affinity. It's same as schedulerAffinity except during
|
|
/// thread migration
|
|
/// </param>
|
|
/// <param name="currentThread">the thread currently running</param>
|
|
/// <param name="schedulerAction">thread state to change to for the current thread</param>
|
|
/// <param name="currentTime">Current system time</param>
|
|
///
|
|
[NoHeapAllocation]
|
|
public override Thread RunPolicy(
|
|
int schedulerAffinity,
|
|
int runningThreadAffinity,
|
|
Thread currentThread,
|
|
ThreadState schedulerAction,
|
|
SchedulerTime currentTime)
|
|
{
|
|
// Assert preconditions: current thread can be either NULL or its base scheduler
|
|
// should be the same as specified by affinity
|
|
VTable.Assert(currentThread == null ||
|
|
currentThread.Affinity == schedulerAffinity);
|
|
|
|
// Use affinity to derive actual scheduler
|
|
return schedulers[schedulerAffinity].RunPolicy(
|
|
schedulerAffinity,
|
|
runningThreadAffinity,
|
|
currentThread,
|
|
schedulerAction,
|
|
currentTime);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Suspend thread and wait until it is suspended.
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread to suspend </param>
|
|
///
|
|
[NoHeapAllocation]
|
|
public override void Suspend(Thread thread)
|
|
{
|
|
schedulers[thread.Affinity].Suspend(thread);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Resume thread from being suspended
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread to resume </param>
|
|
///
|
|
[NoHeapAllocation]
|
|
public override void Resume(Thread thread)
|
|
{
|
|
schedulers[thread.Affinity].Resume(thread);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Retrieve scheduler lock - used by dispatcher to protect scheduler state
|
|
/// </summary>
|
|
///
|
|
/// <param name="affinity">Affinity of dispatcher making actual call</param>
|
|
///
|
|
[CLSCompliant(false)]
|
|
[NoHeapAllocation]
|
|
internal override SchedulerLock RetrieveSchedulerLock(int affinity)
|
|
{
|
|
// Use the our defualt scheder's to return
|
|
return schedulers[affinity].MyLock;
|
|
}
|
|
|
|
/// <summary>An array of schedulers</summary>
|
|
private static BaseScheduler [] schedulers;
|
|
|
|
/// <summary> A number of active dispatchers </summary>
|
|
private static int numberOfActiveDispatchers;
|
|
|
|
/// <summary> Flag showing whether multi processing is enabled </summary>
|
|
private static bool isMPEnabled;
|
|
}
|
|
|
|
}
|
|
|