// --------------------------------------------------------------------------------------------------------------------
//
// 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);
}
}
}
}