public void Advance(TimeInterval time, bool immediately = false) { lock (sync) { if (time > nearestLimitIn && !skipAdvancesHigherThanNearestLimit) { var left = time; while (left.Ticks > 0) { var thisTurn = TimeInterval.Min(nearestLimitIn, left); left -= thisTurn; AdvanceInner(thisTurn, immediately); } } else { AdvanceInner(time, immediately); } } }
/// <summary> /// Execute one iteration of time-granting loop. /// </summary> /// <remarks> /// The steps are as follows: /// (1) remove and forget all slave handles that requested detaching /// (2) check if there are any blocked slaves; if so DO NOT grant a time interval /// (2.1) if there are no blocked slaves grant a new time interval to every slave /// (3) wait for all slaves that are relevant in this execution (it can be either all slaves or just blocked ones) until they report back /// (4) (optional) sleep if the virtual time passed faster than a real one; this step is executed if <see cref="AdvanceImmediately"> is not set and <see cref="Performance"> is low enough /// (5) update elapsed virtual time /// (6) execute sync hook and delayed actions if any /// </remarks> /// <param name="virtualTimeElapsed">Contains the amount of virtual time that passed during execution of this method. It is the minimal value reported by a slave (i.e, some slaves can report higher/lower values).</param> /// <param name="timeLimit">Maximum amount of virtual time that can pass during the execution of this method. If not set, current <see cref="Quantum"> is used.</param> /// <returns> /// True if sync point has just been reached or False if the execution has been blocked. /// </returns> protected bool InnerExecute(out TimeInterval virtualTimeElapsed, TimeInterval?timeLimit = null) { if (updateNearestSyncPoint) { NearestSyncPoint += timeLimit.HasValue ? TimeInterval.Min(timeLimit.Value, Quantum) : Quantum; updateNearestSyncPoint = false; this.Trace($"Updated NearestSyncPoint to: {NearestSyncPoint}"); } DebugHelper.Assert(NearestSyncPoint.Ticks >= ElapsedVirtualTime.Ticks, "Nearest sync point set in the past"); isBlocked = false; var quantum = NearestSyncPoint - ElapsedVirtualTime; this.Trace($"Starting a loop with #{quantum.Ticks} ticks"); virtualTimeElapsed = TimeInterval.Empty; State = TimeSourceState.ReportingElapsedTime; using (sync.LowPriority) { handles.LatchAllAndCollectGarbage(); var shouldGrantTime = handles.AreAllReadyForNewGrant; this.Trace($"Iteration start: slaves left {handles.ActiveCount}; will we try to grant time? {shouldGrantTime}"); elapsedAtLastGrant = stopwatch.Elapsed; if (handles.ActiveCount > 0) { if (shouldGrantTime && quantum != TimeInterval.Empty) { this.Trace($"Granting {quantum.Ticks} ticks"); // inform all slaves about elapsed time foreach (var slave in handles) { slave.GrantTimeInterval(quantum); } } // in case we did not grant any time due to quantum being empty, we must not call wait as well if (!(shouldGrantTime && quantum == TimeInterval.Empty)) { this.Trace("Waiting for slaves"); // wait for everyone to report back State = TimeSourceState.WaitingForReportBack; TimeInterval?minInterval = null; foreach (var slave in handles.WithLinkedListNode) { var result = slave.Value.WaitUntilDone(out var usedInterval); if (!result.IsDone) { EnterBlockedState(); } handles.UpdateHandle(slave); if (result.IsUnblockedRecently) { Antmicro.Renode.Debugging.DebugHelper.Assert(recentlyUnblockedSlaves.Contains(slave.Value), $"Expected slave to be in {nameof(recentlyUnblockedSlaves)} collection."); recentlyUnblockedSlaves.Remove(slave.Value); this.Trace($"Number of unblocked slaves set to {recentlyUnblockedSlaves.Count}"); if (recentlyUnblockedSlaves.Count == 0) { sync.Pulse(); } } if (minInterval == null || minInterval > slave.Value.TotalElapsedTime) { minInterval = slave.Value.TotalElapsedTime; } } if (minInterval != null) { virtualTimeElapsed = minInterval.Value - ElapsedVirtualTime; } } } else { this.Trace($"There are no slaves, updating VTE by {quantum.Ticks}"); // if there are no slaves just make the time pass virtualTimeElapsed = quantum; TimePassed?.Invoke(quantum); } handles.UnlatchAll(); State = TimeSourceState.Sleeping; var elapsedThisTime = stopwatch.Elapsed - elapsedAtLastGrant; if (!AdvanceImmediately) { var scaledVirtualTicksElapsed = virtualTimeElapsed.WithScaledTicks(1 / Performance).ToTimeSpan() - elapsedThisTime; sleeper.Sleep(scaledVirtualTicksElapsed); } lock (hostTicksElapsed) { this.Trace($"Updating virtual time by {virtualTimeElapsed.InMicroseconds} us"); this.virtualTicksElapsed.Update(virtualTimeElapsed.InMicroseconds); this.hostTicksElapsed.Update(elapsedThisTime.InMicroseconds()); } } if (!isBlocked) { ExecuteSyncPhase(); updateNearestSyncPoint = true; } else { BlockHook?.Invoke(); } State = TimeSourceState.Idle; this.Trace($"The end of {nameof(InnerExecute)} with result={!isBlocked}"); return(!isBlocked); }
/// <summary> /// Execute one iteration of time-granting loop. /// </summary> /// <remarks> /// The steps are as follows: /// (1) remove and forget all slave handles that requested detaching /// (2) check if there are any blocked slaves; if so DO NOT grant a time interval /// (2.1) if there are no blocked slaves grant a new time interval to every slave /// (3) wait for all slaves that are relevant in this execution (it can be either all slaves or just blocked ones) until they report back /// (4) (optional) sleep if the virtual time passed faster than a real one; this step is executed if <see cref="AdvanceImmediately"> is not set and <see cref="Performance"> is low enough /// (5) update elapsed virtual time /// (6) execute sync hook and delayed actions if any /// </remarks> /// <param name="virtualTimeElapsed">Contains the amount of virtual time that passed during execution of this method. It is the minimal value reported by a slave (i.e, some slaves can report higher/lower values).</param> /// <param name="timeLimit">Maximum amount of virtual time that can pass during the execution of this method. If not set, current <see cref="Quantum"> is used.</param> /// <returns> /// True if sync point has just been reached or False if the execution has been blocked. /// </returns> protected bool InnerExecute(out TimeInterval virtualTimeElapsed, TimeInterval?timeLimit = null) { if (updateNearestSyncPoint) { NearestSyncPoint += timeLimit.HasValue ? TimeInterval.Min(timeLimit.Value, Quantum) : Quantum; updateNearestSyncPoint = false; this.Trace($"Updated NearestSyncPoint to: {NearestSyncPoint}"); } DebugHelper.Assert(NearestSyncPoint.Ticks >= ElapsedVirtualTime.Ticks, $"Nearest sync point set in the past: EVT={ElapsedVirtualTime} NSP={NearestSyncPoint}"); isBlocked = false; var quantum = NearestSyncPoint - ElapsedVirtualTime; this.Trace($"Starting a loop with #{quantum.Ticks} ticks"); SynchronizeVirtualTime(); var elapsedVirtualTimeAtStart = ElapsedVirtualTime; using (sync.LowPriority) { handles.LatchAllAndCollectGarbage(); var shouldGrantTime = handles.AreAllReadyForNewGrant; this.Trace($"Iteration start: slaves left {handles.ActiveCount}; will we try to grant time? {shouldGrantTime}"); if (handles.ActiveCount > 0) { var executor = new PhaseExecutor <LinkedListNode <TimeHandle> >(); if (!shouldGrantTime) { executor.RegisterPhase(ExecuteUnblockPhase); executor.RegisterPhase(ExecuteWaitPhase); } else if (quantum != TimeInterval.Empty) { executor.RegisterPhase(s => ExecuteGrantPhase(s, quantum)); executor.RegisterPhase(ExecuteWaitPhase); } if (ExecuteInSerial) { executor.ExecuteInSerial(handles.WithLinkedListNode); } else { executor.ExecuteInParallel(handles.WithLinkedListNode); } SynchronizeVirtualTime(); virtualTimeElapsed = ElapsedVirtualTime - elapsedVirtualTimeAtStart; } else { this.Trace($"There are no slaves, updating VTE by {quantum.Ticks}"); // if there are no slaves just make the time pass virtualTimeElapsed = quantum; UpdateTime(quantum); // here we must trigger `TimePassed` manually as no handles has been updated so they won't reflect the passed time TimePassed?.Invoke(quantum); } handles.UnlatchAll(); } SinksReportedkHook?.Invoke(); if (!isBlocked) { ExecuteSyncPhase(); updateNearestSyncPoint = true; } else { BlockHook?.Invoke(); } State = TimeSourceState.Idle; this.Trace($"The end of {nameof(InnerExecute)} with result={!isBlocked}"); return(!isBlocked); }
/// <summary> /// Execute one iteration of time-granting loop. /// </summary> /// <remarks> /// The steps are as follows: /// (1) remove and forget all slave handles that requested detaching /// (2) check if there are any blocked slaves; if so DO NOT grant a time interval /// (2.1) if there are no blocked slaves grant a new time interval to every slave /// (3) wait for all slaves that are relevant in this execution (it can be either all slaves or just blocked ones) until they report back /// (4) (optional) sleep if the virtual time passed faster than a real one; this step is executed if <see cref="AdvanceImmediately"> is not set and <see cref="Performance"> is low enough /// (5) update elapsed virtual time /// (6) execute sync hook and delayed actions if any /// </remarks> /// <param name="virtualTimeElapsed">Contains the amount of virtual time that passed during execution of this method. It is the minimal value reported by a slave (i.e, some slaves can report higher/lower values).</param> /// <param name="timeLimit">Maximum amount of virtual time that can pass during the execution of this method. If not set, current <see cref="Quantum"> is used.</param> /// <returns> /// True if sync point has just been reached or False if the execution has been blocked. /// </returns> protected bool InnerExecute(out TimeInterval virtualTimeElapsed, TimeInterval?timeLimit = null) { if (updateNearestSyncPoint) { NearestSyncPoint += timeLimit.HasValue ? TimeInterval.Min(timeLimit.Value, Quantum) : Quantum; updateNearestSyncPoint = false; this.Trace($"Updated NearestSyncPoint to: {NearestSyncPoint}"); } DebugHelper.Assert(NearestSyncPoint.Ticks >= ElapsedVirtualTime.Ticks, $"Nearest sync point set in the past: EVT={ElapsedVirtualTime} NSP={NearestSyncPoint}"); isBlocked = false; var quantum = NearestSyncPoint - ElapsedVirtualTime; this.Trace($"Starting a loop with #{quantum.Ticks} ticks"); virtualTimeElapsed = TimeInterval.Empty; using (sync.LowPriority) { handles.LatchAllAndCollectGarbage(); var shouldGrantTime = handles.AreAllReadyForNewGrant; this.Trace($"Iteration start: slaves left {handles.ActiveCount}; will we try to grant time? {shouldGrantTime}"); elapsedAtLastGrant = stopwatch.Elapsed; if (handles.ActiveCount > 0) { var executor = new PhaseExecutor <LinkedListNode <TimeHandle> >(); if (!shouldGrantTime) { executor.RegisterPhase(ExecuteUnblockPhase); executor.RegisterPhase(ExecuteWaitPhase); } else if (quantum != TimeInterval.Empty) { executor.RegisterPhase(s => ExecuteGrantPhase(s, quantum)); executor.RegisterPhase(ExecuteWaitPhase); } if (ExecuteInSerial) { executor.ExecuteInSerial(handles.WithLinkedListNode); } else { executor.ExecuteInParallel(handles.WithLinkedListNode); } var commonElapsedTime = handles.CommonElapsedTime; DebugHelper.Assert(commonElapsedTime >= ElapsedVirtualTime, $"A slave reports time from the past! The current virtual time is {ElapsedVirtualTime}, but {commonElapsedTime} has been reported"); virtualTimeElapsed = commonElapsedTime - ElapsedVirtualTime; } else { this.Trace($"There are no slaves, updating VTE by {quantum.Ticks}"); // if there are no slaves just make the time pass virtualTimeElapsed = quantum; // here we must trigger `TimePassed` manually as no handles has been updated so they won't reflect the passed time TimePassed?.Invoke(quantum); } handles.UnlatchAll(); State = TimeSourceState.Sleeping; var elapsedThisTime = stopwatch.Elapsed - elapsedAtLastGrant; if (!AdvanceImmediately) { var scaledVirtualTicksElapsed = virtualTimeElapsed.WithScaledTicks(1 / Performance).ToTimeSpan() - elapsedThisTime; sleeper.Sleep(scaledVirtualTicksElapsed); } lock (hostTicksElapsed) { this.Trace($"Updating virtual time by {virtualTimeElapsed.InMicroseconds} us"); this.virtualTicksElapsed.Update(virtualTimeElapsed.Ticks); this.hostTicksElapsed.Update(TimeInterval.FromTimeSpan(elapsedThisTime).Ticks); } } if (!isBlocked) { ExecuteSyncPhase(); updateNearestSyncPoint = true; } else { BlockHook?.Invoke(); } State = TimeSourceState.Idle; this.Trace($"The end of {nameof(InnerExecute)} with result={!isBlocked}"); return(!isBlocked); }