mirror of https://github.com/SixLabors/ImageSharp
4 changed files with 144 additions and 34 deletions
@ -0,0 +1,134 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="AsyncDeDuperLock.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Throttles duplicate requests.
|
|||
// </summary>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
namespace ImageProcessor.Web.Helpers |
|||
{ |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
/// <summary>
|
|||
/// Throttles duplicate requests.
|
|||
/// Based loosely on <see href="http://stackoverflow.com/a/21011273/427899"/>
|
|||
/// </summary>
|
|||
public sealed class AsyncDeDuperLock |
|||
{ |
|||
/// <summary>
|
|||
/// The semaphore slims.
|
|||
/// </summary>
|
|||
private static readonly ConcurrentDictionary<string, SemaphoreSlim> SemaphoreSlims = new ConcurrentDictionary<string, SemaphoreSlim>(); |
|||
|
|||
/// <summary>
|
|||
/// The lock.
|
|||
/// </summary>
|
|||
/// <param name="key">
|
|||
/// The hash.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The <see cref="IDisposable"/>.
|
|||
/// </returns>
|
|||
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__
|
|||
/// <summary>
|
|||
/// The lock async.
|
|||
/// </summary>
|
|||
/// <param name="key">
|
|||
/// The key.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The <see cref="Task"/>.
|
|||
/// </returns>
|
|||
public Task<IDisposable> LockAsync(string key) |
|||
{ |
|||
DisposableScope releaser = new DisposableScope( |
|||
key, |
|||
s => |
|||
{ |
|||
SemaphoreSlim locker; |
|||
if (SemaphoreSlims.TryRemove(s, out locker)) |
|||
{ |
|||
locker.Release(); |
|||
locker.Dispose(); |
|||
} |
|||
}); |
|||
|
|||
Task<IDisposable> 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
|
|||
/// <summary>
|
|||
/// The disposable scope.
|
|||
/// </summary>
|
|||
internal sealed class DisposableScope : IDisposable |
|||
{ |
|||
/// <summary>
|
|||
/// The key
|
|||
/// </summary>
|
|||
private readonly string key; |
|||
|
|||
/// <summary>
|
|||
/// The close scope action.
|
|||
/// </summary>
|
|||
private readonly Action<string> closeScopeAction; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DisposableScope"/> class.
|
|||
/// </summary>
|
|||
/// <param name="key">
|
|||
/// The key.
|
|||
/// </param>
|
|||
/// <param name="closeScopeAction">
|
|||
/// The close scope action.
|
|||
/// </param>
|
|||
public DisposableScope(string key, Action<string> closeScopeAction) |
|||
{ |
|||
this.key = key; |
|||
this.closeScopeAction = closeScopeAction; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The dispose.
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
this.closeScopeAction(this.key); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue