//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: OneShotReservation.cs // // Note: // using System; using System.Collections; using System.Diagnostics; using System.Threading; using Microsoft.Singularity; using Microsoft.Singularity.Scheduling; namespace Microsoft.Singularity.Scheduling.Rialto { /// /// OneShotReservations are created from tasks for the calling thread. /// It has fields to track the thread calling and the thread waiting on, /// the activity associated when it becomes an activity reservation, its /// surrounding reservation, its place on the free reservation list, /// general bookkeeping, bookkeeping for possible stolen time, and /// references for the general reservation list. /// public class OneShotReservation : ISchedulerTask { static OneShotReservation guaranteedReservations = null; public static OneShotReservation GuaranteedReservations { get { return guaranteedReservations; } } static OneShotReservation idleReservations = null; public static OneShotReservation IdleReservations { get { return idleReservations; } } static OneShotReservation currentReservation = null; public static OneShotReservation CurrentReservation { get { return currentReservation; } //set { currentReservation = value; } } public Hashtable resourcesGranted = new Hashtable(); public override Hashtable ResourcesGranted { get { resourcesGranted[CpuResource.Provider().ResourceString] = CpuResource.Provider().TimeToCpu(InitialEstimate); return resourcesGranted; } } private Task enclosingTask; public override Task EnclosingTask { get { return enclosingTask; } set { enclosingTask = value; } } public OneShotReservation SurroundingReservation; public OneShotReservation FreeListNext; public RialtoThread ReservTask; TimeSpan constraintExecution; public TimeSpan ConstraintExecution { get { return constraintExecution; } } TimeSpan initialThreadExecution; public DateTime Start, Deadline; public TimeSpan Estimate, RelativeDeadline; public TimeSpan InitialEstimate; // test only public QueueType MyQueueType; public bool Guaranteed; public bool Valid; public bool Satisfied; // Reservations can be made for Threads XOR Activities!. public RialtoThread OriginalThread; // always the constraint thread -- non-null until the reservation is complete //public RialtoThread ActiveThread; // the above, or the thread blocking it //public int ActiveThreadEpoch; public RialtoActivity AssociatedActivity; // the two fields above must be null! public OneShotReservation Next; public OneShotReservation Previous; public TimeSpan InheritedEstimate; // Stolen => Inherited, no longer critical public int ResolutionEpoch; // Stolen => Resolution, since not stealing. public int ReferenceCount; public OneShotReservation() { } public void Clear() { //ActiveThread = null; //ActiveThreadEpoch = 0; constraintExecution = new TimeSpan(0); //Move to enclosing class initialThreadExecution = new TimeSpan(0); //Move to enclosing class Deadline = new DateTime(0); RelativeDeadline = new TimeSpan(0); Estimate = new TimeSpan(0); FreeListNext = null; Guaranteed = false; InitialEstimate = new TimeSpan(0); Next = null; OriginalThread = null; AssociatedActivity = null; Previous = null; MyQueueType = QueueType.NoQueue; ReferenceCount = 0; ReservTask = null; Satisfied = false; Start = new DateTime(0); SurroundingReservation = null; ResolutionEpoch = 0; InheritedEstimate = new TimeSpan(0); Valid = false; if (enclosingTask != null) { enclosingTask.ClearSchedulerTask(); enclosingTask = null; } } // find the runnable thread of a reservation: public RialtoThread GetRunnableThread() { // RialtoThread thread; // if (AssociatedActivity != null) { // if an activity reservation return AssociatedActivity.GetRunnableThread(); } Debug.Assert(OriginalThread != null); // if ((ActiveThread == OriginalThread) && // (ActiveThread.EnclosingThread.ThreadState == System.Threading.ThreadState.Running)) // return ActiveThread; // // thread = OriginalThread; // search for active thread starting w/ original thread // // if ((ActiveThread != null) && // (ActiveThread.Epoch == ActiveThreadEpoch)) { // if (ActiveThread.EnclosingThread.ThreadState == System.Threading.ThreadState.Running) { //#if DEBUG_MUTEX // DebugStub.WriteLine("Mutex inheritance (Epoch ok): Orig {0} Active {1} GO ON", // thread.Id, ActiveThread.Id); //#endif // return ActiveThread; // } // } //CKillian -- can't do this because of the array of blockedOn. // else if (ActiveThread.EnclosingThread.IsSleeping()) { // // more efficient: search for active thread starting w/ current active thread // thread = ActiveThread; // } // else { // Debug.Assert(false); // } //} Thread temp = /*Active*/OriginalThread.EnclosingThread.GetRunnableBeneficiary(); if (temp == null) { return null; } else { return (RialtoThread)temp.SchedulerThread; } } public void StopThreadExecution(RialtoThread thread) { Debug.Assert(thread.ExecutionTime >= initialThreadExecution); constraintExecution += thread.ExecutionTime - initialThreadExecution; //Between two reservations which are atomically stopped/started, some // time is charged against the default parent resource container. This // is because I changed the way we account for tasks to do the general // rule we wanted. In particular, I update the scheduling status just // before resolving a new reservation. Not sure what the right fix is, // so I'm leaving it. The assertion below will reveal the "error." The // old code to track CPU usage is still active under constraintExecution, // but isn't presently exposed to the applications. Currently UpdateSchedulingStatus() // updates the CPU resources used for the whole task stack. //Debug.Assert(CpuResource.Provider().TimeToCpu(constraintExecution).Cycles == ((CpuResourceAmount)enclosingTask.resourcesUsed[CpuResource.Provider().ResourceString]).Cycles); //EnclosingTask.AddResourceAmountUsed(CpuResource.Provider().ResourceString, CpuResource.Provider().TimeToCpu(constraintExecution)); } public void StartThreadExecution(RialtoThread thread) { initialThreadExecution = thread.ExecutionTime; } public void StartThreadExecution(RialtoThread thread, TimeSpan delta) { initialThreadExecution = thread.ExecutionTime + delta; } public static void BeginConstraintBeforeWait(RialtoThread thread, bool endPrevious, TimeConstraint timeConstraint, DateTime timeNow) { OneShotReservation reservation; Debug.Assert(thread == RialtoScheduler.GetCurrentThread()); Debug.Assert(!Processor.InterruptsDisabled()); if (endPrevious) { bool iflag = Processor.DisableInterrupts(); EndPreviousConstraint(thread, timeNow); Processor.RestoreInterrupts(iflag); } RialtoScheduler.UpdateSchedulingStatus(); reservation = thread.PendingReservation; reservation.StartThreadExecution(thread, (timeNow - RialtoScheduler.SchedulingTime)); reservation.constraintExecution = new TimeSpan(0); reservation.Start = timeConstraint.Start; reservation.Deadline = timeConstraint.Deadline; reservation.RelativeDeadline = timeConstraint.RelativeDeadline; reservation.Estimate = timeConstraint.Estimate; reservation.Valid = reservation.Guaranteed = true; reservation.MyQueueType = QueueType.NoQueue; reservation.ReservTask = thread; reservation.OriginalThread = /*reservation.ActiveThread =*/ thread; thread.AddRef(); //thread.AddRef(); //reservation.ActiveThreadEpoch = thread.Epoch; reservation.AssociatedActivity = null; reservation.Next = reservation.Previous = null; } // DI -- this is a new function used in both Begin and EndConstraint public static bool EndPreviousConstraint(RialtoThread thread, DateTime timeNow) { OneShotReservation reservation; OneShotReservation reservationNext; bool success; QueueType tempQueue; //Value assigned but never used. Debug.Assert(Processor.InterruptsDisabled()); reservation = thread.ReservationStack; success = (timeNow <= reservation.Deadline); RialtoScheduler.UpdateSchedulingStatus(); reservation.StopThreadExecution(thread); Debug.Assert(reservation.ConstraintExecution.Ticks >= 0); // if (timeTaken.Ticks != 0) { // timeTaken = reservation.ConstraintExecution; // Debug.Assert(timeTaken.Ticks >= 0); //CK: Added = because VPC returns 0 sometimes. // } thread.ReservationStack = reservation.SurroundingReservation; reservation.SurroundingReservation = null; if (currentReservation == reservation) { // note that it might be the case that // a reservation for the task id exist on the guaranteed Q but the // the actual (executed) constraint is in the UnfinishedQ currentReservation = null; // ???????????? RialtoScheduler.Reschedule(); } Debug.Assert(reservation.OriginalThread == thread); Debug.Assert(reservation.Start <= timeNow); if (thread.ReservationStack == null) { if (reservation.Estimate >= RialtoScheduler.MinSlice) { // the constraint used less than estimated: Debug.Assert(reservation.MyQueueType == QueueType.GuaranteedQueue); //Debug.Assert((reservation.ActiveThread == null) || // (reservation.ActiveThread.Epoch != reservation.ActiveThreadEpoch) || // (reservation.ActiveThread == reservation.OriginalThread)); // reservation.ActiveThread.ReleaseProtected(); reservation.OriginalThread.ReleaseProtected(); reservation.AssociatedActivity = reservation.OriginalThread.AssociatedActivity; // and make it an activity reserv reservation.OriginalThread = null; //reservation.ActiveThread = null; reservation.ReservTask = null; RialtoScheduler.ActivityObjAddRef(reservation.AssociatedActivity); // leave it on whatever Q it happens to be on } else { reservation.DequeueReservation(); // from whatever Q it happens to be on } } else { tempQueue = reservation.MyQueueType; reservationNext = thread.ReservationStack; reservationNext.Estimate += reservation.Estimate; reservationNext.constraintExecution += reservation.constraintExecution; reservationNext.StartThreadExecution(thread); Debug.Assert(((reservation.MyQueueType == QueueType.GuaranteedQueue) && (reservationNext.MyQueueType == QueueType.NoQueue)) || ((reservation.MyQueueType == QueueType.UnfinishedQueue) && ((reservationNext.Estimate.Ticks <= 0) || (reservationNext.MyQueueType != QueueType.UnfinishedQueue)))); reservation.Estimate = new TimeSpan(0); Debug.Assert(reservation.Next != null); reservation.DequeueReservation(); ReplaceOnEarliestDeadlineFirst(reservationNext, timeNow); } // fprintf(stdout,"EndPrevConstr 0x%x %d\n", reservation, reservation.ReferenceCount); reservation.ReleaseReservationProtected(); Scheduler.LogEndConstraint(); if (success == false) { Scheduler.LogSchedulerLate(); } // DebugStub.WriteLine("EndPreviousConstraint() {0} constraintExecution: {1}", success?"SUCCESS":"FAILURE", reservation.constraintExecution.Ticks); return success; } public static bool ResolveConstraint(RialtoThread thread) { TimeSpan timeLeft, ownNodesTimeToSteal = new TimeSpan(1); DateTime start, deadline; TimeSpan timeInherited; OneShotReservation pendingReservation = thread.PendingReservation; OneShotReservation reservationPrevious; DateTime timeNow = SystemClock.GetKernelTime(); bool ok = false; Debug.Assert(Processor.InterruptsDisabled()); Debug.Assert(pendingReservation != null); Scheduler.LogResolveConstraint(); thread.PendingReservation = null; // just clean the place Debug.Assert(pendingReservation.Start.Ticks >= 0); start = pendingReservation.Start; if (start < timeNow) { start = timeNow; } Debug.Assert(pendingReservation.Deadline.Ticks >= 0); deadline = pendingReservation.Deadline; if (deadline.Ticks == 0) { deadline = timeNow + pendingReservation.RelativeDeadline; } if (thread.ReservationStack != null) { OneShotReservation reservation = thread.ReservationStack; while (!reservation.Valid && ((reservation = reservation.SurroundingReservation)!= null)) { // no body. } if (reservation!= null) { deadline = RialtoScheduler.minTime(deadline, reservation.Deadline); } } // update data ure pendingReservation.Start = start; pendingReservation.Deadline = deadline; //TODO: Should be for SIM ONLY! Scheduler.LogReservationId(pendingReservation); // SIM only if (start + pendingReservation.Estimate > deadline) { // DebugStub.WriteLine("RialtoScheduler::ResolveConstraint FAIL - start({0}) + pendingReservation.Estimate({1}) > deadline({2})", start.Ticks, pendingReservation.Estimate.Ticks, deadline.Ticks); ok = false; goto Failure; } timeInherited = new TimeSpan(0); RialtoScheduler.ResolutionAttempt++; // mark a new resolution epoch RecurringReservation recurringReservation = thread.AssociatedActivity.MyRecurringCpuReservation; RialtoScheduler.ReserveSlices(start, deadline, pendingReservation.Estimate, thread, out timeLeft, ref timeInherited, pendingReservation, (recurringReservation != null?recurringReservation.AssignedNodes:null), timeNow); if (timeLeft.Ticks == 0) { // Success: goto Success; } RialtoScheduler.ReserveSlices(start, deadline, timeLeft, thread, out timeLeft, ref timeInherited, pendingReservation, RialtoScheduler.FreeNodes, timeNow); if (timeLeft.Ticks <= 0) { // Success: goto Success; } // DebugStub.WriteLine("RialtoScheduler::ResolveConstraint FAIL - couldn't find time in schedule"); Failure: // Failure: pendingReservation.Valid = false; pendingReservation.Start = timeNow; // no waiting if failed pendingReservation.Estimate = new TimeSpan(0); goto Exit; Success: // DebugStub.WriteLine("RialtoScheduler::ResolveConstraint SUCCESS"); InheritAndReplaceOnEarliestDeadlineFirst(thread.ReservationStack, timeInherited, timeNow); ok = true; Exit: pendingReservation.InitialEstimate = pendingReservation.Estimate; reservationPrevious = thread.ReservationStack; pendingReservation.SurroundingReservation= thread.ReservationStack; thread.ReservationStack = pendingReservation; RialtoScheduler.AddRefReservation(pendingReservation); if (reservationPrevious != null) { reservationPrevious.constraintExecution += pendingReservation.initialThreadExecution - reservationPrevious.initialThreadExecution; } // // Reschedule() called if currently we are executing on a pendingReservation // and the EarliestDeadlineFirst top changed // or the current pendingReservation goes into the idle list // solve in SetStateWaiting // pendingReservation.EnqueueReservation(timeNow); RialtoScheduler.DirectSwitchOnConstraint(); #if DEBUG_RESERV PrintReservations(pendingReservation, sc, timeNow); #endif return ok; } void EnqueueReservation(DateTime timeNow) { OneShotReservation temp; Debug.Assert((AssociatedActivity != null) || (OriginalThread != null)); // Infeasible or (Estimate == 0) Reservations: if (Estimate <= RialtoScheduler.AFewSlice) { RialtoActivity activity; Debug.Assert(OriginalThread != null); Debug.Assert(AssociatedActivity == null); activity = OriginalThread.AssociatedActivity; MyQueueType = QueueType.UnfinishedQueue; if (activity.UnfinishedConstraints == null) { Next = Previous = this; activity.UnfinishedConstraints = this; } else { // no order enforced Next = activity.UnfinishedConstraints; Previous = activity.UnfinishedConstraints.Previous; activity.UnfinishedConstraints.Previous.Next = this; activity.UnfinishedConstraints.Previous = this; activity.UnfinishedConstraints = this; // optional } // DebugStub.WriteLine("Add: EnqueueReserve 1 0x{0:x} {1}", this, ReferenceCount); RialtoScheduler.AddRefReservation(this); return; } // Debug.Assert(Valid); // Idle Reservations: if (Start > timeNow + RialtoScheduler.AFewSlice) { // take thread off activity RoundRobin queue and simulate a sleep // i.e. don't set a timer interrupt OriginalThread.InternalSetStateWaiting(DateTime.MaxValue); #if DEBUG_START_RESERV DebugStub.WriteLine("Put reservation {0}:{1} in IdleQueue\n", OriginalThread, ReservationId); #endif Debug.Assert(OriginalThread != null); MyQueueType = QueueType.IdleQueue; if (idleReservations == null) { Next = Previous = this; idleReservations = this; } else { temp = idleReservations; if (Start < temp.Start) { idleReservations = this; } else { do { temp = temp.Next; } while (temp != idleReservations && Start >= temp.Start); // >= is compulsory to keep it 'FIFO' } // insert before 'temp' Next = temp; Previous = temp.Previous; temp.Previous.Next = this; temp.Previous = this; } // fprintf(stdout,"Add: EnqueueReserve 2 0x%x %d\n", this, ReferenceCount); RialtoScheduler.AddRefReservation(this); return; } // Guaranteed OneShotReservation: MyQueueType = QueueType.GuaranteedQueue; if (GuaranteedReservations == null) { Next = Previous = this; guaranteedReservations = this; } else { temp = GuaranteedReservations; if (Deadline < GuaranteedReservations.Deadline) { guaranteedReservations = this; } else { do { temp = temp.Next; } while (temp != guaranteedReservations && Deadline > temp.Deadline); // > is compulsory to keep it 'FIFO' } // insert before 'temp' Next = temp; Previous = temp.Previous; temp.Previous.Next = this; temp.Previous = this; } RialtoScheduler.AddRefReservation(this); #if DEBUG_RESERV PrintQueueReserv(guaranteedReservations, timeNow); //TODO: PrintQueueReserv #endif return; } static void ReplaceOnEarliestDeadlineFirst(OneShotReservation reservation, DateTime timeNow) { while (reservation != null) { if (reservation.Estimate.Ticks > 0) { if (reservation.MyQueueType == QueueType.NoQueue) { reservation.EnqueueReservation(timeNow); } else { Debug.Assert(reservation.MyQueueType == QueueType.GuaranteedQueue); } break; } if (reservation.MyQueueType == QueueType.NoQueue) { reservation.EnqueueReservation(timeNow); } else { Debug.Assert(reservation.MyQueueType == QueueType.UnfinishedQueue); } reservation = reservation.SurroundingReservation; } } public static bool SatisfyAcceptedConstraint(DateTime timeNow) { OneShotReservation reservation; if (currentReservation != null) { currentReservation.Estimate -= timeNow - RialtoScheduler.SchedulingTime; if (currentReservation.Estimate.Ticks <= 0) { RialtoThread tempThread = currentReservation.OriginalThread; currentReservation.DequeueReservation(); if (tempThread != null) { // activity reservations vanish here //Debug.Assert(currentReservation.ActiveThread == RialtoScheduler.GetCurrentThread()); // only thread reserv are added to currentReservation.EnqueueReservation(RialtoScheduler.SchedulingTime); // the UnfinishedConstraints list of the OriginalThread's Activ OneShotReservation.ReplaceOnEarliestDeadlineFirst(currentReservation.SurroundingReservation, RialtoScheduler.SchedulingTime); } } } reservation = guaranteedReservations; while (reservation != null) { if (!RialtoScheduler.CheckReservation(reservation, timeNow)) { return false; } reservation = reservation.Next; if (reservation == guaranteedReservations) { break; } } reservation = idleReservations; while (reservation != null) { if (!RialtoScheduler.CheckReservation(reservation, timeNow)) { return false; } reservation = reservation.Next; if (reservation == idleReservations) { break; } } return true; } public static void UpdateCurrentReservation() { if ((currentReservation != null) && (currentReservation.Estimate <= RialtoScheduler.AFewSlice)) { // slice used for a reservation (CK: & estimate now empty) RialtoThread tempThread = currentReservation.OriginalThread; currentReservation.DequeueReservation(); if (tempThread != null) { // activity reservations vanish here currentReservation.EnqueueReservation(RialtoScheduler.SchedulingTime); // the UnfinishedConstraints list of the OriginalThread's Activ ReplaceOnEarliestDeadlineFirst(currentReservation.SurroundingReservation, RialtoScheduler.SchedulingTime); } } } public static void FreshenReservationQueues() { OneShotReservation tempReservation; // Move 'idle' reservations to the 'active' reservations list, if necessary. // A reservation is 'idle' until its Start time is reached. // The 'active' reservation list is ordered 'Earliest Deadline Firts'. // The 'idle' queue is ordered Earliest Start First. while ((tempReservation = IdleReservations) != null) { if (RialtoScheduler.SchedulingTime + RialtoScheduler.AFewSlice < tempReservation.Start) { break; } tempReservation.DequeueReservation(); // Remove OneShotReservation from the Idle Queue tempReservation.EnqueueReservation(RialtoScheduler.SchedulingTime); // Earliest Deadline First in the (Non)Guaranteed Queue RialtoScheduler.EnqueueRunThread(tempReservation.OriginalThread); #if DEBUG_START_RESERV DebugStub.WriteLine("Get reservation {0}:{1} from IdleQueue", tempReservation.OriginalThread, tempReservation.ReservationId); #endif } // Remove the active reservations from the guaranteed and nonguaranteed queues // if their Deadline is <= CurrentTime. while ((tempReservation = guaranteedReservations) != null) { if (RialtoScheduler.SchedulingTime + RialtoScheduler.AFewSlice < tempReservation.Deadline) { break; } // fprintf(stdout,"Add: Reschedule Guaranteed Deadline 0x%x %d\n", tempReservation, tempReservation.ReferenceCount); RialtoScheduler.AddRefReservation(tempReservation); tempReservation.DequeueReservation(); // Remove OneShotReservation from the Guaranteed Queue if ((tempReservation.OriginalThread != null) && // if a thread reservation (tempReservation.OriginalThread.AssociatedActivity != null)) { // if a thread reservation tempReservation.Estimate = new TimeSpan(0); tempReservation.EnqueueReservation(RialtoScheduler.SchedulingTime); // add it to the Pending End Constraint List } // fprintf(stdout,"Reschedule Guaranteed Deadline off 0x%x %d\n", tempReservation, tempReservation.ReferenceCount); tempReservation.ReleaseReservationProtected(); } } int ReleaseReservationProtected() { return RialtoScheduler.ReleaseReservationProtected(this); } public void DequeueReservation() //TODO: Should this be a resource container function? { Debug.Assert(Processor.InterruptsDisabled()); OneShotReservation next; // OneShotReservation pSurrounding = null; Debug.Assert((AssociatedActivity != null) || (OriginalThread != null)); if (Next != this) { //If there's another reservation, remove us from the queue Next.Previous = Previous; Previous.Next = Next; next = Next; Debug.Assert(next.Deadline.Ticks > 0); } else { //Otherwise we are only reservation. Debug.Assert(Previous == this); next = null; } if (this == GuaranteedReservations) { //Take care when we're the head of the list. guaranteedReservations = next; #if DEBUG_RESERV DebugStub.WriteLine("Dequeue {0}: {1}", ReservationId, Estimate); // Debug.Assert(Criticality != CRITICAL || !Valid || Estimate ==0); PrintQueueReserv(GuaranteedReservations, SystemClock.GetKernelTime()); #endif } else if (this == IdleReservations) { //Or maybe we're the head of this list (but definitely not both). idleReservations = next; } else if ((OriginalThread != null) && (this == OriginalThread.AssociatedActivity.UnfinishedConstraints)) { //Or else we might be the head of the unfinished constraints list for the activity associated with our original thread. OriginalThread.AssociatedActivity.UnfinishedConstraints = next; } // else { we're OK! } Next = Previous = null; // DebugStub.WriteLine("DequeueReservation 0x{0:x} {1}", this, ReferenceCount); MyQueueType = QueueType.NoQueue; ReleaseReservationProtected(); } public static void FindRunnableReservation(ref RialtoThread currentThread) { currentReservation = guaranteedReservations; while (currentReservation != null) { if ((currentThread = currentReservation.GetRunnableThread()) != null) { break; } currentReservation = currentReservation.Next; if (currentReservation == guaranteedReservations) { break; } } } static void InheritAndReplaceOnEarliestDeadlineFirst(OneShotReservation reservation, TimeSpan timeInherited, DateTime timeNow) { OneShotReservation current = null; while (reservation != null) { if (reservation.Estimate.Ticks > 0) { if (current == null) { Debug.Assert(reservation.MyQueueType != QueueType.NoQueue); current = reservation; } else { Debug.Assert(reservation.MyQueueType == QueueType.NoQueue); } } else if (reservation.MyQueueType != QueueType.NoQueue) { Debug.Assert (current != null || reservation.MyQueueType == QueueType.UnfinishedQueue); reservation.DequeueReservation(); } if (reservation.ResolutionEpoch == RialtoScheduler.ResolutionAttempt) { Debug.Assert(timeInherited >= reservation.InheritedEstimate); timeInherited -= reservation.InheritedEstimate; Debug.Assert(reservation.Estimate >= reservation.InheritedEstimate); reservation.Estimate -= reservation.InheritedEstimate; } reservation = reservation.SurroundingReservation; } if (current != null) { if (current == CurrentReservation) { current.Estimate -= timeNow - RialtoScheduler.SchedulingTime; currentReservation = null; RialtoScheduler.Reschedule(); } current.DequeueReservation(); } } public static void InheritOnEarliestDeadlineFirst(OneShotReservation reservation, TimeSpan timeToInherit) { while (reservation != null && timeToInherit.Ticks > 0) { if (reservation.ResolutionEpoch == RialtoScheduler.ResolutionAttempt) { Debug.Assert(timeToInherit >= reservation.InheritedEstimate); reservation.Estimate -= reservation.InheritedEstimate; timeToInherit -= reservation.InheritedEstimate; } reservation = reservation.SurroundingReservation; } } public static void ClearCurrentReservation() { currentReservation = null; } } }