//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: Scheduler.cs // // Note: // // #define DEBUG_SCHEDULER using System; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; using Microsoft.Singularity; using Microsoft.Singularity.Hal; using Microsoft.Singularity.Io; namespace Microsoft.Singularity.Scheduling { public class Scheduler { public static readonly TimeSpan MaxPeriod = new TimeSpan(10000000); private static bool yieldFlag; public static bool YieldFlag { get { return yieldFlag; } set { yieldFlag = value; } } private static bool timerInterruptedFlag = true; public static bool TimerInterruptedFlag { get { return timerInterruptedFlag; } set { timerInterruptedFlag = value; } } ////////////////////////////////////////////////////////////////////// /// /// Return the Task object that the calling thread is currently working on behalf of. /// public static Task CurrentTask() { return Thread.CurrentThread.CurrentTask(); } /// /// Return the Activity object that the calling thread is currently working on behalf of. /// public static Activity CurrentActivity() { return Thread.CurrentThread.Activity; // XXX TBD } /// /// Information logging used for debugging schedulers. /// [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] [CLSCompliant(false)] public static void LogWakeThread(Thread thread) { } [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] public static void LogSchedulerLate() { } [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] public static void LogContextSwitch() { } [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] public static void LogReservationId(ISchedulerTask task) { } [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] [CLSCompliant(false)] public static void LogBeginConstraint(Thread foo, bool ok, ulong start, ulong stop) { } [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] public static void LogEndConstraint(TimeSpan diff) { } [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] public static void LogTimeJump() { } [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] public static void LogSleepAdd() { } [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] public static void LogDeleteActivity() { } [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] public static void LogEndConstraint() { } [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] public static void LogResolveConstraint() { } [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] public static void LogReschedule() { } [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] public static void LogReservedActivity(int proxyCount) { } [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] public static void LogRecurringCpuReservation() { } // ////////////////////////////////////////////////////////////////////////////// [CLSCompliant(false)] public static void OnTimerInterrupt(IHalTimer timer) { // For now: We don't invoke the scheduler here, as the base // interrupt function will switch to the scheduler // thread context. (Q: Should we do that here instead?) // Instead, for now just set the interrupted flag. // Also -- in the future instead we may be calling // scheduler.NextThread() and setting to that context. //DebugStub.Print("Timer Interrupt: {0} #\n", __arglist(kCurrentTime)); Scheduler.TimerInterruptedFlag = true; } [CLSCompliant(false)] public static Thread GetResumedThread() { if (!CpuResource.Provider().ShouldReschedule() && !IoSystem.DeferredSignalsPending()) { //Kernel.Waypoint(302); return Thread.CurrentThread; } else { if (Thread.CurrentThread != Thread.schedulingThread) { //Kernel.Waypoint(303); return Thread.schedulingThread; } else { //Kernel.Waypoint(304); return Thread.CurrentThread; } } } [CLSCompliant(false)] public bool GetNextThread(out Thread next) { //Kernel.Waypoint(3); // DrainDeferredSignals returns true if any signals were queued. IoSystem.DrainDeferredSignals(); //Kernel.Waypoint(4); // XXX If there is an interrupt right here that adds // a deferred signals to the list it won't get // signalled until after the next thread runs. // // A potential fix is to loop here and make sure the flag is // clear before scheduling the next thread. //DebugStub.Print("Scheduler.GetNextThread()\n"); //return (item != null) ? item.Thread : null; Debug.Assert(Thread.CurrentThread != null); Thread foo; //Kernel.Waypoint(5); bool halted = CpuResource.Provider().NextThread(out foo); next = (Thread)foo; yieldFlag = false; //DebugStub.Print("Exiting Scheduler.GetNextThread({0:x8},{1}\n", // __arglist(next.threadIndex, halted)); //Kernel.Waypoint(6); return halted; } static bool notExiting = true; public static void StopSystem() { notExiting = false; } //TODO: Replace with a Processor Halt! void Halt() { // DebugStub.Print("Doing Processor hlt\n"); #if false //[SOSP-2005] Disable hlt so we can always use the cycle counter! Tracing.Log(Tracing.Debug, "Halting processor with hlt."); Processor.HaltUntilInterrupt(); #endif // Check for a debug break. if (DebugStub.PollForBreak()) { DebugStub.Print("Debugger breakin.\n"); DebugStub.Break(); } } [CLSCompliant(false)] public void Start(Processor rootProcessor) { // WARNING: Because the scheduler thread cannot go to sleep, // we must avoid any operation (such as memory allocation) // that might require the current thread to block. #if DEBUG_SCHEDULER || !DEBUG_SCHEDULER Tracing.Log(Tracing.Audit, "Scheduler Started."); #endif Thread.schedulingThread = Thread.CurrentThread; IHalTimer timer = rootProcessor.Timer; timer.SetNextInterrupt(timer.MaxInterruptInterval); Thread next; #if DEBUG_SCHEDULER || !DEBUG_SCHEDULER Tracing.Log(Tracing.Audit, "Scheduler Loop Started."); #endif while (notExiting) { bool halted = GetNextThread(out next); Debug.Assert(halted || next != null); if (!halted) { #if DEBUG_SCHEDULER Tracing.Log(Tracing.Audit, "Scheduling thread {0:x3}", (UIntPtr)unchecked((uint)next.threadIndex)); #endif next.Schedule(); if (next != null && next.IsStopping()) { #if DEBUG_SCHEDULER Tracing.Log(Tracing.Audit, "Cleaning up after thread {0:x3}", (UIntPtr)unchecked((uint)next.threadIndex)); #endif next.Cleanup(); } } else { if (notExiting) { Kernel.Waypoint(99); Halt(); } } } #if DEBUG_SCHEDULER || !DEBUG_SCHEDULER Tracing.Log(Tracing.Audit, "Scheduler loop terminated."); #endif } } }