RaspberryIO/Unosquare.Swan/Components/DelayProvider.cs
2019-12-03 18:44:25 +01:00

139 lines
3.5 KiB
C#

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Unosquare.Swan.Abstractions;
namespace Unosquare.Swan.Components {
/// <summary>
/// Represents logic providing several delay mechanisms.
/// </summary>
/// <example>
/// The following example shows how to implement delay mechanisms.
/// <code>
/// using Unosquare.Swan.Components;
///
/// 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();
/// }
/// }
/// }
/// </code>
/// </example>
public sealed class DelayProvider : IDisposable {
private readonly Object _syncRoot = new Object();
private readonly Stopwatch _delayStopwatch = new Stopwatch();
private Boolean _isDisposed;
private IWaitEvent _delayEvent;
/// <summary>
/// Initializes a new instance of the <see cref="DelayProvider"/> class.
/// </summary>
/// <param name="strategy">The strategy.</param>
public DelayProvider(DelayStrategy strategy = DelayStrategy.TaskDelay) => this.Strategy = strategy;
/// <summary>
/// Enumerates the different ways of providing delays.
/// </summary>
public enum DelayStrategy {
/// <summary>
/// Using the Thread.Sleep(15) mechanism.
/// </summary>
ThreadSleep,
/// <summary>
/// Using the Task.Delay(1).Wait mechanism.
/// </summary>
TaskDelay,
/// <summary>
/// Using a wait event that completes in a background ThreadPool thread.
/// </summary>
ThreadPool,
}
/// <summary>
/// Gets the selected delay strategy.
/// </summary>
public DelayStrategy Strategy {
get;
}
/// <summary>
/// Creates the smallest possible, synchronous delay based on the selected strategy.
/// </summary>
/// <returns>The elapsed time of the delay.</returns>
public TimeSpan WaitOne() {
lock(this._syncRoot) {
if(this._isDisposed) {
return TimeSpan.Zero;
}
this._delayStopwatch.Restart();
switch(this.Strategy) {
case DelayStrategy.ThreadSleep:
DelaySleep();
break;
case DelayStrategy.TaskDelay:
DelayTask();
break;
#if !NETSTANDARD1_3
case DelayStrategy.ThreadPool:
this.DelayThreadPool();
break;
#endif
}
return this._delayStopwatch.Elapsed;
}
}
#region Dispose Pattern
/// <inheritdoc />
public void Dispose() {
lock(this._syncRoot) {
if(this._isDisposed) {
return;
}
this._isDisposed = true;
this._delayEvent?.Dispose();
}
}
#endregion
#region Private Delay Mechanisms
private static void DelaySleep() => Thread.Sleep(15);
private static void DelayTask() => Task.Delay(1).Wait();
#if !NETSTANDARD1_3
private void DelayThreadPool() {
if(this._delayEvent == null) {
this._delayEvent = WaitEventFactory.Create(isCompleted: true, useSlim: true);
}
this._delayEvent.Begin();
_ = ThreadPool.QueueUserWorkItem((s) => {
DelaySleep();
this._delayEvent.Complete();
});
this._delayEvent.Wait();
}
#endif
#endregion
}
}