//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: RialtoScheduler.cs // // Note: // // #define LIFO #define ASCEND_RESERV #define LOG_SCHEDULER_DETAILS // #define DEBUG_TREE 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() { Rialto.RialtoScheduler.RegisterScheduler(); } } #endif } namespace Microsoft.Singularity.Scheduling.Rialto { /// /// Summary description for RialtoScheduler. /// public class RialtoScheduler : ICpuScheduler { #region Constants static readonly TimeSpan AmountCpu = CpuResource.MaxPeriod; //(MaxPeriod * 100); //TODO: TICKS vs. TIME_SPAN vs DATE_TIME public static readonly TimeSpan ContextSwitch = new TimeSpan(200); 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); #endregion #region Static data members public static OneShotReservation ReservationFreePool; public static GraphNode SchedulerPlan; //static GraphNode NextPlan; private static DateTime changePlanTime; // static Mutex SchedPlanMutex; public static GraphNode CurrentPlanNode; private static RialtoActivity circularActivityList; private static RialtoActivity robinActivityNext; private static RialtoActivity robinActivityCurrent; // activity currently executing from //static TimeSpan CurrentSlice; private static TimeSpan idleTime; private static bool idle; private static RialtoThread currentThreadGlobal; public static bool EarliestDeadlineFirstBlocked; private static RialtoActivity standbyActivities; // head of the Standby Activities list private static RialtoActivity currentStandbyActivity; // Standby Activity executing, if any private static TimeSpan robinSliceLeft = RialtoScheduler.RobinSlice; //new TimeSpan(0); // the round robin list, if any, and its CPU slice public static DateTime SchedulingTime; // moment of the last reschedules private static bool needToReschedule; private static RialtoThread directSwitchTo; private static TimeSpan freeCpu; // (freeCpu/AmountCpu) * 100. == % of CPU free //static NodeProxy[] nodeProxies; private static ArrayList nodeProxies = new ArrayList(); //static int ProxyCount; //static int MaxProxies; private static TimeSpan[][] freeSlots; // root of helper data structure //static int FreeLevels; // levels in the helper data structures public static TimeSpan LA_Time = RialtoScheduler.MinSlice; private static TimeSpan minPeriod; // minimum period in the system public static GraphNode FreeNodes; // list of free nodes in current scheduling tree private static GraphNode tempFreeNodes; // list of free nodes in new scheduling tree public static int ResolutionAttempt; private static int hackTries; #endregion static public void RegisterScheduler() { CpuResource.RegisterSystemScheduler(new RialtoScheduler()); } public override ISchedulerProcessor CreateSchedulerProcessor(Processor processor) { return new RialtoProcessor(processor); } public override ISchedulerThread CreateSchedulerThread(Thread thread) { return new RialtoThread(thread); } public override ISchedulerActivity CreateSchedulerActivity() { return new RialtoActivity(); } public override ISchedulerCpuReservation ReserveCpu(ISchedulerActivity schedulerActivity, CpuResourceAmount cpuAmount, TimeSpan period) { RialtoActivity activity = (RialtoActivity) schedulerActivity; if (activity.MyRecurringCpuReservation == null) { activity.MyRecurringCpuReservation = new RecurringReservation(); activity.MyRecurringCpuReservation.Slice = new TimeSpan(0); activity.MyRecurringCpuReservation.Period = CpuResource.MaxPeriod; } //Activity activity = GetActivity(activityId); TimeSpan oldSlice; TimeSpan oldPeriod; TimeSpan amount = CpuResource.Provider().CpuToTime(cpuAmount); GraphNode newSchedulerPlan; GraphNode oldSchedulerPlan; TimeSpan deltaReservation; DateTime timeNow; #if DEBUG_TREE TimeSpan requestedSlice = amount; TimeSpan requestedPeriod = period; #endif bool flagContext = false; bool localNeedToReschedule; // bool InterruptsDisableFlag; ulong start; ulong stop; // if (InThreadContext()) { // Mutex_Lock(&SchedPlanMutex); // flagContext = true; // } // else { // Debug.Assert(Processor.InterruptsDisabled()); // } Scheduler.LogRecurringCpuReservation(); // record fixed cost // hack!!!! hackTries = 0; start = Processor.CycleCount; localNeedToReschedule = false; oldSlice = activity.MyRecurringCpuReservation.Slice; oldPeriod = activity.MyRecurringCpuReservation.Period; Debug.Assert(oldPeriod.Ticks != 0); if (period.Ticks == 0) { Debug.Assert(amount.Ticks == 0); period = oldPeriod; } else if (amount.Ticks != 0) { if (period > CpuResource.MaxPeriod) { amount = new TimeSpan((amount.Ticks * CpuResource.MaxPeriod.Ticks)/ period.Ticks); period = CpuResource.MaxPeriod; } if (amount < RialtoScheduler.MinSlice) { amount = RialtoScheduler.MinSlice; // DebugStub.Print("ReserveRecurringCpu:: S_FALSE amount < RialtoScheduler.MinSlice.\n"); goto Exit; } } else if (oldSlice.Ticks == 0) { // case slice == 0 // DebugStub.Print("ReserveRecurringCpu:: S_OK amount == 0.\n"); goto Exit; } deltaReservation = new TimeSpan((amount.Ticks * AmountCpu.Ticks) / period.Ticks - (oldSlice.Ticks * AmountCpu.Ticks)/oldPeriod.Ticks); // Handle a simple case first. if (deltaReservation > freeCpu) { // not enough free CPU // slice = (TIME)((freeCpu + (oldSlice * AmountCpu)/oldPeriod) * period); amount = new TimeSpan((((freeCpu.Ticks + (oldSlice.Ticks * AmountCpu.Ticks)/oldPeriod.Ticks) * period.Ticks)/ AmountCpu.Ticks)); if (amount < RialtoScheduler.MinSlice) amount = new TimeSpan(0); // DebugStub.Print("ReserveRecurringCpu:: S_FALSE deltaReservation > freeCpu.\n"); goto Exit; // return old reservation } if (IncrementalReserveActivity(activity, ref amount, ref period)) { #if DEBUG_TREE DebugStub.Print("IncReservActivity: 0x{0:x} Req: {1}/{2} Get: " + "{3}/{4} Activ {5}:{6}\n", __arglist( activity, requestedSlice, requestedPeriod, amount, period, activity.MyRecurringCpuReservation.Slice, activity.MyRecurringCpuReservation.Period)); timeNow = SystemClock.GetKernelTime(); PrintSchedPlan(SchedulerPlan, timeNow); #endif GetActivityRecurringCpuReservation(FreeNodes, out oldPeriod, out oldSlice); freeCpu = new TimeSpan((oldSlice.Ticks * AmountCpu.Ticks) / oldPeriod.Ticks); #if VERBOSE DebugStub.Print("ReserveRecurringCpu:: S_OK Via Incremental Reservation.\n"); #endif goto Exit; } activity.MyRecurringCpuReservation.Slice = amount; activity.MyRecurringCpuReservation.Period = period; newSchedulerPlan = BuildSchedulerPlan(); // Scheduler.LogReservedActivity(ProxyCount); if (newSchedulerPlan != null) { // if successful bool iflag = Processor.DisableInterrupts(); timeNow = SystemClock.GetKernelTime(); if (!OneShotReservation.SatisfyAcceptedConstraint(timeNow)) { Processor.RestoreInterrupts(iflag); FreeSchedulerPlan(newSchedulerPlan); activity.MyRecurringCpuReservation.Slice = oldSlice; activity.MyRecurringCpuReservation.Period = oldPeriod; amount = new TimeSpan((period.Ticks * freeCpu.Ticks)/AmountCpu.Ticks); #if VERBOSE DebugStub.Print("ReserveRecurringCpu:: S_FALSE SatisfyAcceptedConstraint false.\n"); #endif goto Exit; } // TODO: compute time change // set a timer interrupt, if needed, for the moment when the plan will // change oldSchedulerPlan = SynchronizeSchedulerPlans(newSchedulerPlan, timeNow, ref localNeedToReschedule); Processor.RestoreInterrupts(iflag); if (oldSchedulerPlan != null) { // this may execute with preemption enabled FreeSchedulerPlan(oldSchedulerPlan); } GetActivityRecurringCpuReservation(activity.MyRecurringCpuReservation.AssignedNodes, out period, out amount); activity.MyRecurringCpuReservation.Slice = amount; activity.MyRecurringCpuReservation.Period = period; #if DEBUG_TREE DebugStub.Print("ReservActivity: 0x{0:x} Req: {1}/{2} Get {3}/{4} --- {5}:{6}\n", __arglist(activity, requestedSlice, requestedPeriod, amount, period, activity.MyRecurringCpuReservation.Slice, activity.MyRecurringCpuReservation.Period)); PrintSchedPlan(newSchedulerPlan, timeNow); #endif GetActivityRecurringCpuReservation(FreeNodes, out oldPeriod, out oldSlice); freeCpu = new TimeSpan((oldSlice.Ticks * AmountCpu.Ticks) / oldPeriod.Ticks); } else { // otherwise return old reservation (possible null) Debug.Assert(oldPeriod.Ticks != 0); activity.MyRecurringCpuReservation.Slice = oldSlice; activity.MyRecurringCpuReservation.Period = oldPeriod; amount = new TimeSpan((period.Ticks * freeCpu.Ticks)/AmountCpu.Ticks); minPeriod = SchedulerPlan.Period; #if VERBOSE DebugStub.Print("ReserveRecurringCpu:: S_FALSE new scheduler plan null.\n"); #endif } #if VERBOSE DebugStub.Print("ReserveRecurringCpu:: S_OK new scheduler plan.\n"); #endif Exit: if (flagContext) { // Debug.Assert(InThreadContext()); // if (localNeedToReschedule && !DuringBootstrap()) { // RescheduleWithInterruptsEnabled(); // } // localNeedToReschedule = false; // Mutex_Unlock(&SchedPlanMutex); } else { if (localNeedToReschedule) { localNeedToReschedule = false; Reschedule(); } } stop = Processor.CycleCount; #if PRINT_RESERV_OVERHEAD DebugStub.Print("ReservActivity internal timing: {0} cycles, " + "Free CPU {0} " + "hackTries {0}\n", __arglist(stop-start, freeCpu, hackTries)); #endif if (activity.MyRecurringCpuReservation.Slice.Ticks == 0) { activity.MyRecurringCpuReservation = null; } return activity.MyRecurringCpuReservation; } public override bool ShouldReschedule() { RialtoThread 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; RialtoThread 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(false); nextThread = null; } if (nextThread != null) { unchecked { Tracing.Log(Tracing.Debug, "NextThread => {0}", (UIntPtr)(uint)nextThread.GetThreadId()); } } else { Tracing.Log(Tracing.Debug, "NextThread => none"); } needToReschedule = false; directSwitchTo = null; // DebugStub.Print("-------------------------currentThread-Id {0}\n", // __arglist(currentThread.Id)); //return currentThread; Processor.RestoreInterrupts(iflag); return halted; } ////////////////////////////////////////////////////////////////////// //public static void Yield(); public RialtoScheduler() { } public override void Initialize() { RialtoScheduler.InitializeScheduler(); } /// /// Initialize types that would otherwise cause dynamic allocations at runtime with preemption disabled /// static void InitializeSchedulerTypes() { Kernel.InitType(typeof(OneShotReservation)); } public static void InitializeScheduler() { InitializeSchedulerTypes(); //TODO: Should all the null's be added in here? SchedulerPlan = GraphNode.InitTree(); SchedulerPlan.NextExec = SystemClock.GetKernelTime(); //perhaps this should happen when the first thread is created, instead CurrentPlanNode = SchedulerPlan; FreeNodes = SchedulerPlan; SchedulingTime = SystemClock.GetKernelTime(); freeCpu = AmountCpu; minPeriod = CpuResource.MaxPeriod; TimeSpan currentSliceLeft = SchedulerPlan.Slice; currentSliceLeft /* CurrentSlice */ = minInterval(currentSliceLeft , robinSliceLeft); bool iflag = Processor.DisableInterrupts(); SchedulerClock.SetNextInterrupt(SchedulingTime + currentSliceLeft); Processor.RestoreInterrupts(iflag); } #region ISystemScheduler Members public override void BeginDelayedConstraint(Hashtable resourceEstimates, TimeSpan relativeDeadline, ISchedulerTask taskToEnd, out ISchedulerTask schedulerTask) { TimeConstraint timeConstraint = new TimeConstraint(); timeConstraint.Estimate = CpuResource.Provider().CpuToTime((CpuResourceAmount)resourceEstimates[CpuResource.Provider().ResourceString]); timeConstraint.Start = new DateTime(0); //Start now. timeConstraint.RelativeDeadline = relativeDeadline; timeConstraint.Deadline = new DateTime(0); //A 0-deadline means relative instead. schedulerTask = Thread.CurrentThread.SchedulerThread.PrepareDelayedTask(taskToEnd, ref timeConstraint, SystemClock.GetKernelTime()); } //I'm afraid the preemption needs to be disabled here around beginBeforeWait, // but I feel like there was a problem. public override bool BeginConstraint(Hashtable resourceEstimates, DateTime deadline, ISchedulerTask taskToEnd, out ISchedulerTask schedulerTask) { DateTime timeNow = SystemClock.GetKernelTime(); RialtoThread thread = GetCurrentThread(); ulong start; ulong stop; Debug.Assert(!Processor.InterruptsDisabled()); Debug.Assert(taskToEnd == null || taskToEnd == thread.ReservationStack); bool endPrevious = (taskToEnd != null); thread.IpcCheckFreeConstraint(); start = Processor.CycleCount; TimeConstraint constraint = new TimeConstraint(); constraint.Deadline = deadline; constraint.Estimate = CpuResource.Provider().CpuToTime((CpuResourceAmount)resourceEstimates[CpuResource.Provider().ResourceString]); constraint.Start = timeNow; bool ok = thread.BeginConstraintBeforeWaitValidate(endPrevious, ref constraint, timeNow); schedulerTask = thread.PendingReservation; if (ok) { OneShotReservation.BeginConstraintBeforeWait(thread, endPrevious, constraint, timeNow); bool iflag = Processor.DisableInterrupts(); ok = OneShotReservation.ResolveConstraint(thread); Processor.RestoreInterrupts(iflag); } else { // DebugStub.Print("RialtoScheduler::BeginConstraintBeforeWaitValidate failed\n"); } stop = Processor.CycleCount; Scheduler.LogBeginConstraint(thread.EnclosingThread, ok, start, stop); //TODO: Check if this is a necessity, or if it's only for sim time. Call in wrapper if necessary. //NextThread(); return ok; } public override bool EndConstraint(ISchedulerTask taskToEnd) { Debug.Assert(taskToEnd == GetCurrentThread().ReservationStack); Debug.Assert(!Processor.InterruptsDisabled()); bool iflag = Processor.DisableInterrupts(); bool ok = OneShotReservation.EndPreviousConstraint(GetCurrentThread(), SystemClock.GetKernelTime()); // // need to reschedule only if on a reserved slot and the top of EarliestDeadlineFirst doesn't // belong to this task // if ((OneShotReservation.CurrentReservation != null) && (OneShotReservation.GuaranteedReservations != null) && (OneShotReservation.GuaranteedReservations.ReservTask != (GetCurrentThread()))) { Reschedule(); } Processor.RestoreInterrupts(iflag); Scheduler.LogEndConstraint(); //TODO: In the system wrapper for BeginConstraint -- it needs to call Yield/NextThread/MaybeYield //NextThread(); return ok; } #endregion #region Reserve Activity (x out of y) support functions public static void FreeCurrentSchedulerPlan() { FreeSchedulerPlan(SchedulerPlan); } static void FreeSchedulerPlan(GraphNode oldPlan) { GraphNode leftNode, rightNode; int i; // Debug.Assert(!Processor.InterruptsDisabled()); minPeriod = SchedulerPlan.Period; if (oldPlan == null) return; if ((oldPlan.Type == GraphNode.NodeType.Free) || (oldPlan.Type == GraphNode.NodeType.Used)) { leftNode = oldPlan.Next; // reuse local FreeSchedulerPlan(leftNode); } else { leftNode = oldPlan.Left; rightNode = oldPlan.Right; FreeSchedulerPlan(leftNode); FreeSchedulerPlan(rightNode); } for (i = 0; i < oldPlan.ReservCount; i++) { // DebugStub.Print("FreeSchedulerPlan {0} {1}\n", // __arglist(POINTER(oldPlan->ReservationArray[i]), // POINTER(oldPlan->ReservationArray[i])->ReferenceCount)); ReleaseReservation(oldPlan.ReservationArray[i].AssociatedReservation); } if (oldPlan.Type == GraphNode.NodeType.Used) { Interlocked.Decrement(ref oldPlan.DefaultActivity.CountNodes); ActivityObjRelease(oldPlan.DefaultActivity); } oldPlan = null; //free(oldPlan); } static int NextProxyOnLevel(int nextProxy, int level) { for (int i = nextProxy; i < nodeProxies.Count; i++) { if (((NodeProxy)nodeProxies[i]).TreeLevel == level) { return i; } } return nodeProxies.Count; } /// /// Search for the existence of any node with a higher TreeLevel "under" me. /// /// TreeLevel (basically tree position, 2^level + place in row) /// static bool NextProxyOnHigherLevel(int level) { // search for a node w/ TreeLevel in a 'cone' under Level for (int i = 0; i < nodeProxies.Count; i++) { int min = (level << 1) + 1; int max = (level << 1) + 2; while (((NodeProxy)nodeProxies[i]).TreeLevel >= min) { if (((NodeProxy)nodeProxies[i]).TreeLevel <= max) { return true; } min = (min << 1) + 1; max = (max << 1) + 2; } } return false; } static void ActivityAddNode(RialtoActivity activity) { Interlocked.Increment(ref activity.CountNodes); ActivityObjAddRef(activity); } static void ActivityRemoveNode(RialtoActivity activity) { int newrefcnt; Debug.Assert(Processor.InterruptsDisabled()); ActivityReleaseProtected(activity); newrefcnt = Interlocked.Decrement(ref activity.CountNodes); Debug.Assert(newrefcnt >= 0); } // CK: Recursive function to build the GraphNode tree from the GraphNode Proxies. // NOTE: Level refers to tree position in full binary tree -- not row. static GraphNode BuildLevel(int Level, TimeSpan Period, TimeSpan timeLeft, int NextProxy) { int i; GraphNode ptemp; if (timeLeft < RialtoScheduler.MinSlice) return null; ptemp = new GraphNode(); //(PNODE)malloc(sizeof (GraphNode)); //memset(ptemp, 0, sizeof(GraphNode)); ptemp.TimeToOrigin = timeLeft; Debug.Assert(Period.Ticks % minPeriod.Ticks == 0); ptemp.Period = Period; if ((i = NextProxyOnLevel(NextProxy, Level)) < nodeProxies.Count) { // allocate an Used GraphNode ptemp.Type = GraphNode.NodeType.Used; ptemp.Slice = ((NodeProxy)nodeProxies[i]).Slice; Debug.Assert(ptemp.Slice >= RialtoScheduler.MinSlice); Debug.Assert(Period == ((NodeProxy)nodeProxies[i]).Period); ptemp.DefaultActivity = ((NodeProxy)nodeProxies[i]).AssociatedActivity; ActivityAddNode(ptemp.DefaultActivity); ptemp.SameActivityNext = ptemp.DefaultActivity.MyRecurringCpuReservation.TempAssignedNodes; ptemp.DefaultActivity.MyRecurringCpuReservation.TempAssignedNodes = ptemp; ptemp.Left = ptemp.Right = null; ptemp.Next = BuildLevel(Level, Period, timeLeft - ptemp.Slice - RialtoScheduler.ContextSwitch, i + 1); } else if (NextProxyOnHigherLevel(Level)) { ptemp.Type = GraphNode.NodeType.LeftBranch; ptemp.Slice = timeLeft; ptemp.Next = null; ptemp.Left = BuildLevel(2 * Level + 1, Period+Period, timeLeft, 0); ptemp.Right = BuildLevel(2 * Level + 2, Period+Period, timeLeft, 0); } else { // free node ptemp.Type = GraphNode.NodeType.Free; ptemp.Slice = timeLeft; ptemp.Next = null; ptemp.SameActivityNext = tempFreeNodes; tempFreeNodes = ptemp; } return ptemp; } static void FreeHelperDataStructures() { // Free the (FreeLevels) freeSlots Arrays! if (freeSlots != null) { for (int i = 0; i < freeSlots.Length; i++) { freeSlots[i] = null; //free(freeSlots[i]); } if (freeSlots != null) { freeSlots = null; //free(freeSlots); } } nodeProxies = null; //free(nodeProxies); // free the ordered array of reservations } static bool BuildHelperDataStructures() { int count, i; RialtoActivity ptemp; TimeSpan T; // Allocate 'nodeProxies', 'ordered array' of node proxies: nodeProxies = new ArrayList(); //NodeProxy[MaxProxies]; //(NodeProxy*) malloc(MaxProxies * sizeof(NodeProxy)); //for (i=0; i ((NodeProxy)nodeProxies[i]).Slice))) break; # else #if ASCEND_RESERV //Sort the NodeProxy array in descending order of fraction of CPU //NOTE: The multiplication in numerator and denominator is to allow this to be done as integer math. //This is testing if (ptemp.MyRecurringCpuReservation.Slice.Ticks) / ptemp.MyRecurringCpuReservation.Period.Ticks > (((NodeProxy)nodeProxies[i]).Slice.Ticks) / ((NodeProxy)nodeProxies[i]).Period.Ticks //Converting to multiply only, for integer math. if ((ptemp.MyRecurringCpuReservation.Slice.Ticks * ((NodeProxy)nodeProxies[i]).Period.Ticks) > (((NodeProxy)nodeProxies[i]).Slice.Ticks * ptemp.MyRecurringCpuReservation.Period.Ticks)) break; // if ((ptemp.MyRecurringCpuReservation.Slice.Ticks * AmountCpu.Ticks * 100) / ptemp.MyRecurringCpuReservation.Period.Ticks > //TODO: Is this enough for accurate? Or just "close enough"? // (((NodeProxy)nodeProxies[i]).Slice.Ticks * AmountCpu.Ticks * 100) / ((NodeProxy)nodeProxies[i]).Period.Ticks) // break; #else break; //TODO: Does this mean anything? #endif # endif // make room for the new activity //if (i < ProxyCount) //Array.Copy(nodeProxies, i, nodeProxies, i+1, ProxyCount - i); //memmove(&(nodeProxies[i+1]), &(nodeProxies[i]), // sizeof(NodeProxy) * (ProxyCount - i)); nodeProxies.Insert(i, new NodeProxy()); //ProxyCount++; //Debug.Assert(nodeProxies.Count <= MaxProxies); ((NodeProxy)nodeProxies[i]).AssociatedActivity = ptemp; ((NodeProxy)nodeProxies[i]).Slice = ptemp.MyRecurringCpuReservation.Slice; ((NodeProxy)nodeProxies[i]).Period = ptemp.MyRecurringCpuReservation.Period; ((NodeProxy)nodeProxies[i]).TreeLevel = -1; if (((NodeProxy)nodeProxies[i]).Period < minPeriod) minPeriod = ((NodeProxy)nodeProxies[i]).Period; } ptemp = ptemp.Next; } while (ptemp != circularActivityList); Debug.Assert(minPeriod <= CpuResource.MaxPeriod); int freeLevels = 0; freeSlots = null; for (i = 0; i < nodeProxies.Count; i++) { T = minPeriod; count = 0; while ((((NodeProxy)nodeProxies[i]).Period >= T+T) && (T+T <= CpuResource.MaxPeriod)) { T = T+T; count++; } if (count > freeLevels) { //NOTE: Basically FreeLevels is the max height of the tree (free list) freeLevels = count; } //Store the slice as the scaled slice (fraction of the period). ((NodeProxy)nodeProxies[i]).Slice = new TimeSpan((((NodeProxy)nodeProxies[i]).Slice.Ticks * T.Ticks)/((NodeProxy)nodeProxies[i]).Period.Ticks); if (((NodeProxy)nodeProxies[i]).Slice < RialtoScheduler.MinSlice) { //free(nodeProxies); return false; } Debug.Assert(T.Ticks % minPeriod.Ticks == 0); ((NodeProxy)nodeProxies[i]).Period = T; ((NodeProxy)nodeProxies[i]).FreeLevel = count; //CK NOTE: This is essentially the level in the tree this reservation preferably goes. } // Allocate level 0 activities: for (i = 0, T = minPeriod, count = 0; i < nodeProxies.Count; i++) { if (((NodeProxy)nodeProxies[i]).Period == minPeriod) { ((NodeProxy)nodeProxies[i]).TreeLevel = 0; T -= RialtoScheduler.ContextSwitch + ((NodeProxy)nodeProxies[i]).Slice; } else { Debug.Assert(((NodeProxy)nodeProxies[i]).Period.Ticks % minPeriod.Ticks == 0); count++; } } if ((count > 0) && (T < RialtoScheduler.MinSlice)) { //CK- There exist non-level-0 activities, but not enough schedule time Left for more scheduled activities // free(nodeProxies); return false; } // Allocate the (FreeLevels) freeSlots Arrays! freeLevels +=1; freeSlots = new TimeSpan[freeLevels][]; //(FREESLOT**) malloc(FreeLevels * sizeof(FREESLOT *)); for (i = 0; i < freeSlots.Length; i++) { freeSlots[i] = new TimeSpan[1 << i]; //(FREESLOT*) malloc((1 << i) * sizeof(FREESLOT)); } for (i = 0; i < freeSlots.Length; i++) { for (count = 0; count < (1 << i); count++) { freeSlots[i][count] = T; //CK- Initialize all free slots to have T (time left after level-0 activities) } } return true; } // // Returns the first index of a NodeProxy which has TreeLevel == -1 // static int NextProxy(int ProxyIndex) { while ((ProxyIndex < nodeProxies.Count) && (((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel != -1)) { ProxyIndex++; } return ProxyIndex; } // // Returns NextProxy(0) // static int FirstProxy() { return NextProxy(0); } /// /// Updates freeSlots by subtracting the slice from all appropriate descendants and parents. /// /// /// /// static void SubtractSlice(int AllocLevel, int AllocPosition, TimeSpan Slice) { int Level, Position, Sibling, ParentPosition, i; freeSlots[AllocLevel][AllocPosition] -= Slice; Debug.Assert(freeSlots[AllocLevel][AllocPosition].Ticks >= 0); // update Free Slots; the larger periods first: // CK- At all points in time, freeSlots seems to keep the time available at the slot, to adding to a place in the tree requires reducing all descendants for (i = 1; AllocLevel + i < freeSlots.Length; i++) { for (Position = AllocPosition * (1 << i); Position < (AllocPosition + 1) * (1 << i); Position++) { Debug.Assert(freeSlots[AllocLevel + i][Position] >= Slice); freeSlots[AllocLevel + i][Position] -= Slice; } } // smaller periods Next: // CK- Update parent(s) if we now have less than our sibling. (parent is min of me and sibling). for (Level = AllocLevel, Position = AllocPosition; Level > 0; Level--, Position = ParentPosition) { Sibling = (Position & 0x01)==1 ? Position - 1 : Position + 1; if (freeSlots[Level][Position] >= freeSlots[Level][Sibling]) { break; // no need to go further } ParentPosition = Position >> 1; freeSlots[Level - 1][ParentPosition] = freeSlots[Level][Position]; } } /// /// Updates freeSlots by adding the slice to all appropriate descendants and parents. /// /// /// /// static void AddSlice(int AllocLevel, int AllocPosition, TimeSpan Slice) { int Level, Position, Sibling, ParentPosition, i; freeSlots[AllocLevel][AllocPosition] += Slice; //Update Free Slots; the larger periods first: for (i = 1; AllocLevel + i < freeSlots.Length; i++) { for (Position = AllocPosition * (1 << i); Position < (AllocPosition + 1) * (1 << i); Position++) { freeSlots[AllocLevel + i][Position] += Slice; } } // smaller periods Next: for (Level = AllocLevel, Position = AllocPosition; Level > 0; Level--, Position = ParentPosition) { Sibling = (Position & 0x01)==1 ? Position - 1 : Position + 1; ParentPosition = Position >> 1; if (freeSlots[Level][Position] <= freeSlots[Level][Sibling]) { freeSlots[Level - 1][ParentPosition] = freeSlots[Level][Position]; } else { freeSlots[Level - 1][ParentPosition] = freeSlots[Level][Sibling]; } } } // Temporarily, it disrupts the order of the 'NodeProxy' array static void SplitSlice(int ProxyIndex, TimeSpan AllocSlice) { Debug.Assert(AllocSlice >= RialtoScheduler.MinSlice); // if (nodeProxies.Count == MaxProxies) { // MaxProxies += GraphNode.MaxProxySplits; // //ArrayList already takes care of the allocation // //nodeProxies = (NodeProxy*) realloc(nodeProxies, MaxProxies * sizeof(NodeProxy)); // } //memmove(&nodeProxies[ProxyIndex + 1], &nodeProxies[ProxyIndex], // sizeof(NodeProxy) * (ProxyCount - ProxyIndex)); //ProxyCount++; nodeProxies.Insert(ProxyIndex+1, ((NodeProxy)nodeProxies[ProxyIndex]).Clone()); //Debug.Assert(nodeProxies.Count <= MaxProxies); ((NodeProxy)nodeProxies[ProxyIndex + 1]).Slice = ((NodeProxy)nodeProxies[ProxyIndex]).Slice - AllocSlice; ((NodeProxy)nodeProxies[ProxyIndex]).Slice = AllocSlice; } // Expects the split to be performed by 'SplitSlice' above. static void RestoreSlice(int ProxyIndex) { Debug.Assert(((NodeProxy)nodeProxies[ProxyIndex + 1]).AssociatedActivity == ((NodeProxy)nodeProxies[ProxyIndex]).AssociatedActivity); ((NodeProxy)nodeProxies[ProxyIndex]).Slice += ((NodeProxy)nodeProxies[ProxyIndex + 1]).Slice; nodeProxies.RemoveAt(ProxyIndex+1); } /// /// Next largest less than previous max. /// /// /// /// /// static int NextLargestFreeSlot(TimeSpan PrevMax, int PrevIndex, int Level) { TimeSpan[] LevelSlots; int position, MaxPosition = (1 << Level); TimeSpan MaxValue = RialtoScheduler.MinSlice; LevelSlots = freeSlots[Level]; for (position = 0; position < LevelSlots.Length; position++) { if ((LevelSlots[position] < PrevMax) || ((LevelSlots[position] == PrevMax) && (position > PrevIndex))) { if (LevelSlots[position] > MaxValue) { MaxPosition = position; MaxValue = LevelSlots[position]; } } } return MaxPosition; } /// /// Assigns Activities (w/ non-zero reservations) to their Scheduling Tree Branches/SubTrees /// Recursive. /// /// /// static bool AssignBranch(int ProxyIndex) { int level, position; TimeSpan slice, FirstHalf; TimeSpan[] LevelSlots; if (hackTries++ > GraphNode.MaxMarcelTries) { return false; } if (ProxyIndex >= nodeProxies.Count) { return true; } level = ((NodeProxy)nodeProxies[ProxyIndex]).FreeLevel; Debug.Assert(level >= 0); LevelSlots = freeSlots[level]; slice = ((NodeProxy)nodeProxies[ProxyIndex]).Slice; if (slice < RialtoScheduler.MinSlice) { return false; } // Search for a fit that will leave enough free time in slot: for (position = NextLargestFreeSlot(TimeSpan.MaxValue, 0, level); position < LevelSlots.Length; position = NextLargestFreeSlot(LevelSlots[position], position, level)) { if (LevelSlots[position] - slice - RialtoScheduler.ContextSwitch >= RialtoScheduler.MinSlice) { // try allocation SubtractSlice(level, position, slice + RialtoScheduler.ContextSwitch); //Note: TreeLevel is a composite number. Its value defines both the freelevel and the //position within that level. It's of magnitude 1 << level, guaranteeing that //position is less than 1 << level, so you can tell the level by the magnitude, and the //position by looking at the remainder, so to speak. ((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel = (1 << level) - 1 + position; if (AssignBranch(NextProxy(ProxyIndex))) { // try allocate Next activity return true; // look for A solution, not for the BEST one } ((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel = -1; AddSlice(level, position, slice + RialtoScheduler.ContextSwitch); // restore the freeSlots[] values } else { //Since the slot is only getting smaller, no need to continue looking. break; } } // Search for a 'perfect fit': for (position = 0; position < LevelSlots.Length; position++) { if ((slice <= LevelSlots[position]) && // try allocation (LevelSlots[position] - slice < (RialtoScheduler.AFewSlice))) { ((NodeProxy)nodeProxies[ProxyIndex]).Slice = LevelSlots[position]; SubtractSlice(level, position, LevelSlots[position]); //Note: TreeLevel is a composite number. Its value defines both the freelevel and the //position within that level. It's of magnitude 1 << level, guaranteeing that //position is less than 1 << level, so you can tell the level by the magnitude, and the //position by looking at the remainder, so to speak. ((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel = (1 << level) - 1 + position; // assign branch if (AssignBranch(NextProxy(ProxyIndex))) { // try allocate Next activity return true; // return if successful (look for A solution, not for the BEST one) } ((NodeProxy)nodeProxies[ProxyIndex]).Slice = slice; ((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel = -1; AddSlice(level, position, LevelSlots[position]); // restore the freeSlots[] values } } // We'll have to split the slice: for (position = NextLargestFreeSlot(TimeSpan.MaxValue, 0, level); position < LevelSlots.Length; position = NextLargestFreeSlot(LevelSlots[position], position, level)) { FirstHalf = minInterval(LevelSlots[position] - RialtoScheduler.MinSlice - RialtoScheduler.ContextSwitch, slice - RialtoScheduler.MinSlice - RialtoScheduler.ContextSwitch); if (FirstHalf >= RialtoScheduler.MinSlice) { Debug.Assert(LevelSlots[position] >= RialtoScheduler.MinSlice + FirstHalf); SplitSlice(ProxyIndex, FirstHalf); SubtractSlice(level, position, FirstHalf + RialtoScheduler.ContextSwitch); //Note: TreeLevel is a composite number. Its value defines both the freelevel and the //position within that level. It's of magnitude 1 << level, guaranteeing that //position is less than 1 << level, so you can tell the level by the magnitude, and the //position by looking at the remainder, so to speak. ((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel = (1 << level) - 1 + position; if (AssignBranch(NextProxy(ProxyIndex))) { return true; // look for A solution, not for the BEST one } ((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel = -1; AddSlice(level, position, FirstHalf + RialtoScheduler.ContextSwitch); // restore Previous freeSlots[] RestoreSlice(ProxyIndex); } else { //No point in continuing, the slots only get smaller. break; } } #if NOT_DEFINED if (slice > (RialtoScheduler.MinSlice << 1)) { FirstHalf = slice >> 1; for (position = 0; position < LevelSlots.Length; position++) { if (LevelSlots[position] - FirstHalf - RialtoScheduler.ContextSwitch >= RialtoScheduler.MinSlice) { // try allocation w/ split Debug.Assert(LevelSlots[position] >= RialtoScheduler.MinSlice); SplitSlice(ProxyIndex, FirstHalf); SubtractSlice(level, position, FirstHalf + RialtoScheduler.ContextSwitch); nodeProxies[ProxyIndex].TreeLevel = (1 << level) - 1 + position; if (AssignBranch(NextProxy(ProxyIndex))) { return true; // look for A solution, not for the BEST one } nodeProxies[ProxyIndex].TreeLevel = -1; AddSlice(level, position, FirstHalf + RialtoScheduler.ContextSwitch); // restore Previous freeSlots[] RestoreSlice(ProxyIndex); } } } #endif // Sorry.... return false; } static GraphNode BuildSchedulerPlan() { GraphNode newPlan = null; if (circularActivityList == null) { // build a 'large' free node newPlan = GraphNode.InitTree(); } else { if (BuildHelperDataStructures()) { // build the sorted nodeProxies and freeSlots arrays Scheduler.LogReservedActivity(nodeProxies.Count); // record additional cost if (AssignBranch(FirstProxy())) { newPlan = BuildLevel(0, // Level minPeriod,// Period minPeriod,// timeLeft 0); // NextProxy } } } FreeHelperDataStructures(); return newPlan; } // TODO: (OLD) look only to the list of nodes of the activity static TimeSpan ReservedPeriod(GraphNode plan, RialtoActivity activity) { if (plan == null) { return new TimeSpan(0); } switch(plan.Type) { case GraphNode.NodeType.Used: if (plan.DefaultActivity == activity) { return maxInterval(plan.Period, ReservedPeriod(plan.Next, activity)); } else { return ReservedPeriod(plan.Next, activity); } case GraphNode.NodeType.Free: return ReservedPeriod(plan.Next, activity); default: // branch node return maxInterval(ReservedPeriod(plan.Left, activity), ReservedPeriod(plan.Right, activity)); } } // TODO - look only to the list of nodes of the activity static TimeSpan ReservedSlice(GraphNode plan, TimeSpan period, RialtoActivity activity) { if (plan == null) { return new TimeSpan(0); } switch(plan.Type) { case GraphNode.NodeType.Used: if (plan.DefaultActivity == activity) { return (new TimeSpan((plan.Slice.Ticks * period.Ticks) / plan.Period.Ticks) + ReservedSlice(plan.Next, period, activity)); } else { return ReservedSlice(plan.Next, period, activity); } case GraphNode.NodeType.Free: return ReservedSlice(plan.Next, period, activity); default: // branch node return ReservedSlice(plan.Left, period, activity) + ReservedSlice(plan.Right, period, activity); } } static void GetActivityRecurringCpuReservation(GraphNode startList, out TimeSpan period, out TimeSpan slice) { GraphNode node; TimeSpan tempPeriod, tempSlice; if ((node = startList) == null) { period = CpuResource.MaxPeriod; slice = new TimeSpan(0); return; } tempPeriod = new TimeSpan(0); tempSlice = new TimeSpan(0); while (node != null) { if (node.Period > tempPeriod) tempPeriod = node.Period; node = node.SameActivityNext; } Debug.Assert(tempPeriod.Ticks != 0); node = startList; while (node != null) { tempSlice += new TimeSpan((node.Slice.Ticks * tempPeriod.Ticks) / node.Period.Ticks); node = node.SameActivityNext; } period = tempPeriod; slice = tempSlice; return; } static void SetNextExec(GraphNode plan, DateTime time) { if (plan == null) { return; } plan.NextExec = time; if (plan.Type == GraphNode.NodeType.LeftBranch) { SetNextExec(plan.Left, time); SetNextExec(plan.Right, time + plan.Period); } else if (plan.Type == GraphNode.NodeType.RightBranch) { SetNextExec(plan.Left, time + plan.Period); SetNextExec(plan.Right, time); } else SetNextExec(plan.Next, time + plan.Slice + RialtoScheduler.ContextSwitch); } static GraphNode SynchronizeSchedulerPlans(GraphNode newPlan, DateTime timeChange, ref bool localNeedToReschedule) { GraphNode oldPlan; RialtoActivity activity = circularActivityList; // any pointer into the activity circular list works Debug.Assert(Processor.InterruptsDisabled()); // set the pointers to the Used and Free nodes in the new plan: FreeNodes = tempFreeNodes; tempFreeNodes = null; do { if (activity.MyRecurringCpuReservation != null && activity.MyRecurringCpuReservation.Slice.Ticks > 0) { activity.MyRecurringCpuReservation.AssignedNodes = activity.MyRecurringCpuReservation.TempAssignedNodes; activity.MyRecurringCpuReservation.TempAssignedNodes = null; } activity = activity.Next; } while (activity != circularActivityList); #if VERBOSE DebugStub.Print("SettingNextExec: {0}\n", __arglist(timeChange.Ticks)); #endif SetNextExec(newPlan, timeChange); // TODO: extend later! oldPlan = SchedulerPlan; SchedulerPlan = newPlan; CurrentPlanNode = SchedulerPlan; localNeedToReschedule = true; return oldPlan; } static void ClearActivityNodes(GraphNode startNode, RialtoActivity activity) { GraphNode temp; if (startNode == null) { return; } temp = startNode; Debug.Assert((temp.Type == GraphNode.NodeType.Used) && (temp.DefaultActivity == activity)); while (temp.SameActivityNext != null) { ActivityRemoveNode(activity); temp.Type = GraphNode.NodeType.Free; temp = temp.SameActivityNext; Debug.Assert((temp.Type == GraphNode.NodeType.Used) && (temp.DefaultActivity == activity)); } ActivityRemoveNode(activity); // for the last node in the list temp.Type = GraphNode.NodeType.Free; temp.DefaultActivity = null; // this should be non preemptible temp.SameActivityNext = FreeNodes; FreeNodes = startNode; } // attaches nodes found to TempAssignedNodes static bool AllocateActivityNodes(RialtoActivity activity, ref TimeSpan slice, ref TimeSpan period) { GraphNode node, bestNode = null; GraphNode tmpNode, tmpBest = null; TimeSpan adjustedSlice, bestPeriod = new TimeSpan(0), diffSlice; bool split = false; bool iflag = false; //So far we haven't disabled preemption. TimeSpan bestDiffSlice = new TimeSpan(0), currentPeriod; bool currentSplit; GraphNode rightNode, leftNode, next, free; for (tmpNode = null, node = FreeNodes; node != null; tmpNode = node, node = node.SameActivityNext) { //node = *tmpNode; Debug.Assert(node.Type == GraphNode.NodeType.Free); if (node.Period > period || node.Period < bestPeriod) { continue; } // the activity may be assigned a node with reservations // this increases the chances to find a node, although // with such a node, the activity will not fully benefit of the // allocation until the reservations are finished // The alternative to the current choice: // if (node.ReservCount > 0) // continue; // S97 -- if the request's period is larger than the maximum period // in the system, extend the tree with branch and activity // nodes instead of converting the reservation to the maximum // existing period // -- is a request's period is equal to some period in the system, pick // the best free node (not the first fit node) to make the // reservation from. // - node.Next != null -- can not be transformed in a branch node // apply the scaling technique // - node.Next == null if (node.Next != null || node.ReservCount != 0) { currentPeriod = node.Period; } else { currentPeriod = period; } diffSlice = new TimeSpan(-1); while (currentPeriod >= node.Period) { adjustedSlice = new TimeSpan((slice.Ticks * currentPeriod.Ticks)/ period.Ticks); if (adjustedSlice < RialtoScheduler.MinSlice) { break; } diffSlice = node.Slice - adjustedSlice - RialtoScheduler.ContextSwitch; if (diffSlice.Ticks >= 0) { break; } currentPeriod = new TimeSpan(currentPeriod.Ticks / 2); } if (diffSlice.Ticks < 0) { continue; } if (diffSlice >= RialtoScheduler.AFewSlice) { currentSplit = true; } else { currentSplit = false; } if (currentSplit && diffSlice < RialtoScheduler.MinSlice) { // the remaining slice is too long to be entirely reserved for the // current request, but is also too small to represent an // acceptable slice continue; } // assume accepted if (bestNode == null || // no previous fit currentPeriod > bestPeriod || // larger period // at equal periods... (split != currentSplit && split) || // the no split has priority (split == currentSplit && // at the same split type... // is split, larger diff is better, and otherwise, // smaller diff is better ((currentSplit && bestDiffSlice < diffSlice) || (!currentSplit && bestDiffSlice > diffSlice)))) { bestNode = node; tmpBest = tmpNode; bestPeriod = currentPeriod; bestDiffSlice = diffSlice; split = currentSplit; } } if (bestNode == null) { return false; } currentPeriod = bestNode.Period; node = null; adjustedSlice = new TimeSpan((slice.Ticks * bestPeriod.Ticks)/ period.Ticks); if (currentPeriod != bestPeriod) { // not the same period: need to extend the tree up to bestPeriod. // create the subtree, but do not modify yet the bestNode (try // to do atomically the modifications that require preemption disabled. rightNode = new GraphNode(); //(PNODE)malloc(sizeof(GraphNode)); //memset(rightNode, 0, sizeof(GraphNode)); leftNode = new GraphNode(); //(PNODE)malloc(sizeof(GraphNode)); //memset(leftNode, 0, sizeof(GraphNode)); TimeSpan halfPeriod = currentPeriod; //CK TEST // set rightNode as a free node except for SameActivityNext currentPeriod += currentPeriod; rightNode.Type = GraphNode.NodeType.Free; rightNode.Period = currentPeriod; rightNode.Slice = bestNode.Slice; rightNode.NextExec = bestNode.NextExec + halfPeriod; //CK TEST currentPeriod rightNode.TimeToOrigin = bestNode.TimeToOrigin; free = rightNode; // set leftNode as a branch node. node = leftNode; for (;;) { node.Type = GraphNode.NodeType.LeftBranch; node.Period = currentPeriod; Debug.Assert(currentPeriod.Ticks % minPeriod.Ticks == 0); node.Slice = bestNode.Slice; node.NextExec = bestNode.NextExec; node.TimeToOrigin = bestNode.TimeToOrigin; if (currentPeriod == bestPeriod) { break; } halfPeriod = currentPeriod; //CK TEST currentPeriod += currentPeriod; // set the right branch. next = new GraphNode(); // (GraphNode)malloc(sizeof(GraphNode)); //memset(Next, 0, sizeof(GraphNode)); next.Type = GraphNode.NodeType.Free; next.Period = currentPeriod; Debug.Assert(currentPeriod.Ticks % minPeriod.Ticks == 0); next.Slice = bestNode.Slice; next.NextExec = bestNode.NextExec + halfPeriod; //CK TEST currentPeriod next.TimeToOrigin = bestNode.TimeToOrigin; next.SameActivityNext = free; free = next; node.Right = next; next = new GraphNode(); // (GraphNode)malloc(sizeof(GraphNode)); //memset(Next, 0, sizeof(GraphNode)); node.Left = next; node = next; } if (split) { next = new GraphNode(); iflag = Processor.DisableInterrupts(); // insert Used node before the Free node: next.Slice = node.Slice - adjustedSlice - RialtoScheduler.ContextSwitch; node.Slice = adjustedSlice; node.Next = next; next.Type = GraphNode.NodeType.Free; next.NextExec = node.NextExec + node.Slice + RialtoScheduler.ContextSwitch; next.TimeToOrigin = node.TimeToOrigin - node.Slice - RialtoScheduler.ContextSwitch; next.SameActivityNext = free; free = next; // now set all the characteristics the new node next.Period = node.Period; Debug.Assert(node.Period.Ticks % minPeriod.Ticks == 0); Debug.Assert(next.TimeToOrigin.Ticks >= 0); } else { iflag = Processor.DisableInterrupts(); } // modify bestNode. bestNode.Type = GraphNode.NodeType.LeftBranch; bestNode.Slice = bestNode.Slice; bestNode.Left = leftNode; bestNode.Right = rightNode; if (tmpBest == null) { FreeNodes = bestNode.SameActivityNext; } else { tmpBest.SameActivityNext = bestNode.SameActivityNext; } // add the new free nodes to the global list. rightNode.SameActivityNext = FreeNodes; FreeNodes = free; } else { // allocate node at bestNode's period. if (split) { node = new GraphNode(); //(PNODE)malloc(sizeof(GraphNode)); //memset(node, 0, sizeof(GraphNode)); // insert Used node before Free node: node.Slice = bestNode.Slice - adjustedSlice - RialtoScheduler.ContextSwitch; node.NextExec = bestNode.NextExec + adjustedSlice + RialtoScheduler.ContextSwitch; node.TimeToOrigin = bestNode.TimeToOrigin - adjustedSlice - RialtoScheduler.ContextSwitch; node.Period = bestNode.Period; Debug.Assert(node.TimeToOrigin.Ticks >= 0); Debug.Assert(node.Period.Ticks % minPeriod.Ticks == 0); iflag = Processor.DisableInterrupts(); bestNode.Slice = adjustedSlice; bestNode.Next = node; if (tmpBest == null) { FreeNodes = bestNode.SameActivityNext; } else { tmpBest.SameActivityNext = bestNode.SameActivityNext; } if ((node.ReservCount = bestNode.ReservCount) > 0) { int i; node.ReservationArray = (ReservationSlice[])bestNode.ReservationArray.Clone(); //memcpy(node.ReservationArray, bestNode.ReservationArray, // sizeof(ReservationSlice) * node.ReservCount); for (i = 0; i < node.ReservCount; i++) { node.ReservationArray[i] = (ReservationSlice)node.ReservationArray[i].Clone(); AddRefReservation(node.ReservationArray[i].AssociatedReservation); } } node.SameActivityNext = FreeNodes; FreeNodes = node; } else { if (tmpBest == null) { FreeNodes = bestNode.SameActivityNext; } else { tmpBest.SameActivityNext = bestNode.SameActivityNext; } } node = bestNode; } node.Type = GraphNode.NodeType.Used; node.DefaultActivity = activity; node.SameActivityNext = activity.MyRecurringCpuReservation.TempAssignedNodes; activity.MyRecurringCpuReservation.TempAssignedNodes = node; Processor.RestoreInterrupts(iflag); ActivityAddNode(activity); slice = node.Slice; period = node.Period; Debug.Assert(period.Ticks % minPeriod.Ticks == 0); return true; } static bool IncrementalReserveActivity(RialtoActivity activity, ref TimeSpan slice, ref TimeSpan period) // preemption disabled for the entire or part of the function { // UNLESS success is returned, DON'T modify slice and period TimeSpan newSlice, newPeriod; GraphNode node; Debug.Assert(period <= CpuResource.MaxPeriod); // adjust Period and scale Slice down if (slice.Ticks == 0 && activity.MyRecurringCpuReservation.Slice.Ticks > 0) { bool iflag = Processor.DisableInterrupts(); node = activity.MyRecurringCpuReservation.AssignedNodes; activity.MyRecurringCpuReservation.AssignedNodes = null; ClearActivityNodes(node, activity); activity.MyRecurringCpuReservation.Slice = slice; activity.MyRecurringCpuReservation.Period = period; Processor.RestoreInterrupts(iflag); return true; } if (period < minPeriod) { return false; } for (newPeriod = minPeriod; newPeriod + newPeriod <= period; newPeriod += newPeriod) { // no body. } newSlice = new TimeSpan((slice.Ticks * newPeriod.Ticks)/ period.Ticks); if ((activity.MyRecurringCpuReservation.Slice.Ticks > 0) && (activity.MyRecurringCpuReservation.Period > newPeriod)) { // new period smaller than previous one if (AllocateActivityNodes(activity, ref newSlice, ref newPeriod)) { // uses TempAssignedNodes Debug.Assert((newPeriod <= period) && (newSlice.Ticks >= (slice.Ticks * newPeriod.Ticks)/ period.Ticks)); Debug.Assert(newPeriod.Ticks != 0); bool iflag = Processor.DisableInterrupts(); node = activity.MyRecurringCpuReservation.AssignedNodes; activity.MyRecurringCpuReservation.AssignedNodes = activity.MyRecurringCpuReservation.TempAssignedNodes; activity.MyRecurringCpuReservation.TempAssignedNodes = null; activity.MyRecurringCpuReservation.Slice = slice = newSlice; activity.MyRecurringCpuReservation.Period = period = newPeriod; Debug.Assert(period == newPeriod); Debug.Assert(newPeriod.Ticks % minPeriod.Ticks == 0); ClearActivityNodes(node, activity); Processor.RestoreInterrupts(iflag); return true; } else { return false; } } // compute the additional slice you need to allocate newSlice -= new TimeSpan((activity.MyRecurringCpuReservation.Slice.Ticks * newPeriod.Ticks)/activity.MyRecurringCpuReservation.Period.Ticks); Debug.Assert(newSlice.Ticks >= 0); if (newSlice < RialtoScheduler.MinSlice) { return false; } if (AllocateActivityNodes(activity, ref newSlice, ref newPeriod)) { freeCpu -= new TimeSpan((newSlice.Ticks * AmountCpu.Ticks)/newPeriod.Ticks); // transfer the new nodes from the TempAssignedNodes list to the AssignedNodes list: node = activity.MyRecurringCpuReservation.TempAssignedNodes; while (node.SameActivityNext != null) { node = node.SameActivityNext; } bool iflag = Processor.DisableInterrupts(); node.SameActivityNext = activity.MyRecurringCpuReservation.AssignedNodes; activity.MyRecurringCpuReservation.AssignedNodes = activity.MyRecurringCpuReservation.TempAssignedNodes; activity.MyRecurringCpuReservation.TempAssignedNodes = null; GetActivityRecurringCpuReservation(activity.MyRecurringCpuReservation.AssignedNodes, out newPeriod, out newSlice); activity.MyRecurringCpuReservation.Slice = slice = newSlice; activity.MyRecurringCpuReservation.Period = period = newPeriod; Processor.RestoreInterrupts(iflag); Debug.Assert(newPeriod.Ticks != 0); // *pNewSlice is the total slice, not only the additional one Debug.Assert((newPeriod <= period) && (newSlice.Ticks >= (slice.Ticks * newPeriod.Ticks)/ period.Ticks)); return true; } return false; } // Reserve Activity (x out of any y) support functions end. // ---------------------------------------------------------------------------------- #endregion // NOTE: 6/15/2004. This comment copied verbatim from the SOSP'97. As such, it may not // be fully accurate. Read for yourself. // // In the following, reservation refers to a data structure allocated // when 'BeginConstraint' is executed. // // IMPORTANT: // The OneShotReservation data structure is always allocated, whether // the constraint is found feasible or not. // The OneShotReservation data structure is freed after the // matching 'EndConstraint' call is executed AND the estimate is exhausted. // Constraints that are not found feasible are assigned a zero estimate. // // At 'BeginConstraint', the new reservation is placed on a stack associated with // the issuing thread; ReservationStack (in Thread) points to the top of the stack and // SurroundingReservation (in OneShotReservation) points to the Next reservation on the stack. // Reservations are removed from this stack at 'EndConstraint'. // The stack mirrors the 'nesting' relationship between constraints. // // In addition, new reservations are placed on a double linked list: // - feasible constraints on the 'GuaranteedReservations' list or, if Start < CurrentTime, // on the 'IdleReservations' list. // - constraints found non-feasible are placed on the 'UnfinishedConstraints' list // associated with the activity of the issuing thread; since 'GetRunnableThread' looks for // a runnable thread in the 'UnfinishedConstraints' list first, the issuing thread has // 'higher priority' than the other threads of the same activity (and the matching // 'EndConstraint' is executed earlier). // // Feasible NonCritical constraints can have their CPU reservation partially stolen by // a Critical Constraint in the same activity. // When this happens, the estimate of the victim reservation is diminished accordingly, // and if the estimate gets to zero it is transfered to the 'UnfinishedConstraints' list // // When a reservation becomes active, it is moved from the 'pIdleReservation' to the // 'GuaranteedReservations' // // Reservations WITH NO SurroundingReservations(i.e., NOT nested) // -------------------------------------------------------------- // If 'EndConstraint' is executed while the reservation is active, the status of the // reservation changes to an activity reservation, i.e. the remaining CPU is allocated // to the activity and not only to the issuing thread (see 'GetRunnableThread'). // The reservation is not removed from the 'p(Non)GuaranteedReservations' lists until its // estimate is exhausted. // // An active reservation is removed from the 'p(Non)GuaranteedReservations' lists // when (see 'Reschedule' and 'EnqueueReservation'): // - CurrentTime >= Deadline or // - Estimate <= 0; the estimate is decremented every time the active thread of the // reservation (see above and 'GetRunnableThread') is executed. If the matching // 'EndConstraint' has not been executed by the issuing thread, the reservation is // is placed 'UnfinishedConstraints' list of the issuing thread's activity. // // Reservations WITH SurroundingReservations(i.e., nested) // --------------------------------------------------------- // such a reservation may inherit time for any of the reservations on its nested stack. // When an inherit happens, the estimate of the 'giving' reservation is diminished // accordingly. // When a nested reservation is granted (i.e., estimate > 0), it will be positioned on the // idle or Guaranteed lists AND, all other surrounding reservations will be dequeued (from // which ever Guaranteed or Unfinished list they are. // The reservation is not removed from the 'GuaranteedReservations' lists until its // estimate is exhausted or at EndConstraint. // // If 'EndConstraint' is executed // while the reservation is active (i.e., estimate > 0), // the estimate is given to the immediately surrounding reservation, and this is // placed on Guaranteed list. // while the reservation has no estimate (on the Unfinished list) // it will be simply removed from the list // // When a reservation gets to estimate 0 before EndConstraint // (see 'Reschedule', StealNonCriticalCpu) // it is placed on the Unfinished list. The same happen with all the surrounding // reservations up to the first with estimate non zero which is placed on the // Guaranteed list. // #region OneShotCpuReservation related functions // OneShotCpuReservation related functions begin: public static int ReleaseReservationProtected(OneShotReservation reservation) { int newrefcnt; Debug.Assert(Processor.InterruptsDisabled()); Debug.Assert(reservation.ReferenceCount > 0); 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) { int newrefcnt; // Debug.Assert(!Processor.InterruptsDisabled()); Debug.Assert(reservation.ReferenceCount > 0); 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++; } #if DEBUG_RESERV static void PrintQueueReserv(OneShotReservation reservation, DateTime timeNow) { OneShotReservation pSave = reservation; DebugStub.Print("ReservQueue {0} {1:d}\n", __arglist((pSave == null? "null":""), timeNow)); while (reservation != null) { DebugStub.Print("R{0} T{1}:A{2} - Deadline {3:d} Estimate {4:d} Lax {5:d}", __arglist( reservation.ReservationId, (reservation.OriginalThread != null ? reservation.OriginalThread.Id : -1), (reservation.OriginalThread != null ? reservation.OriginalThread.pActivity.Id : reservation.pActivity.Id), reservation.Deadline, reservation.Estimate, reservation.Deadline - timeNow)); if ((reservation = reservation.Next) == pSave) { break; } } } #endif #endregion #region Potpourri (Unregioned functions) static GraphNode NextNode(GraphNode currentNode) { if (currentNode.Next != null) { currentNode = currentNode.Next; } else { currentNode = SchedulerPlan; } while ((currentNode.Type == GraphNode.NodeType.RightBranch) || (currentNode.Type == GraphNode.NodeType.LeftBranch)) { if (currentNode.Type == GraphNode.NodeType.RightBranch) { currentNode.Type = GraphNode.NodeType.LeftBranch; // Next time take the other branch currentNode = currentNode.Right; } else if (currentNode.Type == GraphNode.NodeType.LeftBranch) { currentNode.Type = GraphNode.NodeType.RightBranch;// Next time take the other branch currentNode = currentNode.Left; } } Debug.Assert(currentNode != null); Debug.Assert(currentNode.TimeToOrigin.Ticks >= 0); Debug.Assert(currentNode.Period.Ticks % minPeriod.Ticks == 0); return currentNode; } // returns true if there is at least one free slot left in the array static bool CleanReservationArray(GraphNode node, DateTime timeNow) { int i, j; OneShotReservation reservation; for (i = j = 0; i < node.ReservCount; i++) { reservation = node.ReservationArray[i].AssociatedReservation; if (node.ReservationArray[i].End <= timeNow + RialtoScheduler.AFewSlice || !reservation.Valid || (reservation.Estimate <= RialtoScheduler.AFewSlice && reservation.OriginalThread == null)) { // DebugStub.Print("CleanReservationArray {0} {1}\n", // __arglist(reservation, reservation.ReferenceCount)); ReleaseReservationProtected(reservation); // each slice has own reference } else { if (j < i) { node.ReservationArray[j] = (ReservationSlice)node.ReservationArray[i].Clone(); } //memcpy(&(node.ReservationArray[j]), &(node.ReservationArray[i]), sizeof(ReservationSlice)); j++; } } node.ReservCount = j; Debug.Assert(node.ReservCount <= GraphNode.MaxNumReservations); return (j < GraphNode.MaxNumReservations); } public static RialtoThread GetCurrentThread() { return currentThreadGlobal; } public static void IChangeCurrentThread(RialtoThread thread) { currentThreadGlobal = 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); } // The Queue of Standby Activities is circular and // standbyActivities points to the Next activity // to get spare CPU. By default, the Queue is FIFO. public static void EnqueueStandbyActivity(RialtoActivity activity) { Debug.Assert(Processor.InterruptsDisabled()); activity.LastNode = null; if (activity.NextStandbyActivity != null) { Debug.Assert(activity.PreviousStandbyActivity != null); return; } if (standbyActivities == null) { activity.PreviousStandbyActivity = activity.NextStandbyActivity = activity; standbyActivities = activity; return; } // else ... // insert in front of 'standbyActivities' activity.NextStandbyActivity = standbyActivities; activity.PreviousStandbyActivity = standbyActivities.PreviousStandbyActivity; standbyActivities.PreviousStandbyActivity.NextStandbyActivity = activity; standbyActivities.PreviousStandbyActivity = activity; #if LIFO standbyActivities = activity; #endif } static void DequeueStandbyActivity(RialtoActivity activity) { Debug.Assert(Processor.InterruptsDisabled()); if (activity.NextStandbyActivity == null) { // if not in Queue, a NOP Debug.Assert(activity.PreviousStandbyActivity == null); return; } if (activity.NextStandbyActivity == activity) { // last one in the Queue Debug.Assert(activity.PreviousStandbyActivity == activity); standbyActivities = null; } else { activity.PreviousStandbyActivity.NextStandbyActivity = activity.NextStandbyActivity; activity.NextStandbyActivity.PreviousStandbyActivity = activity.PreviousStandbyActivity; if (standbyActivities == activity) { // advance the Queue head standbyActivities = activity.NextStandbyActivity; } } activity.NextStandbyActivity = activity.PreviousStandbyActivity = null; } // Add Activity in the last Round-Robin position public static void EnqueueActivity(RialtoActivity activity) { Debug.Assert(Processor.InterruptsDisabled()); if (circularActivityList == null) { robinActivityNext = circularActivityList = robinActivityCurrent = activity.Next = activity.Previous = activity; } else { activity.Next = robinActivityNext; activity.Previous = robinActivityNext.Previous; robinActivityNext.Previous.Next = activity; robinActivityNext.Previous = activity; } } public static void DequeueActivity(RialtoActivity activity) //TODO: Make an activity function? { // In our simulated environment, Activity 0 is never removed from the Q Debug.Assert(Processor.InterruptsDisabled()); //NOTE: Might be replaced with a test and return. Debug.Assert(activity.Next != null); Debug.Assert(activity.Previous != null); // Previous. in rialto if (activity.Id == 0) return; DequeueStandbyActivity(activity); activity.Next.Previous = activity.Previous; activity.Previous.Next = activity.Next; if (circularActivityList == activity) { circularActivityList = activity.Next; } if (robinActivityNext == activity) { robinActivityNext = activity.Next; } } // if possible, places thread in the 2nd position in the RunnableThreads Q // Make this a function on a resource container. public static void EnqueueRunThread(RialtoThread thread) { Debug.Assert(Processor.InterruptsDisabled()); RialtoActivity activity = thread.AssociatedActivity; if (thread.Next != null) { Debug.Assert(thread.Previous != null); return; } // Debug.Assert(thread.QueueType == QUEUE_NONE); //if (thread.State == RialtoThread.ThreadState.ThreadWaiting) { // thread.SetState(RialtoThread.ThreadState.ThreadReady); //} if (activity.RunnableThreads == null) { thread.Next = thread.Previous = thread; activity.RunnableThreads = thread; } else { thread.Previous = activity.RunnableThreads; thread.Next = activity.RunnableThreads.Next; activity.RunnableThreads.Next.Previous = thread; activity.RunnableThreads.Next = thread; } } public static bool DequeueRunThread(RialtoThread thread) { Debug.Assert(Processor.InterruptsDisabled()); if (thread.Next == null) { Debug.Assert(thread.Previous == null); return false; } if (thread.Next == thread) { Debug.Assert(thread.Previous == thread); thread.AssociatedActivity.RunnableThreads = null; DequeueStandbyActivity(thread.AssociatedActivity); } else { // multiple threads in the RunnableThreads Q thread.Previous.Next = thread.Next; thread.Next.Previous = thread.Previous; if (thread.AssociatedActivity.RunnableThreads == thread) { thread.AssociatedActivity.RunnableThreads = thread.Next; } } thread.Next = thread.Previous = null; return true; } #endregion #region Additions according to kernel implementation static bool ResetTimerTimeout(DateTime NewTimerTimeout) { DateTime timeNow = SchedulingTime; if (timeNow > NewTimerTimeout) { return false; } Debug.Assert(Processor.InterruptsDisabled()); // Debug.Assert(CurrentThread()->GetState() == Thread.ThreadState.ThreadRunning); // Debug.Assert(NewTimerTimeout > SchedulingTime); //TODO: Need LA_Time? // if (NewTimerTimeout > SleepTimeout - LA_Time) { // NewTimerTimeout = SleepTimeout - LA_Time; // } if (NewTimerTimeout > RialtoThread.GetSleepTimeout()) { NewTimerTimeout = RialtoThread.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 MoveToStandby(RialtoThread thread, bool fromWakeUp) { if (thread.AssociatedActivity.LastNode == null) { if (fromWakeUp) { // a thread that wakes up always gets a (small) chance to run: // but ...IReadyThread would not do this enqueue //thread.AssociatedActivity.MyRecurringCpuReservation.SliceLeft = RialtoScheduler.MinSlice; EnqueueStandbyActivity(thread.AssociatedActivity); } } else { // sets 'pthd.pActivity.LastNode' to null. EnqueueStandbyActivity(thread.AssociatedActivity); if (CurrentPlanNode == thread.AssociatedActivity.LastNode) { //TODO: since EnqueueStandbyActivity sets it to null, isn't this a no-op? Reschedule(); } } } static void UpdateStandbyStatus(RialtoThread thread) { RialtoActivity activity = thread.AssociatedActivity; if ((activity.RunnableThreads == null) && (CurrentPlanNode.Type == GraphNode.NodeType.Used) && (CurrentPlanNode.DefaultActivity == activity) && ((activity.MyRecurringCpuReservation.SliceLeft = CurrentPlanNode.NextExec + CurrentPlanNode.Slice - SystemClock.GetKernelTime()) >= RialtoScheduler.MinSlice)) { activity.LastNode = CurrentPlanNode; // record it. } } public static void UpdateSchedulingStatus() { TimeSpan timeRun; RialtoThread currentThread = GetCurrentThread(); DateTime newSchedulingTime = SystemClock.GetKernelTime(); timeRun = newSchedulingTime - SchedulingTime; SchedulingTime = newSchedulingTime; #if false Tracing.Log(Tracing.Debug, "UpdateSchedulingStatus"); #endif 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); // In the current implementation, the scheduler has to call CurrentTask on the // known running thread, since the thread may not actually be running when // UpdateSchedulingStatus() is called. It may be the scheduler thread running. // I left things the way they are though since Scheduler is suitable to // use for any application/non-kernel code. We may need to document it there // too, or perhaps have it ask the CpuResource what the default task is at the // moment, since presumably it may be the CpuResource which actually tracks that. currentThread.EnclosingThread.CurrentTask().AddResourceAmountUsed(CpuResource.Provider().ResourceString, CpuResource.Provider().TimeToCpu(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 (currentStandbyActivity != null) { if (currentStandbyActivity.MyRecurringCpuReservation != null) { currentStandbyActivity.MyRecurringCpuReservation.SliceLeft -= timeRun; } currentStandbyActivity = null; } else if (robinActivityCurrent != null) { // slice used for RoundRobin. robinSliceLeft -= timeRun; robinActivityCurrent = null; } } // Always called when another thread needs to be scheduled. static bool RescheduleInterrupt() { Debug.Assert(Processor.InterruptsDisabled()); Debug.Assert(!Processor.InterruptsDisabled()); RialtoThread currentThread = GetCurrentThread(); DateTime nextStart; RialtoThread previousThread; RescheduleAgain: // !!!!!!!!!! SIM ONLY !!!!!!!!! if (idle) { currentThread = null; } previousThread = currentThread; EarliestDeadlineFirstBlocked = false; 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(); #if LOG_SCHEDULER_DETAILS bool logDirectSwitch = (directSwitchTo != null); long logCurrentNodeSliceLeft1 = 0; long logCurrentNodeSliceLeft2 = 0; long logCurrentNodeSliceLeft3 = 0; int logCurrentPlanNodeId = -1; int logReservCount = 0; bool logRunningDefault = false; bool logRunningStandby = false; bool logRunningRobin = false; long logCurrentNodeSliceLeft4 = 0; long logCurrentNodeSliceLeft = 0; #endif //Why not check this directly? if (directSwitchTo != null) { //Debug.Print("directSwitchTo!\n"); Debug.Assert(OneShotReservation.CurrentReservation != null); Scheduler.LogContextSwitch(); // Context Switch statistics // Debug.Assert(currentThread.QueueType == QUEUE_NONE); currentThread = directSwitchTo; directSwitchTo = null; Scheduler.LogReschedule(); // DebugStub.Print("Directed Context Switch.\n"); goto exitOK; // don't set TIMER interrupt } OneShotReservation.ClearCurrentReservation(); currentThread = null; // Finished first stage, i.e. updated state. // Start second stage: wakeup threads & select Next CPU slice. RialtoThread.WakeThreads(); // NOTE: In the original Rialto 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(); #if NOT_DEFINED if (SchedulingTime >= changePlanTime) { GraphNode ptemp = SchedulerPlan; SchedulerPlan = pNextPlan; pNextPlan = null; changePlanTime = TIME_FOREVER; Debug.Assert(false); FreeSchedulerPlan(ptemp); CurrentPlanNode = SchedulerPlan; } #endif TimeSpan currentNodeSliceLeft; // Compute currentNodeSliceLeft (possible (very) negative). Debug.Assert(CurrentPlanNode != null); //DebugStub.Print("Next Exec: {0} Slide: {1} SchedulingTime: {2}\n", //__arglist(CurrentPlanNode.NextExec.Ticks, // CurrentPlanNode.Slice.Ticks, // SchedulingTime.Ticks)); currentNodeSliceLeft = CurrentPlanNode.NextExec + CurrentPlanNode.Slice - SchedulingTime; Tracing.Log(Tracing.Debug, "Reservation for {0} ticks", (UIntPtr)unchecked((uint)currentNodeSliceLeft.Ticks)); #if LOG_SCHEDULER_DETAILS logCurrentNodeSliceLeft1 = currentNodeSliceLeft.Ticks; #endif while (currentNodeSliceLeft.Ticks < 0) { // choose Next node GraphNode tempNode = CurrentPlanNode; DateTime tempTime = CurrentPlanNode.NextExec; CurrentPlanNode.NextExec += CurrentPlanNode.Period; // time of Next execution // If incomplete, the default activity will be added // to the 'pStandbyActiv' List when ready CurrentPlanNode = NextNode(CurrentPlanNode); Debug.Assert(CurrentPlanNode.NextExec <= tempNode.NextExec); Debug.Assert(CurrentPlanNode.NextExec > tempTime); Debug.Assert(CurrentPlanNode.NextExec <= SchedulerPlan.NextExec); //In fact, should be min of all nodes. currentNodeSliceLeft = CurrentPlanNode.NextExec + CurrentPlanNode.Slice - SchedulingTime; } #if LOG_SCHEDULER_DETAILS logCurrentNodeSliceLeft2 = currentNodeSliceLeft.Ticks; #endif if (currentNodeSliceLeft < RialtoScheduler.MinSlice) { // choose Next node CurrentPlanNode.NextExec += CurrentPlanNode.Period; // time of Next execution // If incomplete, the default activity will be added // to the 'pStandbyActiv' List when ready CurrentPlanNode = NextNode(CurrentPlanNode); Debug.Assert(CurrentPlanNode.NextExec <= SchedulerPlan.NextExec); currentNodeSliceLeft = CurrentPlanNode.NextExec + CurrentPlanNode.Slice - SchedulingTime; // don't waste the leftover } #if LOG_SCHEDULER_DETAILS logCurrentNodeSliceLeft3 = currentNodeSliceLeft.Ticks; logCurrentPlanNodeId = (CurrentPlanNode.DefaultActivity != null) ? CurrentPlanNode.DefaultActivity.Id : -1; #endif // Do adjustment before computation of CurrentSlice is started // currentNodeSliceLeft -= RESCHEDULE_OVHD; // !!!!!!!!!!! SIM only !!!!!!!!!!! //CurrentSlice = currentNodeSliceLeft; Debug.Assert(currentNodeSliceLeft /* CurrentSlice */ >= RialtoScheduler.MinSlice); if (CurrentPlanNode.ReservCount > 0) { if (!CurrentPlanNode.ReservationArray[0].AssociatedReservation.Valid || // need to do some cleaning (CurrentPlanNode.ReservationArray[0].End <= SchedulingTime + RialtoScheduler.AFewSlice) || (CurrentPlanNode.ReservationArray[0].AssociatedReservation.Estimate <= RialtoScheduler.AFewSlice && CurrentPlanNode.ReservationArray[0].AssociatedReservation.OriginalThread == null)) { CleanReservationArray(CurrentPlanNode, SchedulingTime); } if (CurrentPlanNode.ReservCount > 0) { // are there any active reservation left? #if LOG_SCHEDULER_DETAILS logReservCount = CurrentPlanNode.ReservCount; #endif nextStart = CurrentPlanNode.ReservationArray[0].Start; if (nextStart > SchedulingTime + RialtoScheduler.AFewSlice) { if (nextStart < SchedulingTime + currentNodeSliceLeft) { // GATECH currentNodeSliceLeft /* CurrentSlice */ = nextStart - SchedulingTime; // GATECH Debug.Assert(currentNodeSliceLeft /* CurrentSlice */.Ticks > 0); } } else { // GraphNode OneShotReservation in effect; find a runnable reservation: OneShotReservation.FindRunnableReservation(ref currentThread); if (currentThread != null) { // a runnable reservation was found: // TO DO: optimize the comparisons currentNodeSliceLeft /* CurrentSlice */ = minInterval(currentNodeSliceLeft /* CurrentSlice */, CurrentPlanNode.ReservationArray[0].End - SchedulingTime); currentNodeSliceLeft /* CurrentSlice */ = minInterval(currentNodeSliceLeft /* CurrentSlice */, OneShotReservation.CurrentReservation.Estimate); currentNodeSliceLeft /* CurrentSlice */ = minInterval(currentNodeSliceLeft /* CurrentSlice */, OneShotReservation.CurrentReservation.Deadline - SchedulingTime); // DebugStub.Print("OneShotCpuReservation in effect.\n"); goto AdjustSlice; } else { EarliestDeadlineFirstBlocked = true; OneShotReservation.ClearCurrentReservation(); } } // else } // 2nd if (CurrentPlanNode.ReservCount > 0) } // 1st if (CurrentPlanNode.ReservCount > 0) // No GraphNode OneShotReservation in effect; run default activity, if any: if (CurrentPlanNode.Type == GraphNode.NodeType.Used) { DequeueStandbyActivity(CurrentPlanNode.DefaultActivity); // nop if not on list if ((currentThread = CurrentPlanNode.DefaultActivity.GetRunnableThread()) != null) { // DebugStub.Print("Running Default Resource Container.\n"); Tracing.Log(Tracing.Debug, "Default resource container."); #if LOG_SCHEDULER_DETAILS logRunningDefault = true; #endif goto AdjustSlice; } else if (CurrentPlanNode.DefaultActivity.LastNode == CurrentPlanNode) { CurrentPlanNode.DefaultActivity.LastNode = null; } } // Current GraphNode is Free or the default activity is not runnable; // Try to use slice for the queue of Standby Activities */ while (standbyActivities != null) { // Dequeue some of the activities that have too little left to do! if (standbyActivities.MyRecurringCpuReservation != null && standbyActivities.MyRecurringCpuReservation.SliceLeft < RialtoScheduler.MinSlice) { DequeueStandbyActivity(standbyActivities); continue; // if it was the last incomplete activity, exit while } currentThread = standbyActivities.GetRunnableThread(); currentNodeSliceLeft /* CurrentSlice */ = minInterval(currentNodeSliceLeft /* CurrentSlice */, (standbyActivities.MyRecurringCpuReservation == null? RialtoScheduler.MinSlice: standbyActivities.MyRecurringCpuReservation.SliceLeft)); currentStandbyActivity = standbyActivities; if (currentStandbyActivity.MyRecurringCpuReservation == null) { DequeueStandbyActivity(standbyActivities); } Debug.Assert(currentThread != null); //DebugStub.Print("Running a standby resource container\n"); #if LOG_SCHEDULER_DETAILS logRunningStandby = true; #endif goto AdjustSlice; } // Same about Current GraphNode but no Standby Activity to run. // Use slice for the RoundRobin queue (always nonempty). if (robinSliceLeft < RialtoScheduler.MinSlice) { robinActivityNext = robinActivityNext.Next; // don't run it again robinSliceLeft = RialtoScheduler.RobinSlice; } // Find Next Runnable Activity. robinActivityCurrent = robinActivityNext; // !!!!!!!!!!!!!SIM ONLY !!!!!!!!!! while ((currentThread = robinActivityNext.GetRunnableThread()) == null) { robinActivityNext = robinActivityNext.Next; robinSliceLeft = RialtoScheduler.RobinSlice; if (robinActivityNext == robinActivityCurrent) { // !!!!!!!!SIM ONLY !!!!!!! // in the real scheduler, execute halt if (OneShotReservation.IdleReservations != null) { // reuse nextStart nextStart = minTime(RialtoThread.GetSleepTimeout(), OneShotReservation.IdleReservations.Start); } else { nextStart = RialtoThread.GetSleepTimeout(); //DebugStub.Print("idle, sleeping until {0} cf maxvalue {1}\n", // __arglist(nextStart.Ticks, DateTime.MaxValue.Ticks)); } if (nextStart == DateTime.MaxValue) { Scheduler.StopSystem(); } Tracing.Log(Tracing.Debug, ""); if (! ResetTimerTimeout(nextStart)) { //Error setting timer. Try scheduling again. DebugStub.Print("Thought idle, failed to set interrupt.\n"); goto RescheduleAgain; } idle = true; // !!!!!!!!SIM ONLY !!!!!!! currentThread = null; // !!!!!!!!SIM ONLY !!!!!!! OneShotReservation.ClearCurrentReservation(); robinActivityCurrent = null; currentStandbyActivity = null; if (DateTime.MaxValue != nextStart) { Scheduler.LogTimeJump(); } //DebugStub.Print("Halted.\n"); return true; // !!!!!!!!SIM ONLY !!!!!!! } // !!!!!!!!SIM ONLY !!!!!!! } //DebugStub.Print("Running Round Robin Resource Container\n"); #if LOG_SCHEDULER_DETAILS logRunningRobin = true; #endif robinActivityCurrent = robinActivityNext; // we probably need only one of the two variables currentNodeSliceLeft /* CurrentSlice */ = minInterval(currentNodeSliceLeft /* CurrentSlice */, robinSliceLeft); #if LOG_SCHEDULER_DETAILS logCurrentNodeSliceLeft4 = currentNodeSliceLeft.Ticks; #endif AdjustSlice: // always set timer before Next wakeup time! Debug.Assert(currentThread != null); if (currentThread != previousThread) { Scheduler.LogContextSwitch(); // Context Switch statistics } if (OneShotReservation.IdleReservations != null) { // reuse nextStart nextStart = minTime(RialtoThread.GetSleepTimeout(), OneShotReservation.IdleReservations.Start); } else { nextStart = RialtoThread.GetSleepTimeout(); } if (SchedulingTime + currentNodeSliceLeft /* CurrentSlice */ > nextStart) { currentNodeSliceLeft /* CurrentSlice */ = nextStart - SchedulingTime; } #if LOG_SCHEDULER_DETAILS logCurrentNodeSliceLeft = currentNodeSliceLeft.Ticks; #if VERBOSEX unchecked { Tracing.LogSchedulerDetails( logReservCount, logDirectSwitch, logRunningDefault, logRunningStandby, logRunningRobin, (int) logCurrentNodeSliceLeft1, (int) logCurrentNodeSliceLeft2, (int) logCurrentNodeSliceLeft3, (int) logCurrentNodeSliceLeft4, (int) logCurrentNodeSliceLeft, logCurrentPlanNodeId); } #endif #endif Scheduler.LogReschedule(); if (!ResetTimerTimeout(SchedulingTime + currentNodeSliceLeft) || Scheduler.TimerInterruptedFlag) { //TODO: What do we REALLY want here? currentThread = null; // !!!!!!!!SIM ONLY !!!!!!! OneShotReservation.ClearCurrentReservation(); robinActivityCurrent = null; currentStandbyActivity = null; goto RescheduleAgain; } exitOK: if (currentThread != previousThread) { IChangeCurrentThread(currentThread); } idle = false; // Not necessarily true: Debug.Assert(!Scheduler.TimerInterruptedFlag); return false; } #endregion #region Constraint feasibility analysis support functions // Constraint Feasibility Analysis support functions begin: // ---------------------------------------------------------------------------------- static TimeSpan ComputeAvailableCpu(DateTime begin, DateTime end, GraphNode node) { if (begin >= end) { return new TimeSpan(0); } else { TimeSpan reserved = new TimeSpan(0); DateTime nextExecution; int k; if (node == CurrentPlanNode) { nextExecution = node.NextExec + node.Period; if (begin < node.NextExec + node.Slice) { // TempStart >= CurrentTime reserved += node.NextExec + node.Slice - begin; } } else { nextExecution = node.NextExec; } if (begin > nextExecution) { k = (int) ((begin - nextExecution).Ticks/node.Period.Ticks); reserved -= new TimeSpan(k * node.Slice.Ticks) + minInterval(node.Slice, begin - nextExecution - new TimeSpan(k * node.Period.Ticks)); } if (end > nextExecution) { k = (int) ((end - nextExecution).Ticks/node.Period.Ticks); reserved += new TimeSpan(k * node.Slice.Ticks) + minInterval(node.Slice, end - nextExecution - new TimeSpan(k * node.Period.Ticks)); } return reserved; } } // GraphNode can provide more than time than necessary between start and deadline; come shorter deadline: static TimeSpan AvailableCpuAndDeadline(DateTime begin, DateTime end, out DateTime shortDeadline, GraphNode node, TimeSpan requested) { TimeSpan reserved = new TimeSpan(0); DateTime nextExecution; DateTime temp; TimeSpan tempSpan; int k; shortDeadline = end; if (node == CurrentPlanNode) { //If the node specified is the current one running? if (node.NextExec + node.Slice - begin >= requested) { //If there's enough time between begin and the end of current node's execution shortDeadline = begin + requested; //case 1 return requested; } else if (node.NextExec.Ticks + node.Slice.Ticks - begin.Ticks >= 0) { //case 2 reserved += node.NextExec + node.Slice - begin; // reserved < requested } nextExecution = node.NextExec + node.Period; } else { nextExecution = node.NextExec; } if (begin > nextExecution) { //case 3: note -- precludes case 2 k = (int) ((begin - nextExecution).Ticks/node.Period.Ticks); // k -> number of complete periods before begin. reserved -= new TimeSpan(k * node.Slice.Ticks) + minInterval(node.Slice, begin - nextExecution - new TimeSpan(k * node.Period.Ticks)); // calculates the total time remaining before begin we could have reserved -- subtracts it from reserved } k = (int) ((requested - reserved).Ticks/node.Slice.Ticks); //case 2 above -- number of remaining slices needed //case 3 above -- number of complete slices in the calculation + number of slices for the request tempSpan = requested - new TimeSpan(k * node.Slice.Ticks) - reserved; //remainder ms from (requested-reserved)/slice if (tempSpan.Ticks > 0) { temp = nextExecution + new TimeSpan(k * node.Period.Ticks) + tempSpan; } else { temp = nextExecution + new TimeSpan(((k>0)?k-1:0) * node.Period.Ticks) + node.Slice; } if (temp <= end) { shortDeadline = temp; return requested; } if (end > nextExecution) { k = (int) ((end - nextExecution).Ticks/node.Period.Ticks); reserved += new TimeSpan(k * node.Slice.Ticks) + minInterval(node.Slice, end - nextExecution - new TimeSpan(k * node.Period.Ticks)); } return reserved; } // add a new pointer to a reservation to a node and clean the ordered array static void AddReservationToReservationArray(GraphNode node, OneShotReservation reservation, DateTime start, DateTime end, TimeSpan available, DateTime timeNow) { int i, j, insertPosition = -1; int release = 0; OneShotReservation tempReservation; Debug.Assert(node.ReservCount < GraphNode.MaxNumReservations); Debug.Assert((start < end) && (available.Ticks > 0)); Debug.Assert(node.ReservCount <= GraphNode.MaxNumReservations); for (i = j = 0; i < node.ReservCount; i++) { tempReservation = node.ReservationArray[i].AssociatedReservation; if (node.ReservationArray[i].End <= timeNow || !tempReservation.Valid) { ReleaseReservationProtected(tempReservation); release++; } else { if ((insertPosition < 0) && (start < node.ReservationArray[i].Start)) { insertPosition = j; } if (j < i) { node.ReservationArray[j] = (ReservationSlice)node.ReservationArray[i].Clone(); } //memcpy(&(node.ReservationArray[j]), &(node.ReservationArray[i]), sizeof(ReservationSlice)); j++; } } if (insertPosition < 0) { insertPosition = j; } else { Array.Copy(node.ReservationArray, insertPosition, node.ReservationArray, insertPosition+1, (j-insertPosition)); } //memmove(&(node.ReservationArray[insertPosition+1]), &(node.ReservationArray[insertPosition]), // sizeof(ReservationSlice) * (j - insertPosition)); node.ReservationArray[insertPosition] = new ReservationSlice(); node.ReservationArray[insertPosition].Start = start; node.ReservationArray[insertPosition].End = end; node.ReservationArray[insertPosition].Available = available; node.ReservationArray[insertPosition].AssociatedReservation = reservation; AddRefReservation(reservation); Debug.Assert(node.ReservCount == j + release); node.ReservCount = j + 1; Debug.Assert(node.ReservCount <= GraphNode.MaxNumReservations); } public static void ReserveSlices(DateTime start, DateTime deadline, TimeSpan requestedTime, RialtoThread task, out TimeSpan leftUnreserved, ref TimeSpan timeToInherit, OneShotReservation reservation, GraphNode nodeList, DateTime timeNow) { GraphNode node; OneShotReservation localReservation; int i; TimeSpan stillNeeded, toBeInherited, availableCpu, tempSpan; DateTime tempStart, tempEnd, tempMiddle, newEnd, temp; Debug.Assert(Processor.InterruptsDisabled()); stillNeeded = requestedTime; if (timeToInherit.Ticks >= 0) { toBeInherited = timeToInherit; } else { toBeInherited = new TimeSpan(0); } ArrayList newNodeReservations = new ArrayList(); for (node = nodeList; node != null; node = node.SameActivityNext) { //NewReservCount = 0; newNodeReservations.Clear(); // check if there is room to place another node reservation for this node if ((node.ReservCount == GraphNode.MaxNumReservations) && !CleanReservationArray(node, timeNow)) { continue; // failed to find room for another reservation } tempStart = start; // compute all CPU time available from this node for (i = 0; (i < node.ReservCount) && (tempStart < deadline); i++) { // the Begin fields are in increasing order: localReservation = node.ReservationArray[i].AssociatedReservation; if (!localReservation.Valid) { continue; } tempEnd = node.ReservationArray[i].End; if (tempEnd <= tempStart) { continue; // this node reservation doesn't count } tempMiddle = node.ReservationArray[i].Start; // tempEnd > tempStart // tempStart..min(tempMiddle, deadline) can be allocated, // max(tempStart, tempMiddle)..min(tempEnd, deadline) may be stolen or inherited if ((tempMiddle > tempStart) && (node.ReservCount + newNodeReservations.Count < GraphNode.MaxNumReservations)) { // allocate [tempStart , min(tempMiddle, deadline)] or a fraction of it: temp = minTime(tempMiddle, deadline); availableCpu = AvailableCpuAndDeadline(tempStart, temp, out newEnd, node, stillNeeded); Debug.Assert((availableCpu <= stillNeeded) || (newEnd <= temp)); if (availableCpu >= stillNeeded) { // success: ReservationSlice tempSlice = new ReservationSlice(); tempSlice.Start = tempStart; tempSlice.End = newEnd; tempSlice.Available = availableCpu; tempSlice.AssociatedReservation = reservation; newNodeReservations.Add(tempSlice); //NewNodeReserv[NewReservCount] = new ReservationSlice(); goto early_exit; } else if (availableCpu.Ticks > 0) { ReservationSlice tempSlice = new ReservationSlice(); stillNeeded -= availableCpu; tempSlice.Start = tempStart; tempSlice.End = temp; tempSlice.Available = availableCpu; tempSlice.AssociatedReservation = reservation; newNodeReservations.Add(tempSlice); //NewNodeReserv[NewReservCount] = new ReservationSlice(); } } // max(tempStart, tempMiddle)..min(tempEnd, deadline) may be inherited: if ((timeToInherit.Ticks != -1) && (localReservation.ReservTask == task)) { // not yet stolen slice // we can count on the time provided by this reservation // as it is somewhere down the current stack of constraints if (localReservation.ResolutionEpoch != ResolutionAttempt) { localReservation.InheritedEstimate = new TimeSpan(0); localReservation.ResolutionEpoch = ResolutionAttempt; } tempSpan = localReservation.Estimate - localReservation.InheritedEstimate; if (OneShotReservation.CurrentReservation == localReservation) { tempSpan -= timeNow - SchedulingTime; } if ((tempStart > tempMiddle) || (tempEnd > deadline)) { // tempStart >= timeNow availableCpu = minInterval(ComputeAvailableCpu(maxTime(tempStart, tempMiddle), minTime(tempEnd, deadline), node), tempSpan); } else { // use $$-ed value availableCpu = minInterval(node.ReservationArray[i].Available, tempSpan); } // inherit no more than needed: availableCpu = minInterval(availableCpu, stillNeeded); if (availableCpu.Ticks > 0) { toBeInherited += availableCpu; stillNeeded -= availableCpu; localReservation.InheritedEstimate += availableCpu; Debug.Assert(localReservation.InheritedEstimate <= localReservation.Estimate); if (stillNeeded.Ticks == 0) { goto exit; } } } // Inherit End. tempStart = tempEnd; } // for (i = 0;......) for (i = 0; i < newNodeReservations.Count; i++) { // add collected slice reservations: AddReservationToReservationArray(node, ((ReservationSlice)newNodeReservations[i]).AssociatedReservation, ((ReservationSlice)newNodeReservations[i]).Start, ((ReservationSlice)newNodeReservations[i]).End, ((ReservationSlice)newNodeReservations[i]).Available, timeNow); } if (node.ReservCount < GraphNode.MaxNumReservations) { // if there is room for one more reservation // Compute the last possible CPU slice(s) to be allocated at this node: availableCpu = AvailableCpuAndDeadline(tempStart, deadline, out newEnd, node, stillNeeded); if (availableCpu >= stillNeeded) { AddReservationToReservationArray(node, reservation, tempStart, newEnd, availableCpu, timeNow); stillNeeded = new TimeSpan(0); goto exit; } if (availableCpu.Ticks > 0) { stillNeeded -= availableCpu; AddReservationToReservationArray(node, reservation, tempStart, deadline, availableCpu, timeNow); } } } // for each node IN THE input list (free or from own activity).... exit: leftUnreserved = stillNeeded; if (timeToInherit.Ticks != -1) { timeToInherit = toBeInherited; } return; early_exit: for (i = 0; i < newNodeReservations.Count; i++) { // add collected slice reservations: AddReservationToReservationArray(node, ((ReservationSlice)newNodeReservations[i]).AssociatedReservation, ((ReservationSlice)newNodeReservations[i]).Start, ((ReservationSlice)newNodeReservations[i]).End, ((ReservationSlice)newNodeReservations[i]).Available, timeNow); } goto exit; } public static bool CheckReservation(OneShotReservation reservation, DateTime timeNow) { TimeSpan timeLeft, timeInherited; GraphNode nodeList; TimeSpan timeToInherit; if (reservation.SurroundingReservation != null) { if (!reservation.Valid) { return CheckReservation(reservation.SurroundingReservation, timeNow); } if (!CheckReservation(reservation.SurroundingReservation, timeNow)) { return false; } } else if (!reservation.Valid) { return true; } timeInherited = new TimeSpan(0); if (reservation.AssociatedActivity != null) { timeToInherit = new TimeSpan(-1); nodeList = reservation.AssociatedActivity.MyRecurringCpuReservation.TempAssignedNodes; } else { ResolutionAttempt++; timeToInherit = timeInherited; nodeList = reservation.OriginalThread.AssociatedActivity.MyRecurringCpuReservation.TempAssignedNodes; } TimeSpan foo = new TimeSpan(0); //TODO: May need a separate flag to signal not interested if 0 isn't good enough ReserveSlices(reservation.Start, reservation.Deadline, reservation.Estimate, reservation.ReservTask, // taskId null Activ Reservs out timeLeft, ref timeToInherit, reservation, nodeList, timeNow); if (timeLeft.Ticks != 0) { ReserveSlices(reservation.Start, reservation.Deadline, reservation.Estimate, reservation.ReservTask, // taskId null Activ Reservs out timeLeft, ref timeToInherit, reservation, tempFreeNodes, timeNow); } if (timeLeft.Ticks > 0) { return false; } if (reservation.OriginalThread != null && timeInherited.Ticks > 0) { OneShotReservation.InheritOnEarliestDeadlineFirst(reservation.SurroundingReservation, timeInherited); } return true; } public static void DirectSwitchOnWait() { if (SchedulerClock.TimeToInterrupt() < RialtoScheduler.MinSlice) { return; //Don't do a directed context switch if there isn't much time. } if (OneShotReservation.CurrentReservation != null) { Debug.Assert(OneShotReservation.GuaranteedReservations != null); directSwitchTo = OneShotReservation.GuaranteedReservations.GetRunnableThread(); } // else if ((threadWaiter == GetCurrentThread()) && // (threadWaiter.AssociatedActivity.RunnableThreads == null)) { // directSwitchTo = pTH(thread); // } } public static void DirectSwitchOnWakeup() { if (((OneShotReservation.CurrentReservation != null) && (OneShotReservation.CurrentReservation != OneShotReservation.GuaranteedReservations)) || EarliestDeadlineFirstBlocked) { Reschedule(); } } public static void DirectSwitchOnConstraint() { if ((OneShotReservation.CurrentReservation != null) && (OneShotReservation.GuaranteedReservations != null) && (OneShotReservation.GuaranteedReservations.ReservTask != (GetCurrentThread()))) { Reschedule(); } } #endregion public static void Reschedule() { needToReschedule = true; } public static void ActivityObjAddRef(RialtoActivity activity) { Interlocked.Increment(ref activity.ReferenceCount); } public static void ActivityObjRelease(RialtoActivity activity) { Debug.Assert(activity.ReferenceCount >= 1); activity.ReleaseReference(); } public static void ActivityReleaseProtected(RialtoActivity activity) { Debug.Assert(activity.ReferenceCount >= 1); Debug.Assert(Processor.InterruptsDisabled(), "Interrupts not disabled!"); // XXX Need to implement HelperActivityRelease() that does release with preemption enabled // activity.ReleaseReference(); // DebugStub.Print("Need to implement HelperActivityRelease()\n"); } ///////////////////////////////////////////////////// Debugging Tools. // #if DEBUG_TREE static void PrintNode(GraphNode pNode, int level, string indent) { int k; if (pNode == null) { DebugStub.Print("\n"); return; } if (pNode.Type == GraphNode.NodeType.Used) { DebugStub.Print("{0}L{1} -- Per {2} --- Act {3} Slice {4}\n", __arglist(indent, level, pNode.Period, pNode.DefaultActivity.Id, pNode.Slice)); } else if (pNode.Type == GraphNode.NodeType.Free) { DebugStub.Print("{0}L{1} -- Per {2} --- FREE Slice {3}\n", __arglist(indent, level, pNode.Period, pNode.Slice)); } else { // BRANCH DebugStub.Print("{0}L{1} -- Per {2} --- BRANCH\n", __arglist(indent, level, pNode.Period)); string newIndent = indent + " "; PrintNode (pNode.Left, level+1, newIndent); PrintNode (pNode.Right, level+1, newIndent); Debug.Assert(pNode.Next == null); } PrintNode(pNode.Next, level, indent); } static void PrintSchedPlan(GraphNode SchedPlan, DateTime StartingAt) { int level= 0; DebugStub.Print("Scheduling plan starting at {0}\n", __arglist(StartingAt.Ticks)); PrintNode(SchedPlan, level, ""); } #endif #if DEBUG_RESERV // This code was ported over with PrintNode but hasn't been fully converted to working C# yet static TimeSpan ComputeTotalAvailableCpu(GraphNode pNode, int ReservIndex, DateTime TimeNow) { DateTime TempStart, TempEnd; TimeSpan AvailableCpu = 0; PRESERVATION pReserv; PRESERVATION pResInterest; int i; pResInterest = (PRESERVATION) POINTER(pNode.ReservArray[ReservIndex]); TempStart = maxTime(pResInterest->Start, TimeNow); for (i = 0; i < ReservIndex; i++) { // the Begin fields are in increasing order: pReserv = POINTER(pNode.ReservArray[i]); if (!pReserv->Valid) { continue; } TempEnd = NODE_DEADLINE(pNode.ReservArray[i]); if (TempEnd <= TempStart) { continue; // this node reservation doesn't count } // TempEnd > TempStart && NodeReservation->ReservTaskId != TaskId // TempStart..min(pReserv->Start, Deadline) can be allocated, // max(TempStart, pReserv->Start)..min(TempEnd, Deadline) may be stolen AvailableCpu += ComputeAvailableCpu(TempStart, minTime(min(pReserv->Start, TimeNow), pResInterest->Deadline), pNode); TempStart = TempEnd; } AvailableCpu += ComputeAvailableCpu(TempStart, NODE_DEADLINE(pNode.ReservArray[ReservIndex]), pNode); // assert (AvailableCpu != 0); return AvailableCpu; } static void PrintReservationNode(GraphNode pNode, int level, string indent, DateTime TimeNow) { int i, k; if (pNode == null) { DebugStub.Print("\n"); return; } if (pNode.Type == GraphNode.NodeType.Used || pNode.Type == GraphNode.NodeType.Free) { if (pNode.Type == GraphNode.NodeType.Used) { DebugStub.Print("{0}L{1} -- Per {2} --- Act {3} Slice {4}\n", __arglist(indent, level, pNode.Period, pNode.DefaultActivity.Id, pNode.Slice)); } else { // FREE DebugStub.Print("{0}L{1} -- Per {2} --- FREE Slice {3}\n", __arglist(indent, level, pNode.Period, pNode.Slice)); } for (i = 0; i < pNode.ReservCount; i++) { PRESERVATION pRes = POINTER(pNode.ReservArray[i]); Debug.Assert(NODE_DEADLINE(pNode.ReservArray[i]) != 0); DebugStub.Print("{0} R{1} - T{2}:A{3} (T{4}:A{5}) {6}-{7}"+ "{8} {9} Av.{10} Est {11}\n", __arglist( indent, pRes->ReservationId, (pRes->OriginalThread ? pRes->OriginalThread.Id : -1), (pRes->OriginalThread ? pRes->OriginalThread->pActivity.Id : pRes->pActivity.Id), (pRes->ActiveThread ? pRes->ActiveThread.Id : -1), (pRes->ActiveThread ? pRes->ActiveThread->pActivity.Id : pRes->pActivity.Id), pRes->Start, NODE_DEADLINE(pNode.ReservArray[i]), ((UINT)pNode.ReservArray[i] & 0x02 ? "STL":"NSTL"), (pRes->Valid ? "Vld":"NVld"), (pRes->Valid ? ComputeTotalAvailableCpu(pNode, i, TimeNow.Ticks) : -1), pRes->Estimate)); } } else { // BRANCH DebugStub.Print("{0}L{1} -- Per {2} --- BRANCH\n", __arglist(indent, level, pNode.Period)); string newIndent = indent + " "; PrintReservationNode (pNode.Left, level+1, newIndent, TimeNow); PrintReservationNode (pNode.Right, level+1, newIndent, TimeNow); Debug.Assert(pNode.Next == null); } PrintReservationNode(pNode.Next, level, indent, TimeNow); } void PrintReservations(PRESERVATION pReservation, BOOL ok, DateTime TimeNow) { int level = 0; DebugStub.Print("Reserv {0} (T{1}:A{2}): {3} {4} {5} ({6}) {7} at {8}\n", __arglist( pReservation->ReservationId, (pReservation->OriginalThread != null ? pReservation->OriginalThread.Id: -1), (pReservation->OriginalThread != null ? pReservation->OriginalThread->pActivity.Id : pReservation->pActivity.Id), pReservation->Start, pReservation->Deadline, pReservation->Estimate, (pReservation->Criticality == CRITICAL ? "CRT":"NCRT"), (ok ? "OK":"NO"), TimeNow.Ticks)); // DebugStub.Print(" (O {0} + I {0})\n", // __arglist(pReservation->OwnEstimate, pReservation->InheritedEstimate)); PrintReservationNode(pSchedPlan, level, "", TimeNow); } #endif } }