Example #1
0
        private async Task ExecuteWithPolicy(SubscriptionInfo info, Func <Task> action, Action <Exception> abort, Action ignore = null)
        {
            int  attempts = 0;
            bool retry    = true;

            do
            {
                try
                {
                    attempts++;
                    await action().ConfigureAwait(false);

                    retry = false;
                }
                catch (Exception exception)
                {
                    ExceptionResolution resolution = await ExceptionHandler(exception, attempts, info).ConfigureAwait(false);

                    switch (resolution)
                    {
                    case ExceptionResolution.Abort:
                        abort(exception);
                        retry = false;
                        break;

                    case ExceptionResolution.Retry:
                        break;

                    case ExceptionResolution.Ignore:
                        retry = false;
                        ignore?.Invoke();
                        break;
                    }
                }
            }while (retry);
        }
Example #2
0
        /// <summary>
        /// Main thingy.
        /// </summary>
        /// <remarks>
        /// This method will not run in parallel with itself.
        /// </remarks>
        /// <exception cref="AggregateException">
        /// Thrown when: An exception occurs on your processor method and: Either (1) your ExceptionHandler throws an exception, or (2) you did not provide ExceptionHandler.
        /// </exception>
        /// <exception cref="OperationCanceledException">
        /// Thrown when: your <see cref="CancellationToken"/> is triggered.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown when: you attempt to run this method in parallel.
        /// Or: the main <see cref="IEnumerable{T}"/> collection was modified in mid-iteration.
        /// </exception>
        public async Task <RunResult> Run(CancellationToken token)
        {
            #region Failsafes

            lock (_syncRoot)
            {
                if (_isRunning)
                {
                    throw new InvalidOperationException("You cannot parallel this method.");
                }

                _isRunning = true;
            }

            #endregion

            try
            {
                ExceptionResolution resolution          = ExceptionResolution.Swallow;
                List <Exception>    unhandledExceptions = new List <Exception>();

                #region Main Cycle

                foreach (T item in _collection)
                {
                    //T itemCopy = item;
                    IDisposable gateReleaser = await _gate.WaitAsync(token).ConfigureAwait(false);

                    WorkItem workItem = new WorkItem
                    {
                        CTS          = CancellationTokenSource.CreateLinkedTokenSource(token),
                        GateReleaser = gateReleaser
                    };

                    lock (_syncRoot)
                    {
                        _currentTasks.Add(workItem);
                    }

                    workItem.Task = Task.Run(async() =>
                    {
                        Interlocked.Increment(ref _stats._started);

                        try
                        {
                            await _method(item, workItem.CTS.Token).ConfigureAwait(false);
                            Interlocked.Increment(ref _stats._completed);
                        }
                        catch (OperationCanceledException) when(token.IsCancellationRequested)
                        {
                            // The user should expect this exception to be thrown when they trigger their token.
                            Interlocked.Increment(ref _stats._canceled);
                        }
                        catch (OperationCanceledException) when(workItem.CTS.Token.IsCancellationRequested)
                        {
                            // We triggered the cancellation.
                            Interlocked.Increment(ref _stats._canceled);
                        }
                        catch (Exception ex)
                        {
                            Interlocked.Increment(ref _stats._failed);

                            if (_exceptionHandler == null)
                            {
                                lock (_syncRoot)
                                {
                                    unhandledExceptions.Add(ex);
                                }
                            }
                            else
                            {
                                try
                                {
                                    ExceptionResolution r = await _exceptionHandler(item, ex, workItem.CTS.Token).ConfigureAwait(false);

                                    Interlocked.Increment(ref _stats._exceptionsCaught);

                                    lock (_syncRoot)
                                    {
                                        resolution = (ExceptionResolution)Math.Max((int)resolution, (int)r);
                                    }

                                    if (r == ExceptionResolution.Swallow)
                                    {
                                        Interlocked.Increment(ref _stats._exceptionsSwallowed);
                                    }
                                }
                                catch (OperationCanceledException) when(workItem.CTS.Token.IsCancellationRequested)
                                {
                                    // Swallowed because the iteration is shutting down.
                                }
                                catch (Exception resEx)
                                {
                                    lock (_syncRoot)
                                    {
                                        unhandledExceptions.Add(resEx);
                                    }
                                }
                            }
                        }
                        finally
                        {
                            // Some probably avoidable heavy stuff.
                            lock (_syncRoot)
                            {
                                _currentTasks.Remove(workItem);
                                workItem.GateReleaser.Dispose();
                                workItem.CTS.Dispose();
                            }
                        }
                    }, token);

                    token.ThrowIfCancellationRequested();

                    if (unhandledExceptions.Count > 0 || resolution != ExceptionResolution.Swallow)
                    {
                        break;
                    }
                }

                #endregion

                token.ThrowIfCancellationRequested();

                #region Dealing with uncaught exceptions

                lock (_syncRoot)
                {
                    if (unhandledExceptions.Count > 0)
                    {
                        throw new AggregateException(unhandledExceptions);
                    }
                }

                #endregion

                // Copy the value so that Exceptions thrown within processors do not change this value.
                ExceptionResolution finalResolution = resolution;

                #region Dealing with caught exceptions

                if (finalResolution != ExceptionResolution.Swallow)
                {
                    if (finalResolution != ExceptionResolution.Abandon)
                    {
                        if (finalResolution != ExceptionResolution.SoftStop)
                        {
                            lock (_syncRoot)
                            {
                                foreach (WorkItem workItem in _currentTasks.ToArray())
                                {
                                    workItem.CTS.Cancel();
                                }
                            }
                        }

                        if (finalResolution != ExceptionResolution.Forget)
                        {
                            await AwaitRemainingTasks(token).ConfigureAwait(false);
                        }
                    }

                    lock (_syncRoot)
                    {
                        if (unhandledExceptions.Count > 0)
                        {
                            throw new AggregateException(unhandledExceptions);
                        }
                    }

                    return(RunResult.Interrupted);
                }

                #endregion

                await AwaitRemainingTasks(token).ConfigureAwait(false);
            }
            finally
            {
                _isRunning = false;
            }

            return(RunResult.Finished);
        }