Example #1
0
        internal static async Task <TResult> ImplementationAsync <TResult>(
            Func <Context, CancellationToken, Task <TResult> > action,
            Context context,
            CancellationToken cancellationToken,
            bool continueOnCapturedContext,
            IKeyStrategy keyStrategy,
            ConcurrentDictionary <string, Lazy <Task <object> > > collapser,
            IAsyncLockProvider lockProvider)
        {
            cancellationToken.ThrowIfCancellationRequested();

            string key = keyStrategy.GetKey(context);

            // Fast-path if no key specified on Context (similar to CachePolicy).
            if (key == null)
            {
                return(await action(context, cancellationToken).ConfigureAwait(continueOnCapturedContext));
            }

            Lazy <Task <object> > lazy;

            await using (lockProvider.AcquireLockAsync(key, context, cancellationToken, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext))
            {
                lazy = collapser.GetOrAdd(key, new Lazy <Task <object> >(async() => await action(context, cancellationToken).ConfigureAwait(continueOnCapturedContext), LazyThreadSafetyMode.ExecutionAndPublication)); // Note: per documentation, LazyThreadSafetyMode.ExecutionAndPublication guarantees single execution, but means the executed code must not lock, as this risks deadlocks.  We should document.
            }

            try
            {
                return((TResult)await lazy.Value.ConfigureAwait(continueOnCapturedContext));
            }
            finally
            {
                // As soon as the lazy has returned a result to one thread, the concurrent request set is over, so we evict the lazy from the ConcurrentDictionary.
                // We need to evict within a lock, to be sure we are not, due to potential race with new threads populating, evicting a different lazy created by a different thread.
                // To reduce lock contention, first check outside the lock whether we still need to remove it (we will double-check inside the lock).
                if (collapser.TryGetValue(key, out Lazy <Task <object> > currentValue))
                {
                    if (currentValue == lazy)
                    {
                        await using (lockProvider.AcquireLockAsync(key, context, cancellationToken, continueOnCapturedContext)
                                     .ConfigureAwait(continueOnCapturedContext))
                        {
                            // Double-check that there has not been a race which updated the dictionary with a new value.
                            if (collapser.TryGetValue(key, out Lazy <Task <object> > valueWithinLock))
                            {
                                if (valueWithinLock == lazy)
                                {
                                    collapser.TryRemove(key, out _);
                                }
                            }
                        }
                    }
                }
            }
        }
Example #2
0
        private void QueueTasks(int parallelism, bool useCollapser, bool sameKey, IKeyStrategy overrideKeyStrategy = null)
        {
            IsPolicy policy = GetPolicy(useCollapser, overrideKeyStrategy);

            for (int i = 0; i < parallelism; i++)
            {
                string  key     = sameKey ? SharedKey : i.ToString();
                Context context = new Context(key);

                ConcurrentTasks[i] = ExecuteThroughPolicy(policy, context, i, true);
            }
        }
Example #3
0
        /// <summary>
        /// Creates a new <see cref="Polly.Contrib.DuplicateRequestCollapser.AsyncRequestCollapserPolicy{TResult}"/> policy, using the supplied <see cref="IKeyStrategy"/> and <see cref="IAsyncLockProvider"/>
        /// </summary>
        /// <param name="keyStrategy">A strategy for choosing a key on which to consider requests duplicates.</param>
        /// <param name="lockProvider">The lock provider.</param>
        /// <returns>The policy instance.</returns>
        public static IAsyncRequestCollapserPolicy <TResult> Create(IKeyStrategy keyStrategy, IAsyncLockProvider lockProvider)
        {
            if (keyStrategy == null)
            {
                throw new ArgumentNullException(nameof(keyStrategy));
            }
            if (lockProvider == null)
            {
                throw new ArgumentNullException(nameof(lockProvider));
            }

            return(new AsyncRequestCollapserPolicy <TResult>(keyStrategy, lockProvider));
        }
Example #4
0
 protected abstract IsPolicy GetPolicy(bool useCollapser, IKeyStrategy overrideKeyStrategy = null, ISyncLockProvider lockProvider = null);
