private bool TryOffset(int releaseCount, out int previous, SemaphoreSlimState state) { var expected = Volatile.Read(ref _count); previous = expected; if (_maxCount.HasValue && expected + (long)releaseCount > (long)_maxCount) { throw new SemaphoreFullException(); } var result = expected + releaseCount; if (result < 0) { return(false); } var found = Interlocked.CompareExchange(ref _count, result, expected); if (found != expected) { return(false); } SyncWaitHandle(state); return(true); }
private void Awake(SemaphoreSlimState state) { var spinWait = new SpinWait(); while (state.AsyncWaiters.TryTake(out var waiter)) { if (waiter.Task.IsCompleted) { // Skip - either canceled or timed out continue; } if (TryOffset(-1, out _, state)) { waiter.SetResult(true); } else { // Add it back state.AsyncWaiters.Add(waiter); break; } spinWait.SpinOnce(); } }
private void SyncWaitHandle(SemaphoreSlimState state) { var awake = false; if (Volatile.Read(ref _count) == 0 == state.CanEnter.IsSet && Interlocked.CompareExchange(ref _syncRoot, 1, 0) == 0) { try { awake = SyncWaitHandleExtracted(); } finally { Volatile.Write(ref _syncRoot, 0); } } if (awake) { ThreadPool.QueueUserWorkItem(_ => Awake(state)); } bool SyncWaitHandleExtracted() { int found; var canEnter = state.CanEnter; if ((found = Volatile.Read(ref _count)) == 0 != canEnter.IsSet) { return(false); } if (found == 0) { canEnter.Reset(); } else { canEnter.Set(); return(true); } return(false); } }