PoolingSemaphore optimized.

This commit is contained in:
mlemanczyk committed 2025-12-11 21:46:07 +01:00
1 parent 60ccb3db74
commit 8f0cc17d90
1 file changed
+63 -75
+63 -75
View File
@@ -9,95 +9,83 @@ namespace PerfectNumbers.Core;
/// </summary>
public sealed class PollingSemaphore
{
private readonly int _maximumConcurrency;
private readonly TimeSpan _pauseBetweenChecks;
private readonly bool _hasPause;
private int _currentHolders;
private readonly int _maximumConcurrency;
private readonly TimeSpan _pauseBetweenChecks;
private readonly bool _hasPause;
private int _currentHolders;
public PollingSemaphore(int maximumConcurrency, TimeSpan pauseBetweenChecks)
{
if (maximumConcurrency <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maximumConcurrency));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public PollingSemaphore(int maximumConcurrency, TimeSpan pauseBetweenChecks)
{
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(maximumConcurrency, 0);
ArgumentOutOfRangeException.ThrowIfLessThan(pauseBetweenChecks, TimeSpan.Zero);
if (pauseBetweenChecks < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(pauseBetweenChecks));
}
_maximumConcurrency = maximumConcurrency;
_pauseBetweenChecks = pauseBetweenChecks;
_hasPause = pauseBetweenChecks > TimeSpan.Zero;
}
_maximumConcurrency = maximumConcurrency;
_pauseBetweenChecks = pauseBetweenChecks;
_hasPause = pauseBetweenChecks > TimeSpan.Zero;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool TryEnter()
{
int newValue = Interlocked.Increment(ref _currentHolders);
if (newValue <= _maximumConcurrency)
{
return true;
}
public bool TryEnter()
{
int newValue = Interlocked.Increment(ref _currentHolders);
Interlocked.Decrement(ref _currentHolders);
return false;
}
if (newValue <= _maximumConcurrency)
{
return true;
}
Interlocked.Decrement(ref _currentHolders);
return false;
}
public bool Wait(TimeSpan timeout, CancellationToken cancellationToken = default)
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool Wait(TimeSpan timeout, CancellationToken cancellationToken = default)
{
ArgumentOutOfRangeException.ThrowIfLessThan(timeout, Timeout.InfiniteTimeSpan);
Stopwatch? stopwatch = timeout == Timeout.InfiniteTimeSpan ? null : Stopwatch.StartNew();
SpinWait spinner = new();
bool hasPause = _hasPause;
bool hasStopwatch = timeout != Timeout.InfiniteTimeSpan;
SpinWait spinner = new();
TimeSpan pauseBetweenChecks = _pauseBetweenChecks;
Stopwatch? stopwatch = hasStopwatch ? Stopwatch.StartNew() : null;
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
if (TryEnter())
{
return true;
}
if (TryEnter())
{
return true;
}
if (hasStopwatch && stopwatch!.Elapsed >= timeout)
{
return false;
}
if (stopwatch is not null && stopwatch.Elapsed >= timeout)
{
return false;
}
if (spinner.Count < 10)
{
spinner.SpinOnce();
continue;
}
if (spinner.Count < 10)
{
spinner.SpinOnce();
continue;
}
if (hasPause)
{
Thread.Sleep(pauseBetweenChecks);
spinner.Reset();
}
else
{
Thread.Yield();
}
}
}
if (_hasPause)
{
Thread.Sleep(_pauseBetweenChecks);
spinner.Reset();
}
else
{
Thread.Yield();
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool Wait(CancellationToken cancellationToken) => Wait(Timeout.InfiniteTimeSpan, cancellationToken);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool Wait() => Wait(Timeout.InfiniteTimeSpan, CancellationToken.None);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Release()
{
int newValue = Interlocked.Decrement(ref _currentHolders);
if (newValue < 0)
{
Interlocked.Increment(ref _currentHolders);
throw new SynchronizationLockException("Release called more times than Wait.");
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Release() => _ = Interlocked.Decrement(ref _currentHolders);
}