From 1dfa805f311aa31d22cf08ea2f68deba7f21d9ec Mon Sep 17 00:00:00 2001 From: Nedyalko Dyakov Date: Mon, 10 Nov 2025 03:21:36 +0200 Subject: [PATCH] fix fast semaphore that could have been starved --- internal/semaphore.go | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/internal/semaphore.go b/internal/semaphore.go index 091b6635..f00b3a4b 100644 --- a/internal/semaphore.go +++ b/internal/semaphore.go @@ -89,8 +89,6 @@ func (s *FastSemaphore) Acquire(ctx context.Context, timeout time.Duration, time defer semTimers.Put(timer) timer.Reset(timeout) - start := time.Now() - for { select { case <-ctx.Done(): @@ -100,23 +98,15 @@ func (s *FastSemaphore) Acquire(ctx context.Context, timeout time.Duration, time return ctx.Err() case <-s.waitCh: - // Someone released a token, try to acquire it - if s.TryAcquire() { - if !timer.Stop() { - <-timer.C - } - return nil + // Someone released a token, we got the spot + // no need to touch the counter + if !timer.Stop() { + <-timer.C } - // Failed to acquire (race with another goroutine), continue waiting - + return nil case <-timer.C: return timeoutErr } - - // Periodically check if we can acquire (handles race conditions) - if time.Since(start) > timeout { - return timeoutErr - } } } @@ -132,25 +122,26 @@ func (s *FastSemaphore) AcquireBlocking() { // Slow path: wait for a token for { <-s.waitCh - if s.TryAcquire() { - return - } - // Failed to acquire (race with another goroutine), continue waiting + // Someone released a token, we got the spot + // no need to touch the counter + return } } // Release releases a token back to the semaphore. // This wakes up one waiting goroutine if any are blocked. func (s *FastSemaphore) Release() { - s.count.Add(-1) // Try to wake up a waiter (non-blocking) // If no one is waiting, this is a no-op select { case s.waitCh <- struct{}{}: // Successfully notified a waiter + // no need to decrement the counter, the waiter will use this spot default: // No waiters, that's fine + // decrement the counter + s.count.Add(-1) } }