private Task <TResult> ExecuteAsyncContinueWith(Task <TResult> runningTask)
        {
            if (!runningTask.IsFaulted || _cancellationToken.IsCancellationRequested)
            {
                return(runningTask);
            }

            Exception lastError = runningTask.Exception.InnerException;

            if (!(_isTransient(lastError)) || lastError is RetryLimitExceededException)
            {
                // if not transient, return the faulted running task.
                return(runningTask);
            }

            RetryCondition condition = _shouldRetryHandler(_retryCount++, lastError);

            if (!condition.RetryAllowed)
            {
                return(runningTask);
            }
            TimeSpan delay = condition.DelayBeforeRetry;

            // Perform an extra check in the delay interval.
            if (delay < TimeSpan.Zero)
            {
                delay = TimeSpan.Zero;
            }

            _onRetrying(_retryCount, lastError, delay);

            _previousTask = runningTask;
            if (delay > TimeSpan.Zero && (_retryCount > 1 || !_fastFirstRetry))
            {
                return(Task.Delay(delay)
                       .ContinueWith <Task <TResult> >(ExecuteAsyncImpl, CancellationToken.None,
                                                       TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)
                       .Unwrap());
            }

            return(ExecuteAsyncImpl(null));
        }
Beispiel #2
0
        public virtual TResult ExecuteAction <TResult>(Func <TResult> func)
        {
            Guard.ArgumentNotNull(func, "func");

            int       retryCount = 0;
            TimeSpan  delay      = TimeSpan.Zero;
            Exception lastError;

            var shouldRetry = this.RetryStrategy.GetShouldRetryHandler();

            for (;;)
            {
                lastError = null;

                try
                {
                    return(func());
                }
#pragma warning disable 0618
                catch (RetryLimitExceededException limitExceededEx)
#pragma warning restore 0618
                {
                    // The user code can throw a RetryLimitExceededException to force the exit from the retry loop.
                    // The RetryLimitExceeded exception can have an inner exception attached to it. This is the exception
                    // which we will have to throw up the stack so that callers can handle it.
                    if (limitExceededEx.InnerException != null)
                    {
                        throw limitExceededEx.InnerException;
                    }
                    else
                    {
                        return(default(TResult));
                    }
                }
                catch (Exception ex)
                {
                    lastError = ex;

                    if (!(this.ErrorDetectionStrategy.IsTransient(lastError)))
                    {
                        throw;
                    }

                    RetryCondition condition = shouldRetry(retryCount++, lastError);
                    if (!condition.RetryAllowed)
                    {
                        throw;
                    }
                    delay = condition.DelayBeforeRetry;
                }

                // Perform an extra check in the delay interval. Should prevent from accidentally ending up with the
                // value of -1 that will block a thread indefinitely. In addition, any other negative numbers will
                // cause an ArgumentOutOfRangeException fault that will be thrown by Thread.Sleep.
                if (delay.TotalMilliseconds < 0)
                {
                    delay = TimeSpan.Zero;
                }

                this.OnRetrying(retryCount, lastError, delay);

                if (retryCount > 1 || !this.RetryStrategy.FastFirstRetry)
                {
                    Task.Delay(delay).Wait();
                }
            }
        }