Example #5
0
        protected (int actualExecutions, Task[] tasks) Execute_parallel_delegates_through_policy_with_key_strategy(int parallelism, bool useCollapser, bool sameKey, IKeyStrategy overrideKeyStrategy = null)
        {
            ConcurrentTasks = new Task[parallelism];

            testOutputHelper.WriteLine("Queueing work.");
            QueueTasks(parallelism, useCollapser, sameKey, overrideKeyStrategy);

            testOutputHelper.WriteLine("Waiting for all queued work to start and reach the holding gate.");
            WaitForAllTasksToBeStarted(parallelism);
            Thread.Sleep(BlockWaitToCauseBlockingConcurrency);

            // Release the parallel contention.
            testOutputHelper.WriteLine("All tasks started. Releasing holding gate.");
            ReleaseHoldingGate();

            // Wait for task completion.
            Task.WaitAll(ConcurrentTasks);
            testOutputHelper.WriteLine("All tasks completed.");

            // Return results to caller; the caller is responsible for asserting.
            return(ActualExecutions, ConcurrentTasks);
        }
Example #6
0
 protected override IsPolicy GetPolicy(bool useCollapser, IKeyStrategy overrideKeyStrategy = null, ISyncLockProvider lockProvider = null)
 {
     return(useCollapser ?
            AsyncRequestCollapserPolicy <ResultClass> .Create(overrideKeyStrategy ?? RequestCollapserPolicy.DefaultKeyStrategy, new AsyncWrapperLockProvider(lockProvider ?? RequestCollapserPolicy.GetDefaultLockProvider()))
         : (IAsyncPolicy <ResultClass>)Policy.NoOpAsync <ResultClass>());
 }
Example #7
0
 /// <summary>
 /// Creates a new <see cref="Polly.Contrib.DuplicateRequestCollapser.AsyncRequestCollapserPolicy{TResult}"/> policy, using the supplied <see cref="IKeyStrategy"/> and <see cref="ISyncLockProvider"/>
 /// </summary>
 /// <param name="keyStrategy">A strategy for choosing a key on which to consider requests duplicates.</param>
 /// <param name="lockProvider">The lock provider.</param>
 /// <returns>The policy instance.</returns>
 public static IAsyncRequestCollapserPolicy <TResult> Create(IKeyStrategy keyStrategy, ISyncLockProvider lockProvider)
 => Create(keyStrategy, new AsyncWrapperLockProvider(lockProvider));
Example #8
0
 /// <summary>
 /// Creates a new <see cref="Polly.Contrib.DuplicateRequestCollapser.AsyncRequestCollapserPolicy{TResult}"/> policy, using the supplied <see cref="IKeyStrategy"/>
 /// </summary>
 /// <param name="keyStrategy">A strategy for choosing a key on which to consider requests duplicates.</param>
 /// <returns>The policy instance.</returns>
 public static IAsyncRequestCollapserPolicy <TResult> Create(IKeyStrategy keyStrategy)
 => Create(keyStrategy, AsyncRequestCollapserPolicy.GetDefaultLockProvider());
 internal AsyncRequestCollapserPolicy(IKeyStrategy keyStrategy, IAsyncLockProvider lockProvider)
 {
     _keyStrategy  = keyStrategy ?? throw new ArgumentNullException(nameof(keyStrategy));
     _lockProvider = lockProvider ?? throw new ArgumentNullException(nameof(lockProvider));
 }
 /// <summary>
 /// Creates a new <see cref="Polly.Contrib.DuplicateRequestCollapser.RequestCollapserPolicy"/> policy, using the supplied <see cref="IKeyStrategy"/>
 /// </summary>
 /// <param name="keyStrategy">A strategy for choosing a key on which to consider requests duplicates.</param>
 /// <returns>The policy instance.</returns>
 public static ISyncRequestCollapserPolicy Create(IKeyStrategy keyStrategy)
 => Create(keyStrategy, RequestCollapserPolicy.GetDefaultLockProvider());
 protected override IsPolicy GetPolicy(bool useCollapser, IKeyStrategy overrideKeyStrategy = null, ISyncLockProvider lockProvider = null)
 {
     return(useCollapser ?
            RequestCollapserPolicy.Create(overrideKeyStrategy ?? RequestCollapserPolicy.DefaultKeyStrategy, lockProvider ?? RequestCollapserPolicy.GetDefaultLockProvider())
         : (ISyncPolicy)Policy.NoOp());
 }