// // Copyright (c) 2010-2022 Antmicro // // This file is licensed under the MIT License. // Full license text is available in 'licenses/MIT.txt'. // using System; using System.Diagnostics; using System.Threading; using Antmicro.Migrant; using Antmicro.Renode.Debugging; using Antmicro.Renode.Logging; namespace Antmicro.Renode.Time { /// /// Represents an object that can be used to interruptably suspend execution of the current thread for a given period. /// public class Sleeper { public Sleeper() { locker = new object(); stopwatch = new Stopwatch(); cancellationToken = new CancellationTokenSource(); } /// /// Suspends execution of the current thread for period. This can be interrupted by calling method on this object from other thread. /// /// /// The flag informing if sleeping was interrupted. /// See for the actual time spent sleeping before interruption. /// public bool Sleep(TimeSpan time, out TimeSpan timeElapsed, bool preserveInterruptRequest = false) { stopwatch.Restart(); var timeLeft = time; var tokenSource = cancellationToken; this.Trace($"Asked to sleep for {timeLeft}"); while(timeLeft.Ticks > 0 && !tokenSource.IsCancellationRequested) { this.Trace($"Sleeping for {timeLeft}"); tokenSource.Token.WaitHandle.WaitOne(timeLeft); timeLeft = time - stopwatch.Elapsed; } stopwatch.Stop(); timeElapsed = stopwatch.Elapsed > time ? time : stopwatch.Elapsed; lock(locker) { if(tokenSource.IsCancellationRequested && preserveInterruptRequest) { // Cancel the new token so that the next Sleep will pick up the cancellation // that interrupted this one Disable(); } return tokenSource.IsCancellationRequested; } } /// /// Disables sleeping. /// /// /// All subsequent calls to will finish immediately after calling this method. /// In order to be able to use method again call to is necessary. /// public void Disable() { lock(locker) { cancellationToken.Cancel(); } } /// /// Enables sleeping. /// /// /// Use this method to re-enable sleeping after calling . /// public void Enable() { lock(locker) { cancellationToken?.Cancel(); cancellationToken = new CancellationTokenSource(); } } /// /// Interrupts sleeping. /// /// /// Calling this method will wake up the sleeping thread if the sleeper is enabled. /// If the thread is not sleeping at the moment of calling this method, it has no effects. /// public void Interrupt() { lock(locker) { if(cancellationToken.IsCancellationRequested) { return; } Disable(); Enable(); } } [Constructor] private CancellationTokenSource cancellationToken; private readonly Stopwatch stopwatch; private readonly object locker; } }