protected async Task WaitForIdleAsync(IExpeditableDelaySource expeditableDelaySource) { while (true) { if (CancellationToken.IsCancellationRequested) { return; } var diffInMS = Environment.TickCount - _lastAccessTimeInMS; if (diffInMS >= BackOffTimeSpanInMS) { return; } // TODO: will safestart/unwarp capture cancellation exception? var timeLeft = BackOffTimeSpanInMS - diffInMS; if (!await expeditableDelaySource.Delay(TimeSpan.FromMilliseconds(Math.Max(MinimumDelayInMS, timeLeft)), CancellationToken).ConfigureAwait(false)) { // The delay terminated early to accommodate a blocking operation. Make sure to delay long // enough that low priority (on idle) operations get a chance to be triggered. // // 📝 At the time this was discovered, it was not clear exactly why the delay was needed in order // to avoid live-lock scenarios. await Task.Delay(TimeSpan.FromMilliseconds(10), CancellationToken).ConfigureAwait(false); return; } } }
protected async Task WaitForIdleAsync(IExpeditableDelaySource expeditableDelaySource) { while (true) { if (CancellationToken.IsCancellationRequested) { return; } var diff = _timeSinceLastAccess.Elapsed; if (diff >= BackOffTimeSpan) { return; } // TODO: will safestart/unwarp capture cancellation exception? var timeLeft = BackOffTimeSpan - diff; if (!await expeditableDelaySource.Delay(TimeSpan.FromMilliseconds(Math.Max(s_minimumDelay.TotalMilliseconds, timeLeft.TotalMilliseconds)), CancellationToken).ConfigureAwait(false)) { // The delay terminated early to accommodate a blocking operation. Make sure to yield so low // priority (on idle) operations get a chance to be triggered. // // 📝 At the time this was discovered, it was not clear exactly why the yield (previously delay) // was needed in order to avoid live-lock scenarios. await Task.Yield().ConfigureAwait(false); return; } } }
public void EnqueueWork( Func <Task> workAsync, TaggerDelay delay, IExpeditableDelaySource delaySource, IAsyncToken asyncToken, CancellationToken cancellationToken) { lock (this) { _eventWorkQueue = _eventWorkQueue.ContinueWithAfterDelayFromAsync( _ => workAsync(), cancellationToken, delay.ComputeTimeDelay(), delaySource, TaskContinuationOptions.None, TaskScheduler.Default).CompletesAsyncOperation(asyncToken); } }
/// <summary> /// Create a ResettableDelay that will complete a task after a certain duration. The delay /// can be reset at any point before it elapses in which case completion is postponed. The /// delay can be reset multiple times. /// </summary> /// <param name="delayInMilliseconds">The time to delay before completing the task</param> /// <param name="foregroundTaskScheduler">Optional. If used, the delay won't start until the supplied TaskScheduler schedules the delay to begin.</param> public ResettableDelay(int delayInMilliseconds, IExpeditableDelaySource expeditableDelaySource, TaskScheduler foregroundTaskScheduler = null) { Contract.ThrowIfFalse(delayInMilliseconds >= 50, "Perf, only use delays >= 50ms"); _delayInMilliseconds = delayInMilliseconds; _expeditableDelaySource = expeditableDelaySource; _taskCompletionSource = new TaskCompletionSource <object>(); Reset(); if (foregroundTaskScheduler != null) { Task.Factory.SafeStartNew(() => StartTimerAsync(continueOnCapturedContext: true), CancellationToken.None, foregroundTaskScheduler); } else { _ = StartTimerAsync(continueOnCapturedContext: false); } }