//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: RobinScheduler.cs // // Note: // using System; using System.Collections; using System.Diagnostics; using System.Threading; using Microsoft.Singularity; using Microsoft.Singularity.Scheduling; namespace Microsoft.Singularity.Scheduling { #if !SIMULATOR public class SystemScheduler { static public void RegisterScheduler() { Robin.RobinScheduler.RegisterScheduler(); } } #endif } namespace Microsoft.Singularity.Scheduling.Robin { /// /// Summary description for RobinScheduler. /// public class RobinScheduler : ICpuScheduler { public static readonly TimeSpan ContextSwitch = new TimeSpan(200); // minimum time slice to be worth scheduling the CPU: 1 millisec public static readonly TimeSpan MinSlice = new TimeSpan(10 * ContextSwitch.Ticks); public static readonly TimeSpan AFewSlice = new TimeSpan(3 * MinSlice.Ticks); public static readonly TimeSpan RobinSlice = new TimeSpan(10 * TimeSpan.TicksPerMillisecond); static public void RegisterScheduler() { CpuResource.RegisterSystemScheduler(new RobinScheduler()); } public override ISchedulerProcessor CreateSchedulerProcessor(Processor processor) { return new RobinProcessor(processor); } public override ISchedulerThread CreateSchedulerThread(Thread thread) { return new RobinThread(thread); } public override ISchedulerActivity CreateSchedulerActivity() { return new RobinActivity(); } public override ISchedulerCpuReservation ReserveCpu(ISchedulerActivity activity, CpuResourceAmount amount, TimeSpan period) { return null; } public bool ReserveRecurringCpu(Activity activity, ref TimeSpan amount, ref TimeSpan period) { return true; } public override bool ShouldReschedule() { RobinThread currentThread = GetCurrentThread(); if (Scheduler.TimerInterruptedFlag && currentThread != null) { //SchedulerClock.CheckInterrupt(); } if (currentThread == null || Scheduler.YieldFlag || currentThread.EnclosingThread.IsWaiting() || currentThread.EnclosingThread.IsStopped()) { Reschedule(); } return NeedToReschedule || Scheduler.TimerInterruptedFlag; // || timer should have fired but didn't? } // DI -- this is somehow similar to EnableInterrupts public override bool NextThread(out Thread nextThread) { Debug.Assert(!Processor.InterruptsDisabled()); bool iflag = Processor.DisableInterrupts(); bool halted = false; RobinThread currentThread = GetCurrentThread(); SchedulerClock.CheckInterrupt(); if (ShouldReschedule()) { //Debug.Print("Calling RescheduleInterrupt()\n"); halted = RescheduleInterrupt(); //TODO: RescheduleInterrupt returns true if the processor needs to be halted. currentThread = GetCurrentThread(); } else { //Debug.Print("No call to RescheduleInterrupt()\n"); } if (currentThread != null) { nextThread = currentThread.EnclosingThread; } else { Debug.Assert(halted); nextThread = null; } ((RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor).NeedToReschedule = false; // printf("-------------------------currentThread-Id %d\n", currentThread.Id); //return currentThread; Processor.RestoreInterrupts(iflag); return halted; } //public static void Yield(); #region Constants // Maximum Scheduling Period is 1 sec (i.e. in 100s of nanoseconds). static readonly TimeSpan AmountCpu = CpuResource.MaxPeriod; //TODO: TICKS vs. TIME_SPAN vs DATE_TIME #endregion #region Static data members static RobinActivity CircularActivityList = null; /// /// This must ALWAYS (when lock not held) point to a current resource /// container in the queue. /// static internal RobinActivity NextActivity; //moved to be processor specific //static RobinActivity RobinActivityNext = null; //static RobinActivity RobinActivityCurrent = null; // activity currently executing from static OneShotReservation ReservationFreePool; static TimeSpan IdleTime = new TimeSpan(0); static bool Idle { get { return GetCurrentProcessor().Idle; } } //moved to be processor specific //static RobinThread CurrentThreadGlobal = null; //static TimeSpan RobinSliceLeft = RobinScheduler.RobinSlice; //new TimeSpan(0); // the round robin list, if any, and its CPU slice //public static DateTime SchedulingTime = new DateTime(0); //static bool NeedToReschedule = false; public static TimeSpan LA_Time = RobinScheduler.MinSlice; public static DateTime SchedulingTime { get { return ((RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor).SchedulingTime; } } public static bool NeedToReschedule { get { return ((RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor).NeedToReschedule; } } #endregion public RobinScheduler() { } public override void Initialize() { RobinScheduler.InitializeScheduler(); } public static void InitializeScheduler() { //TODO: Should all the null's be added in here? for (int i=0; i 0); int newrefcnt = Interlocked.Decrement(ref reservation.ReferenceCount); if (newrefcnt == 0) { // Estimate may be positive when reservation reaches // its Deadline w/o using its Estimate Debug.Assert((reservation.Next == null) && (reservation.Previous == null)); if (reservation.OriginalThread != null) { reservation.OriginalThread.ReleaseProtected(); //Debug.Assert(reservation.ActiveThread != null); //reservation.ActiveThread.ReleaseProtected(); } else { Debug.Assert(reservation.AssociatedActivity != null); ActivityReleaseProtected(reservation.AssociatedActivity); } Debug.Assert(reservation.Next == null); CacheFreeReservation(reservation); } return newrefcnt; } public static OneShotReservation AllocateReservation() { OneShotReservation reservation; // DisableInterrupts(); reservation = IpcAllocateReservation(); // EnableInterrupts(); if (reservation == null) { reservation = new OneShotReservation(); //(PRESERVATION)malloc(sizeof(struct OneShotReservation)); // to be done while on the I-stack! reservation.Clear(); } return reservation; } static void FreeReservation(OneShotReservation reservation) { reservation = null; //free(reservation); } // Garbage-collect unused Reservations. public static int ReleaseReservation(OneShotReservation reservation) { // Debug.Assert(!Processor.InterruptsDisabled()); Debug.Assert(reservation.ReferenceCount > 0); int newrefcnt = reservation.ReferenceCount - 1; reservation.ReferenceCount = newrefcnt; if (newrefcnt == 0) { // Estimate may be positive when reservation reaches its Deadline w/o using its Estimate Debug.Assert((reservation.Next == null) && (reservation.Previous == null)); if (reservation.OriginalThread != null) { reservation.OriginalThread.Release(); //Debug.Assert(reservation.ActiveThread != null); //reservation.ActiveThread.Release(); } else if (reservation.AssociatedActivity != null) { ActivityObjRelease(reservation.AssociatedActivity); } CacheFreeReservation(reservation); } return newrefcnt; } // Like CheckFreeConstraint, but callable from the IPC path. // Because we can't call AllocateReservation, we use the helper thread // if there aren't any free Reservations. static void CacheFreeReservation(OneShotReservation reservation) { if (reservation.OriginalThread != null && reservation.OriginalThread.FreeReservation == null) { reservation.OriginalThread.FreeReservation = reservation; } else { IpcFreeReservation(reservation); } } // Callable with preemption disabled. Allocates a OneShotReservation // from the global free list. public static OneShotReservation IpcAllocateReservation() { OneShotReservation reservation; if ((reservation = ReservationFreePool) != null) { ReservationFreePool = reservation.FreeListNext; } return reservation; } // Callable with preemption disabled. Frees a OneShotReservation // to the global free list. public static void IpcFreeReservation(OneShotReservation reservation) { Debug.Assert(reservation != null); Debug.Assert(reservation.ReferenceCount == 0); reservation.FreeListNext = ReservationFreePool; ReservationFreePool = reservation; } ////////////////////////////////////////////////////////////////////// public static void AddRefReservation(OneShotReservation reservation) { Debug.Assert(reservation.ReferenceCount >= 0); reservation.ReferenceCount++; } #endregion #region Potpourri (Unregioned functions) public static RobinThread GetCurrentThread() { return ((RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor).RunningThread; } public static void IChangeCurrentThread(RobinThread thread) { ((RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor).RunningThread = thread; } // Auxiliary Routines // TODO: Revisit the necessity of these functions. public static TimeSpan minInterval(TimeSpan TimeInterv0, TimeSpan TimeInterv1) { return (TimeInterv0 < TimeInterv1? TimeInterv0: TimeInterv1); } public static DateTime minTime(DateTime Time0, DateTime Time1) { return (Time0 < Time1? Time0: Time1); } static TimeSpan maxInterval(TimeSpan TimeInterv0, TimeSpan TimeInterv1) { return (TimeInterv0 < TimeInterv1? TimeInterv1: TimeInterv0); } static DateTime maxTime(DateTime Time0, DateTime Time1) { return (Time0 < Time1? Time1: Time0); } internal static RobinProcessor GetCurrentProcessor() { return (RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor; } // Add Activity in the last Round-Robin position public static void EnqueueActivity(RobinActivity activity) { Debug.Assert(Processor.InterruptsDisabled()); if (CircularActivityList == null) { NextActivity = CircularActivityList /*= GetCurrentProcessor().CurrentActivity */= activity.Next = activity.Previous = activity; //Here modifying other processor specific data rather than global. // for (int i=0; i NewTimerTimeout) { return false; } Debug.Assert(Processor.InterruptsDisabled()); // Debug.Assert(CurrentThread().GetState() == Thread.ThreadState.ThreadRunning); Debug.Assert(NewTimerTimeout > SchedulingTime); // Debug.Assert(SleepTimeout > SchedulingTime); //TODO: Need LA_Time? // if (NewTimerTimeout > SleepTimeout - LA_Time) // NewTimerTimeout = SleepTimeout - LA_Time; if (NewTimerTimeout > RobinThread.GetSleepTimeout()) { NewTimerTimeout = RobinThread.GetSleepTimeout(); } //DebugStub.Print("Setting Next Interrupt for: {0}...\n", //__arglist(NewTimerTimeout.Ticks); bool success = SchedulerClock.SetNextInterrupt(NewTimerTimeout); //TODO: Perhaps only call this if the time changed. //DebugStub.Print(success ? "SUCCESS\n" : "FAILED\n"); return success; } public static void UpdateSchedulingStatus() { TimeSpan timeRun; RobinThread currentThread = GetCurrentThread(); DateTime newSchedulingTime = SystemClock.GetKernelTime(); timeRun = newSchedulingTime - SchedulingTime; RobinProcessor processor = (RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor; processor.SchedulingTime = newSchedulingTime; if (Idle) { // UpdateIdleTime IdleTime += timeRun; return; } // if IDLE compute CurrentPlanNode & set currentThread to null. if (currentThread != null) { // unless thread just exited. // Update Thread & Activity execution times. currentThread.AddExecutionTime(timeRun); Scheduler.CurrentTask().AddResourceAmountUsed(CpuResource.Provider().ResourceString, CpuResource.Provider().TimeToCpu(timeRun)); // if (currentThread.AssociatedActivity != null) { // currentThread.AssociatedActivity.MyRecurringCpuReservation.EnclosingCpuReservation.AddTimeUsed(timeRun); // } } if (OneShotReservation.CurrentReservation != null) { // Slice used for a reservation. OneShotReservation.CurrentReservation.Estimate -= timeRun; } else if (processor.CurrentActivity != null) { // Slice used for RoundRobin. processor.SliceLeft -= timeRun; processor.CurrentActivity = null; } } internal static void CheckInvariants() { if (CircularActivityList != null) { bool listNextFound = (CircularActivityList == NextActivity); RobinActivity start = CircularActivityList, current = CircularActivityList.Next, previous = CircularActivityList; while (current != start) { //perhaps check thread invariants here too. Debug.Assert(current != null, "resource container list is not circular"); Debug.Assert(current.Previous == previous, "back pointer doesn't match forward pointer"); if (current == NextActivity) { listNextFound = true; } previous = current; current = current.Next; } Debug.Assert(listNextFound, "NextActivity isn't in the loop!"); Debug.Assert(current.Previous == previous, "Head doesn't point to tail"); } } // Always called when another thread needs to be scheduled. static bool RescheduleInterrupt() { CheckInvariants(); Debug.Assert(Processor.InterruptsDisabled()); Debug.Assert(!Processor.InterruptsDisabled()); RobinThread currentThread = GetCurrentThread(); Debug.Assert(currentThread == null || currentThread.ActiveProcessor == GetCurrentProcessor()); if (currentThread != null) { currentThread.ActiveProcessor = null; } DateTime nextStart; RobinThread previousThread; RescheduleAgain: // !!!!!!!!!! SIM ONLY !!!!!!!!! if (Idle) { currentThread = null; } previousThread = currentThread; UpdateSchedulingStatus(); // if thread was the head of the runnable threads Q: // Advance the runnable threads Q. if ((currentThread != null) && (currentThread.AssociatedActivity != null) && (currentThread.AssociatedActivity.RunnableThreads == currentThread)) { currentThread.AssociatedActivity.RunnableThreads = currentThread.Next; Debug.Assert(currentThread.AssociatedActivity.RunnableThreads != null); } OneShotReservation.UpdateCurrentReservation(); OneShotReservation.ClearCurrentReservation(); currentThread = null; // Finished first stage, i.e. updated state. // Start second stage: wakeup threads & select Next CPU slice. RobinThread.WakeThreads(); // NOTE: In the original RoundRobin Simulator Code (& MMOSA code) // The call to DrainDeferredConditions() was made here. // In Singularity, this will basically be replaced with a // queue of wait-events to fix. OneShotReservation.FreshenReservationQueues(); TimeSpan currentNodeSliceLeft; RobinProcessor currentProcessor = (RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor; if (currentProcessor.CurrentActivity != null && currentProcessor.SliceLeft >= RobinScheduler.MinSlice) { currentThread = currentProcessor.CurrentActivity.GetRunnableThread(); if (currentThread != null) { goto exit; } } // Find Next Runnable Activity. currentProcessor.SliceLeft = RobinScheduler.RobinSlice; currentProcessor.CurrentActivity = NextActivity; // !!!!!!!!!!!!!SIM ONLY !!!!!!!!!! while ((currentThread = NextActivity.GetRunnableThread()) == null) { NextActivity = NextActivity.Next; //currentProcessor.SliceLeft = RobinScheduler.RobinSlice; if (NextActivity == currentProcessor.CurrentActivity) { // !!!!!!!!SIM ONLY !!!!!!! // in the real scheduler, execute halt if (OneShotReservation.IdleReservations != null) { // reuse nextStart nextStart = minTime(RobinThread.GetSleepTimeout(), OneShotReservation.IdleReservations.Start); } else { nextStart = RobinThread.GetSleepTimeout(); DebugStub.Print("Idle, sleeping until {0} cf maxvalue {1}\n", __arglist(nextStart.Ticks, DateTime.MaxValue.Ticks)); } if (nextStart == DateTime.MaxValue) { Scheduler.StopSystem(); } if (! ResetTimerTimeout(nextStart)) { //Error setting timer. Try scheduling again. DebugStub.Print("Thought Idle, failed to set interrupt.\n"); goto RescheduleAgain; } GetCurrentProcessor().Idle = true; // !!!!!!!!SIM ONLY !!!!!!! currentThread = null; // !!!!!!!!SIM ONLY !!!!!!! OneShotReservation.ClearCurrentReservation(); currentProcessor.CurrentActivity = null; if (DateTime.MaxValue != nextStart) { //Scheduler.LogTimeJump(); } //DebugStub.Print("Halted.\n"); IChangeCurrentThread(null); return true; // !!!!!!!!SIM ONLY !!!!!!! } // !!!!!!!!SIM ONLY !!!!!!! } //DebugStub.Print("Running Round Robin Resource Container\n"); currentProcessor.CurrentActivity = NextActivity; // we probably need only one of the two variables NextActivity = NextActivity.Next; exit: currentNodeSliceLeft = currentProcessor.SliceLeft; Debug.Assert(currentThread != null); if (currentThread != previousThread) { Scheduler.LogContextSwitch(); // Context Switch statistics } if (OneShotReservation.IdleReservations != null) { // reuse nextStart nextStart = minTime(RobinThread.GetSleepTimeout(), OneShotReservation.IdleReservations.Start); } else { nextStart = RobinThread.GetSleepTimeout(); } if (SchedulingTime + currentNodeSliceLeft /* CurrentSlice */ > nextStart) { currentNodeSliceLeft /* CurrentSlice */ = nextStart - currentProcessor.SchedulingTime; } Scheduler.LogReschedule(); if (!ResetTimerTimeout(currentProcessor.SchedulingTime + currentNodeSliceLeft) || Scheduler.TimerInterruptedFlag) { //TODO: What do we REALLY want here? currentThread = null; // !!!!!!!!SIM ONLY !!!!!!! OneShotReservation.ClearCurrentReservation(); currentProcessor.CurrentActivity = null; goto RescheduleAgain; } Debug.Assert(currentThread.ActiveProcessor == null); currentThread.ActiveProcessor = GetCurrentProcessor(); Debug.Assert(currentThread.ActiveProcessor != null); if (currentThread != previousThread) { IChangeCurrentThread(currentThread); } GetCurrentProcessor().Idle = false; // Not necessarily true: Debug.Assert(!Scheduler.TimerInterruptedFlag); CheckInvariants(); return false; } #endregion public static void Reschedule() { ((RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor).Reschedule(); } public static void ActivityObjAddRef(RobinActivity activity) { activity.ReferenceCount++; } public static void ActivityObjRelease(RobinActivity activity) { Debug.Assert(activity.ReferenceCount >= 1); activity.ReleaseReference(); } public static void ActivityReleaseProtected(RobinActivity activity) { Debug.Assert(activity.ReferenceCount >= 1); Debug.Assert(Processor.InterruptsDisabled(), "Interrupts Not Disabled!"); activity.ReleaseReference(); } } }