e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
135 lines
4.2 KiB
C#
135 lines
4.2 KiB
C#
//-----------------------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace System.Runtime
|
|
{
|
|
using System;
|
|
using System.Threading;
|
|
|
|
sealed class BackoffTimeoutHelper
|
|
{
|
|
readonly static int maxSkewMilliseconds = (int)(IOThreadTimer.SystemTimeResolutionTicks / TimeSpan.TicksPerMillisecond);
|
|
readonly static long maxDriftTicks = IOThreadTimer.SystemTimeResolutionTicks * 2;
|
|
readonly static TimeSpan defaultInitialWaitTime = TimeSpan.FromMilliseconds(1);
|
|
readonly static TimeSpan defaultMaxWaitTime = TimeSpan.FromMinutes(1);
|
|
|
|
DateTime deadline;
|
|
TimeSpan maxWaitTime;
|
|
TimeSpan waitTime;
|
|
IOThreadTimer backoffTimer;
|
|
Action<object> backoffCallback;
|
|
object backoffState;
|
|
Random random;
|
|
TimeSpan originalTimeout;
|
|
|
|
internal BackoffTimeoutHelper(TimeSpan timeout)
|
|
: this(timeout, BackoffTimeoutHelper.defaultMaxWaitTime)
|
|
{
|
|
}
|
|
|
|
internal BackoffTimeoutHelper(TimeSpan timeout, TimeSpan maxWaitTime)
|
|
: this(timeout, maxWaitTime, BackoffTimeoutHelper.defaultInitialWaitTime)
|
|
{
|
|
}
|
|
|
|
internal BackoffTimeoutHelper(TimeSpan timeout, TimeSpan maxWaitTime, TimeSpan initialWaitTime)
|
|
{
|
|
this.random = new Random(GetHashCode());
|
|
this.maxWaitTime = maxWaitTime;
|
|
this.originalTimeout = timeout;
|
|
Reset(timeout, initialWaitTime);
|
|
}
|
|
|
|
public TimeSpan OriginalTimeout
|
|
{
|
|
get
|
|
{
|
|
return this.originalTimeout;
|
|
}
|
|
}
|
|
|
|
void Reset(TimeSpan timeout, TimeSpan initialWaitTime)
|
|
{
|
|
if (timeout == TimeSpan.MaxValue)
|
|
{
|
|
this.deadline = DateTime.MaxValue;
|
|
}
|
|
else
|
|
{
|
|
this.deadline = DateTime.UtcNow + timeout;
|
|
}
|
|
this.waitTime = initialWaitTime;
|
|
}
|
|
|
|
public bool IsExpired()
|
|
{
|
|
if (this.deadline == DateTime.MaxValue)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return (DateTime.UtcNow >= this.deadline);
|
|
}
|
|
}
|
|
|
|
public void WaitAndBackoff(Action<object> callback, object state)
|
|
{
|
|
if (this.backoffCallback != callback || this.backoffState != state)
|
|
{
|
|
if (this.backoffTimer != null)
|
|
{
|
|
this.backoffTimer.Cancel();
|
|
}
|
|
this.backoffCallback = callback;
|
|
this.backoffState = state;
|
|
this.backoffTimer = new IOThreadTimer(callback, state, false, BackoffTimeoutHelper.maxSkewMilliseconds);
|
|
}
|
|
|
|
TimeSpan backoffTime = WaitTimeWithDrift();
|
|
Backoff();
|
|
this.backoffTimer.Set(backoffTime);
|
|
}
|
|
|
|
public void WaitAndBackoff()
|
|
{
|
|
Thread.Sleep(WaitTimeWithDrift());
|
|
Backoff();
|
|
}
|
|
|
|
TimeSpan WaitTimeWithDrift()
|
|
{
|
|
return Ticks.ToTimeSpan(Math.Max(
|
|
Ticks.FromTimeSpan(BackoffTimeoutHelper.defaultInitialWaitTime),
|
|
Ticks.Add(Ticks.FromTimeSpan(this.waitTime),
|
|
(long)(uint)this.random.Next() % (2 * BackoffTimeoutHelper.maxDriftTicks + 1) - BackoffTimeoutHelper.maxDriftTicks)));
|
|
}
|
|
|
|
void Backoff()
|
|
{
|
|
if (waitTime.Ticks >= (maxWaitTime.Ticks / 2))
|
|
{
|
|
waitTime = maxWaitTime;
|
|
}
|
|
else
|
|
{
|
|
waitTime = TimeSpan.FromTicks(waitTime.Ticks * 2);
|
|
}
|
|
|
|
if (this.deadline != DateTime.MaxValue)
|
|
{
|
|
TimeSpan remainingTime = this.deadline - DateTime.UtcNow;
|
|
if (this.waitTime > remainingTime)
|
|
{
|
|
this.waitTime = remainingTime;
|
|
if (this.waitTime < TimeSpan.Zero)
|
|
{
|
|
this.waitTime = TimeSpan.Zero;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|