2019-12-08 21:23:54 +01:00
|
|
|
|
using System;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace Swan.Threading {
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Represents a class that implements delay logic for thread workers.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static class WorkerDelayProvider {
|
2019-12-04 18:57:18 +01:00
|
|
|
|
/// <summary>
|
2019-12-08 21:23:54 +01:00
|
|
|
|
/// Gets the default delay provider.
|
2019-12-04 18:57:18 +01:00
|
|
|
|
/// </summary>
|
2019-12-08 21:23:54 +01:00
|
|
|
|
public static IWorkerDelayProvider Default => TokenTimeout;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Provides a delay implementation which simply waits on the task and cancels on
|
|
|
|
|
/// the cancellation token.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static IWorkerDelayProvider Token => new TokenCancellableDelay();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Provides a delay implementation which waits on the task and cancels on both,
|
|
|
|
|
/// the cancellation token and a wanted delay timeout.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static IWorkerDelayProvider TokenTimeout => new TokenTimeoutCancellableDelay();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Provides a delay implementation which uses short sleep intervals of 5ms.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static IWorkerDelayProvider TokenSleep => new TokenSleepDelay();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Provides a delay implementation which uses short delay intervals of 5ms and
|
|
|
|
|
/// a wait on the delay task in the final loop.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static IWorkerDelayProvider SteppedToken => new SteppedTokenDelay();
|
|
|
|
|
|
|
|
|
|
private class TokenCancellableDelay : IWorkerDelayProvider {
|
|
|
|
|
public void ExecuteCycleDelay(Int32 wantedDelay, Task delayTask, CancellationToken token) {
|
|
|
|
|
if(wantedDelay == 0 || wantedDelay < -1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// for wanted delays of less than 30ms it is not worth
|
|
|
|
|
// passing a timeout or a token as it only adds unnecessary
|
|
|
|
|
// overhead.
|
|
|
|
|
if(wantedDelay <= 30) {
|
|
|
|
|
try {
|
|
|
|
|
delayTask.Wait(token);
|
|
|
|
|
} catch { /* ignore */ }
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// only wait on the cancellation token
|
|
|
|
|
// or until the task completes normally
|
|
|
|
|
try {
|
|
|
|
|
delayTask.Wait(token);
|
|
|
|
|
} catch { /* ignore */ }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class TokenTimeoutCancellableDelay : IWorkerDelayProvider {
|
|
|
|
|
public void ExecuteCycleDelay(Int32 wantedDelay, Task delayTask, CancellationToken token) {
|
|
|
|
|
if(wantedDelay == 0 || wantedDelay < -1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// for wanted delays of less than 30ms it is not worth
|
|
|
|
|
// passing a timeout or a token as it only adds unnecessary
|
|
|
|
|
// overhead.
|
|
|
|
|
if(wantedDelay <= 30) {
|
|
|
|
|
try {
|
|
|
|
|
delayTask.Wait(token);
|
|
|
|
|
} catch { /* ignore */ }
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
_ = delayTask.Wait(wantedDelay, token);
|
|
|
|
|
} catch { /* ignore */ }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class TokenSleepDelay : IWorkerDelayProvider {
|
|
|
|
|
private readonly Stopwatch _elapsedWait = new Stopwatch();
|
|
|
|
|
|
|
|
|
|
public void ExecuteCycleDelay(Int32 wantedDelay, Task delayTask, CancellationToken token) {
|
|
|
|
|
this._elapsedWait.Restart();
|
|
|
|
|
|
|
|
|
|
if(wantedDelay == 0 || wantedDelay < -1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while(!token.IsCancellationRequested) {
|
|
|
|
|
Thread.Sleep(5);
|
|
|
|
|
|
|
|
|
|
if(wantedDelay != Timeout.Infinite && this._elapsedWait.ElapsedMilliseconds >= wantedDelay) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class SteppedTokenDelay : IWorkerDelayProvider {
|
|
|
|
|
private const Int32 StepMilliseconds = 15;
|
|
|
|
|
private readonly Stopwatch _elapsedWait = new Stopwatch();
|
|
|
|
|
|
|
|
|
|
public void ExecuteCycleDelay(Int32 wantedDelay, Task delayTask, CancellationToken token) {
|
|
|
|
|
this._elapsedWait.Restart();
|
|
|
|
|
|
|
|
|
|
if(wantedDelay == 0 || wantedDelay < -1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(wantedDelay == Timeout.Infinite) {
|
|
|
|
|
try {
|
|
|
|
|
_ = delayTask.Wait(wantedDelay, token);
|
|
|
|
|
} catch { /* Ignore cancelled tasks */ }
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while(!token.IsCancellationRequested) {
|
|
|
|
|
Int32 remainingWaitTime = wantedDelay - Convert.ToInt32(this._elapsedWait.ElapsedMilliseconds);
|
|
|
|
|
|
|
|
|
|
// Exit for no remaining wait time
|
|
|
|
|
if(remainingWaitTime <= 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(remainingWaitTime >= StepMilliseconds) {
|
|
|
|
|
Task.Delay(StepMilliseconds, token).Wait(token);
|
|
|
|
|
} else {
|
|
|
|
|
try {
|
|
|
|
|
_ = delayTask.Wait(remainingWaitTime);
|
|
|
|
|
} catch { /* ignore cancellation of task exception */ }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(this._elapsedWait.ElapsedMilliseconds >= wantedDelay) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-04 18:57:18 +01:00
|
|
|
|
}
|