namespace Zcbot.WindowsNode; internal sealed class JobExecutionGate : IDisposable { private readonly SemaphoreSlim gate = new(1, 1); private int activeCount; internal bool IsActive => Volatile.Read(ref activeCount) > 0; internal async Task EnterAsync(CancellationToken cancellationToken) { await gate.WaitAsync(cancellationToken); Interlocked.Increment(ref activeCount); return new Lease(this); } public void Dispose() => gate.Dispose(); private void Release() { Interlocked.Decrement(ref activeCount); gate.Release(); } private sealed class Lease(JobExecutionGate owner) : IDisposable { private JobExecutionGate? current = owner; public void Dispose() => Interlocked.Exchange(ref current, null)?.Release(); } }