Try your search with a different keyword or use * as a wildcard.
using Microsoft.Extensions.Caching.Memory;
namespace Nop.Core.Caching;
///
/// A distributed cache manager that locks the acquisition task
///
public partial class MemoryCacheLocker : ILocker
{
#region Fields
protected readonly IMemoryCache _memoryCache;
#endregion
#region Ctor
public MemoryCacheLocker(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
#endregion
#region Utilities
///
/// Run action
///
/// The key of the background task
/// The time after which the lock will automatically be expired
/// The action to perform
/// A CancellationTokenSource for manually canceling the task
///
protected virtual async Task RunAsync(string key, TimeSpan? expirationTime, Func action, CancellationTokenSource cancellationTokenSource = default)
{
var started = false;
try
{
var tokenSource = _memoryCache.GetOrCreate(key, entry => new Lazy(() =>
{
entry.AbsoluteExpirationRelativeToNow = expirationTime;
entry.SetPriority(CacheItemPriority.NeverRemove);
started = true;
return cancellationTokenSource ?? new CancellationTokenSource();
}, true))?.Value;
if (tokenSource != null && started)
await action(tokenSource.Token);
}
catch (OperationCanceledException) { }
finally
{
if (started)
_memoryCache.Remove(key);
}
return started;
}
#endregion
#region Methods
///
/// Performs some asynchronous task with exclusive lock
///
/// The key we are locking on
/// The time after which the lock will automatically be expired
/// Asynchronous task to be performed with locking
/// A task that resolves true if lock was acquired and action was performed; otherwise false
public async Task PerformActionWithLockAsync(string resource, TimeSpan expirationTime, Func action)
{
return await RunAsync(resource, expirationTime, _ => action());
}
///
/// Starts a background task with "heartbeat": a status flag that will be periodically updated to signal to
/// others that the task is running and stop them from starting the same task.
///
/// The key of the background task
/// The time after which the heartbeat key will automatically be expired. Should be longer than
/// The interval at which to update the heartbeat, if required by the implementation
/// Asynchronous background task to be performed
/// A CancellationTokenSource for manually canceling the task
/// A task that resolves true if lock was acquired and action was performed; otherwise false
public async Task RunWithHeartbeatAsync(string key, TimeSpan expirationTime, TimeSpan heartbeatInterval, Func action, CancellationTokenSource cancellationTokenSource = default)
{
// We ignore expirationTime and heartbeatInterval here, as the cache is not shared with other instances,
// and will be cleared on system failure anyway. The task is guaranteed to still be running as long as it is in the cache.
await RunAsync(key, null, action, cancellationTokenSource);
}
///
/// Tries to cancel a background task by flagging it for cancellation on the next heartbeat.
///
/// The task's key
/// The time after which the task will be considered stopped due to system shutdown or other causes,
/// even if not explicitly canceled.
/// A task that represents requesting cancellation of the task. Note that the completion of this task does not
/// necessarily imply that the task has been canceled, only that cancellation has been requested.
public Task CancelTaskAsync(string key, TimeSpan expirationTime)
{
if (_memoryCache.TryGetValue(key, out Lazy tokenSource))
tokenSource.Value.Cancel();
return Task.CompletedTask;
}
///
/// Check if a background task is running.
///
/// The task's key
/// A task that resolves to true if the background task is running; otherwise false
public Task IsTaskRunningAsync(string key)
{
return Task.FromResult(_memoryCache.TryGetValue(key, out _));
}
#endregion
}