singrdk/base/Kernel/Singularity/Scheduling/Rialto/RialtoScheduler.cs

3163 lines
143 KiB
C#
Raw Normal View History

2008-03-05 09:52:00 -05:00
////////////////////////////////////////////////////////////////////////////////
//
// 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>
/// Summary description for RialtoScheduler.
/// </summary>
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();
}
/// <summary>
/// Initialize types that would otherwise cause dynamic allocations at runtime with preemption disabled
/// </summary>
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;
}
/// <summary>
/// Search for the existence of any node with a higher TreeLevel "under" me.
/// </summary>
/// <param name="Level">TreeLevel (basically tree position, 2^level + place in row)</param>
/// <returns></returns>
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<MaxProxies; i++) { nodeProxies.Insert(i, new NodeProxy()); } //memset(nodeProxies, 0, MaxProxies * sizeof(NodeProxy));
//ProxyCount = 0;
minPeriod = CpuResource.MaxPeriod;
ptemp = circularActivityList;
// Order Activities w/ a non-zero reservation in the 'nodeProxies' array!
do {
if (ptemp.MyRecurringCpuReservation != null && ptemp.MyRecurringCpuReservation.Slice.Ticks != 0) {
//Debug.Assert(nodeProxies.Count <= MaxProxies);
for (i = 0; i < nodeProxies.Count; i++) // find the right place
#if ASCEND_PERIOD //Sort the NodeProxy array in ascending order of period i.e. (out of N)
if ((ptemp.Period < ((NodeProxy)nodeProxies[i]).Period) ||
((ptemp.Period == ((NodeProxy)nodeProxies[i]).Period) &&
(ptemp.Slice > ((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;
}
// <summary>
// Returns the first index of a NodeProxy which has TreeLevel == -1
// </summary>
static int NextProxy(int ProxyIndex)
{
while ((ProxyIndex < nodeProxies.Count) &&
(((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel != -1)) {
ProxyIndex++;
}
return ProxyIndex;
}
// <summary>
// Returns NextProxy(0)
// </summary>
static int FirstProxy()
{
return NextProxy(0);
}
/// <summary>
/// Updates freeSlots by subtracting the slice from all appropriate descendants and parents.
/// </summary>
/// <param name="AllocLevel"></param>
/// <param name="AllocPosition"></param>
/// <param name="Slice"></param>
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];
}
}
/// <summary>
/// Updates freeSlots by adding the slice to all appropriate descendants and parents.
/// </summary>
/// <param name="AllocLevel"></param>
/// <param name="AllocPosition"></param>
/// <param name="Slice"></param>
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);
}
/// <summary>
/// Next largest less than previous max.
/// </summary>
/// <param name="PrevMax"></param>
/// <param name="PrevIndex"></param>
/// <param name="Level"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Assigns Activities (w/ non-zero reservations) to their Scheduling Tree Branches/SubTrees
/// Recursive.
/// </summary>
/// <param name="ProxyIndex"></param>
/// <returns></returns>
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
}
}