// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) James South. // Licensed under the Apache License, Version 2.0. // // // Throttles duplicate requests. // // -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Web.Helpers { using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; /// /// Throttles duplicate requests. /// Based loosely on /// public sealed class AsyncDeDuperLock { /// /// The semaphore slims. /// private static readonly ConcurrentDictionary SemaphoreSlims = new ConcurrentDictionary(); /// /// The lock. /// /// /// The hash. /// /// /// The . /// public IDisposable Lock(string key) { DisposableScope releaser = new DisposableScope( key, s => { SemaphoreSlim locker; if (SemaphoreSlims.TryRemove(s, out locker)) { locker.Release(); locker.Dispose(); } }); SemaphoreSlim semaphore = SemaphoreSlims.GetOrAdd(key, new SemaphoreSlim(1, 1)); semaphore.Wait(); return releaser; } #if NET45 && !__MonoCS__ /// /// The lock async. /// /// /// The key. /// /// /// The . /// public Task LockAsync(string key) { DisposableScope releaser = new DisposableScope( key, s => { SemaphoreSlim locker; if (SemaphoreSlims.TryRemove(s, out locker)) { locker.Release(); locker.Dispose(); } }); Task releaserTask = Task.FromResult(releaser as IDisposable); SemaphoreSlim semaphore = SemaphoreSlims.GetOrAdd(key, new SemaphoreSlim(1, 1)); Task waitTask = semaphore.WaitAsync(); return waitTask.IsCompleted ? releaserTask : waitTask.ContinueWith( (_, r) => (IDisposable)r, releaser, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } #endif /// /// The disposable scope. /// internal sealed class DisposableScope : IDisposable { /// /// The key /// private readonly string key; /// /// The close scope action. /// private readonly Action closeScopeAction; /// /// Initializes a new instance of the class. /// /// /// The key. /// /// /// The close scope action. /// public DisposableScope(string key, Action closeScopeAction) { this.key = key; this.closeScopeAction = closeScopeAction; } /// /// The dispose. /// public void Dispose() { this.closeScopeAction(this.key); } } } }