//
// 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;
}
}