195 lines
5.0 KiB
C#
195 lines
5.0 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Threading;
|
|
|
|
namespace Shared.Engine
|
|
{
|
|
public class SemaphorManager
|
|
{
|
|
#region static
|
|
private static readonly ConcurrentDictionary<string, SemaphoreEntry> _semaphoreLocks = new();
|
|
private static readonly Timer _cleanupTimer = new(_ => Cleanup(), null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
|
|
|
|
public static int Stat_ContSemaphoreLocks => _semaphoreLocks.IsEmpty ? 0 : _semaphoreLocks.Count;
|
|
|
|
static int _updatingDb = 0;
|
|
static void Cleanup()
|
|
{
|
|
if (Interlocked.Exchange(ref _updatingDb, 1) == 1)
|
|
return;
|
|
|
|
try
|
|
{
|
|
var deletes = new List<string>();
|
|
var threshold = DateTime.UtcNow - TimeSpan.FromSeconds(90);
|
|
|
|
foreach (var kvp in _semaphoreLocks)
|
|
{
|
|
if (kvp.Value.LastUsed < threshold && _semaphoreLocks.TryRemove(kvp.Key, out var removed))
|
|
removed.Dispose();
|
|
}
|
|
}
|
|
catch { }
|
|
finally
|
|
{
|
|
Volatile.Write(ref _updatingDb, 0);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
SemaphoreEntry semaphore { get; set; }
|
|
CancellationToken cancellationToken;
|
|
|
|
bool regwait, releaseLock;
|
|
|
|
|
|
public SemaphorManager(string key)
|
|
{
|
|
cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(40)).Token;
|
|
semaphore = _semaphoreLocks.GetOrAdd(key, _ => new SemaphoreEntry(new SemaphoreSlim(1, 1)));
|
|
}
|
|
|
|
public SemaphorManager(string key, TimeSpan timeSpan)
|
|
{
|
|
cancellationToken = new CancellationTokenSource(timeSpan).Token;
|
|
semaphore = _semaphoreLocks.GetOrAdd(key, _ => new SemaphoreEntry(new SemaphoreSlim(1, 1)));
|
|
}
|
|
|
|
public SemaphorManager(string key, CancellationToken cancellationToken)
|
|
{
|
|
this.cancellationToken = cancellationToken;
|
|
semaphore = _semaphoreLocks.GetOrAdd(key, _ => new SemaphoreEntry(new SemaphoreSlim(1, 1)));
|
|
}
|
|
|
|
|
|
public Task WaitAsync(TimeSpan timeSpan)
|
|
{
|
|
regwait = true;
|
|
return semaphore.WaitAsync(timeSpan);
|
|
}
|
|
|
|
public Task WaitAsync(CancellationToken cancellationToken)
|
|
{
|
|
regwait = true;
|
|
return semaphore.WaitAsync(cancellationToken);
|
|
}
|
|
|
|
public Task WaitAsync()
|
|
{
|
|
regwait = true;
|
|
return semaphore.WaitAsync(cancellationToken);
|
|
}
|
|
|
|
|
|
public void Release()
|
|
{
|
|
try
|
|
{
|
|
if (regwait && releaseLock == false)
|
|
{
|
|
releaseLock = true;
|
|
semaphore.Release();
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
|
|
async public Task Invoke(Action action)
|
|
{
|
|
try
|
|
{
|
|
await semaphore.WaitAsync(cancellationToken);
|
|
action();
|
|
}
|
|
finally
|
|
{
|
|
semaphore.Release();
|
|
}
|
|
}
|
|
|
|
async public Task Invoke(Func<Task> func)
|
|
{
|
|
try
|
|
{
|
|
await semaphore.WaitAsync(cancellationToken);
|
|
await func();
|
|
}
|
|
finally
|
|
{
|
|
semaphore.Release();
|
|
}
|
|
}
|
|
|
|
|
|
async public Task<T> Invoke<T>(Func<T> func)
|
|
{
|
|
try
|
|
{
|
|
await semaphore.WaitAsync(cancellationToken);
|
|
return func();
|
|
}
|
|
finally
|
|
{
|
|
semaphore.Release();
|
|
}
|
|
}
|
|
|
|
async public Task<T> Invoke<T>(Func<Task<T>> func)
|
|
{
|
|
try
|
|
{
|
|
await semaphore.WaitAsync(cancellationToken);
|
|
return await func();
|
|
}
|
|
finally
|
|
{
|
|
semaphore.Release();
|
|
}
|
|
}
|
|
|
|
|
|
#region SemaphoreEntry
|
|
private sealed class SemaphoreEntry : IDisposable
|
|
{
|
|
private readonly SemaphoreSlim _semaphore;
|
|
|
|
public SemaphoreEntry(SemaphoreSlim semaphore)
|
|
{
|
|
_semaphore = semaphore;
|
|
Touch();
|
|
}
|
|
|
|
public DateTime LastUsed { get; private set; }
|
|
|
|
public void Touch()
|
|
{
|
|
LastUsed = DateTime.UtcNow;
|
|
}
|
|
|
|
public Task WaitAsync(TimeSpan timeSpan)
|
|
{
|
|
Touch();
|
|
return _semaphore.WaitAsync(timeSpan);
|
|
}
|
|
|
|
public Task WaitAsync(CancellationToken cancellationToken)
|
|
{
|
|
Touch();
|
|
return _semaphore.WaitAsync(cancellationToken);
|
|
}
|
|
|
|
public void Release()
|
|
{
|
|
Touch();
|
|
_semaphore.Release();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_semaphore.Dispose();
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|