in src/Microsoft.Azure.SignalR.Common/Utilities/BackOffPolicy.cs [30:109]
public async Task<bool> CallProbeWithBackOffAsync(Func<Task<bool>> probe, Func<int, TimeSpan> getRetryDelay)
{
bool calledProbeOnce = false;
bool probeSuccess = false;
var myTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
do
{
// ensure only one caller will be selected to call probing func
var ongoingProbeTcs = Interlocked.CompareExchange(ref _currentProbeTcs, myTcs, null);
if (ongoingProbeTcs == null)
{
// initiate the probe and indicate its result to others
Task<bool> probeTask = null;
bool awaitProbeTask = false;
try
{
Debug.Assert(!calledProbeOnce);
calledProbeOnce = true;
probeTask = probe();
using (CancellationTokenSource delayCts = new CancellationTokenSource())
{
var delayTask = Task.Delay(getRetryDelay(_currentRetryCount++), delayCts.Token);
await Task.WhenAny(delayTask, probeTask);
// Handle success, timeout, and failure appropriately
if (//probeTask.IsCompletedSuccessfully in .NET Standard 2.1
probeTask.Status == TaskStatus.RanToCompletion && !probeTask.IsFaulted && !probeTask.IsCanceled
&& probeTask.Result)
{
// probe is successful
delayCts.Cancel();
probeSuccess = true;
}
else if (delayTask.IsCompleted) //delayTask.IsCompletedSuccessfully
{
// probe timeout
// make sure we still await for the probe task after indicating the failure to others
awaitProbeTask = true;
}
else
{
// probe failed
// make sure we still await for the probe task
awaitProbeTask = true;
// after waiting for the current backoff time and indicating failure to others
await delayTask;
}
}
}
finally
{
Interlocked.Exchange(ref _currentProbeTcs, null);
// indicate the result to others
myTcs.SetResult(probeSuccess);
}
// take care of unfinished probe task
if (awaitProbeTask)
{
probeSuccess = await probeTask;
}
}
// wait for the shared probe's result and try ourselves in case of the shared probe success
else if (await ongoingProbeTcs.Task)
{
Debug.Assert(!calledProbeOnce);
calledProbeOnce = true;
probeSuccess = await probe();
}
}
while (!calledProbeOnce);
if (probeSuccess)
{
// each successful probe call resets the retry counter
_currentRetryCount = 0;
}
return probeSuccess;
}