namespace Swan.Threading
{
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
///
/// Represents logic providing several delay mechanisms.
///
///
/// The following example shows how to implement delay mechanisms.
///
/// using Swan.Threading;
///
/// public class Example
/// {
/// public static void Main()
/// {
/// // using the ThreadSleep strategy
/// using (var delay = new DelayProvider(DelayProvider.DelayStrategy.ThreadSleep))
/// {
/// // retrieve how much time was delayed
/// var time = delay.WaitOne();
/// }
/// }
/// }
///
///
public sealed class DelayProvider : IDisposable
{
private readonly object _syncRoot = new object();
private readonly Stopwatch _delayStopwatch = new Stopwatch();
private bool _isDisposed;
private IWaitEvent _delayEvent;
///
/// Initializes a new instance of the class.
///
/// The strategy.
public DelayProvider(DelayStrategy strategy = DelayStrategy.TaskDelay)
{
Strategy = strategy;
}
///
/// Enumerates the different ways of providing delays.
///
public enum DelayStrategy
{
///
/// Using the Thread.Sleep(15) mechanism.
///
ThreadSleep,
///
/// Using the Task.Delay(1).Wait mechanism.
///
TaskDelay,
///
/// Using a wait event that completes in a background ThreadPool thread.
///
ThreadPool,
}
///
/// Gets the selected delay strategy.
///
public DelayStrategy Strategy { get; }
///
/// Creates the smallest possible, synchronous delay based on the selected strategy.
///
/// The elapsed time of the delay.
public TimeSpan WaitOne()
{
lock (_syncRoot)
{
if (_isDisposed) return TimeSpan.Zero;
_delayStopwatch.Restart();
switch (Strategy)
{
case DelayStrategy.ThreadSleep:
DelaySleep();
break;
case DelayStrategy.TaskDelay:
DelayTask();
break;
case DelayStrategy.ThreadPool:
DelayThreadPool();
break;
}
return _delayStopwatch.Elapsed;
}
}
#region Dispose Pattern
///
public void Dispose()
{
lock (_syncRoot)
{
if (_isDisposed) return;
_isDisposed = true;
_delayEvent?.Dispose();
}
}
#endregion
#region Private Delay Mechanisms
private static void DelaySleep() => Thread.Sleep(15);
private static void DelayTask() => Task.Delay(1).Wait();
private void DelayThreadPool()
{
if (_delayEvent == null)
_delayEvent = WaitEventFactory.Create(isCompleted: true, useSlim: true);
_delayEvent.Begin();
ThreadPool.QueueUserWorkItem(s =>
{
DelaySleep();
_delayEvent.Complete();
});
_delayEvent.Wait();
}
#endregion
}
}