using System.Collections.Concurrent; using System.Threading; namespace Shared.Engine { public class SemaphorManager { #region static private static readonly ConcurrentDictionary _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(); 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 func) { try { await semaphore.WaitAsync(cancellationToken); await func(); } finally { semaphore.Release(); } } async public Task Invoke(Func func) { try { await semaphore.WaitAsync(cancellationToken); return func(); } finally { semaphore.Release(); } } async public Task Invoke(Func> 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 } }