diff --git a/errgroup/errgroup.go b/errgroup/errgroup.go index f69fd75..c261a8e 100644 --- a/errgroup/errgroup.go +++ b/errgroup/errgroup.go
@@ -109,7 +109,7 @@ if g.sem != nil { select { case g.sem <- token{}: - // Note: this allows barging iff channels in general allow barging. + // Note: this allows barging if and only if channels in general allow barging. default: return false }
diff --git a/errgroup/errgroup_test.go b/errgroup/errgroup_test.go index 85008eb..3b563af 100644 --- a/errgroup/errgroup_test.go +++ b/errgroup/errgroup_test.go
@@ -34,7 +34,7 @@ // JustErrors illustrates the use of a Group in place of a sync.WaitGroup to // simplify goroutine counting and error handling. This example is derived from -// the sync.WaitGroup example at https://golang.org/pkg/sync/#example_WaitGroup. +// the sync.WaitGroup example at https://golang.org/pkg/sync/#example-WaitGroup. func ExampleGroup_justErrors() { g := new(errgroup.Group) var urls = []string{
diff --git a/semaphore/semaphore.go b/semaphore/semaphore.go index b618162..96a035a 100644 --- a/semaphore/semaphore.go +++ b/semaphore/semaphore.go
@@ -24,7 +24,7 @@ } // Weighted provides a way to bound concurrent access to a resource. -// The callers can request access with a given weight. +// The callers can request access with a given non-negative weight. type Weighted struct { size int64 cur int64 @@ -32,10 +32,13 @@ waiters list.List } -// Acquire acquires the semaphore with a weight of n, blocking until resources +// Acquire acquires the semaphore with a non-negative weight of n, blocking until resources // are available or ctx is done. On success, returns nil. On failure, returns // ctx.Err() and leaves the semaphore unchanged. func (s *Weighted) Acquire(ctx context.Context, n int64) error { + if n < 0 { + panic("semaphore: n < 0") + } done := ctx.Done() s.mu.Lock() @@ -83,7 +86,7 @@ default: isFront := s.waiters.Front() == elem s.waiters.Remove(elem) - // If we're at the front and there're extra tokens left, notify other waiters. + // If we're at the front and there are extra tokens left, notify other waiters. if isFront && s.size > s.cur { s.notifyWaiters() } @@ -106,9 +109,12 @@ } } -// TryAcquire acquires the semaphore with a weight of n without blocking. +// TryAcquire acquires the semaphore with a non-negative weight of n without blocking. // On success, returns true. On failure, returns false and leaves the semaphore unchanged. func (s *Weighted) TryAcquire(n int64) bool { + if n < 0 { + panic("semaphore: n < 0") + } s.mu.Lock() success := s.size-s.cur >= n && s.waiters.Len() == 0 if success { @@ -118,8 +124,11 @@ return success } -// Release releases the semaphore with a weight of n. +// Release releases the semaphore with a non-negative weight of n. func (s *Weighted) Release(n int64) { + if n < 0 { + panic("semaphore: n < 0") + } s.mu.Lock() s.cur -= n if s.cur < 0 { @@ -139,15 +148,15 @@ w := next.Value.(waiter) if s.size-s.cur < w.n { - // Not enough tokens for the next waiter. We could keep going (to try to + // Not enough tokens for the next waiter. We could keep going (to try to // find a waiter with a smaller request), but under load that could cause // starvation for large requests; instead, we leave all remaining waiters // blocked. // // Consider a semaphore used as a read-write lock, with N tokens, N - // readers, and one writer. Each reader can Acquire(1) to obtain a read - // lock. The writer can Acquire(N) to obtain a write lock, excluding all - // of the readers. If we allow the readers to jump ahead in the queue, + // readers, and one writer. Each reader can Acquire(1) to obtain a read + // lock. The writer can Acquire(N) to obtain a write lock, excluding all + // of the readers. If we allow the readers to jump ahead in the queue, // the writer will starve — there is always one token available for every // reader. break
diff --git a/semaphore/semaphore_bench_test.go b/semaphore/semaphore_bench_test.go index aa64258..395bbf5 100644 --- a/semaphore/semaphore_bench_test.go +++ b/semaphore/semaphore_bench_test.go
@@ -12,7 +12,7 @@ "golang.org/x/sync/semaphore" ) -// weighted is an interface matching a subset of *Weighted. It allows +// weighted is an interface matching a subset of *Weighted. It allows // alternate implementations for testing and benchmarking. type weighted interface { Acquire(context.Context, int64) error
diff --git a/semaphore/semaphore_test.go b/semaphore/semaphore_test.go index 61012d6..1a2eb7a 100644 --- a/semaphore/semaphore_test.go +++ b/semaphore/semaphore_test.go
@@ -56,6 +56,40 @@ w.Release(1) } +func TestWeightedNegativeWeightPanic(t *testing.T) { + t.Parallel() + + ctx := context.Background() + w := semaphore.NewWeighted(1) + + func() { + defer func() { + if recover() == nil { + t.Fatal("Acquire with negative weight did not panic") + } + }() + w.Acquire(ctx, -1) + }() + + func() { + defer func() { + if recover() == nil { + t.Fatal("TryAcquire with negative weight did not panic") + } + }() + w.TryAcquire(-1) + }() + + func() { + defer func() { + if recover() == nil { + t.Fatal("Release with negative weight did not panic") + } + }() + w.Release(-1) + }() +} + func TestWeightedTryAcquire(t *testing.T) { t.Parallel() @@ -217,7 +251,7 @@ sem.Release(1) close(ch) }() - // Since the context closing happens before enough tokens become available, + // Since the context being closed happens before enough tokens become available, // this Acquire must fail. if err := sem.Acquire(ctx, 2); err != context.Canceled { t.Errorf("Acquire with canceled context returned wrong error: want context.Canceled, got %v", err)
diff --git a/singleflight/singleflight.go b/singleflight/singleflight.go index 90ca138..32e40bd 100644 --- a/singleflight/singleflight.go +++ b/singleflight/singleflight.go
@@ -15,12 +15,12 @@ "sync" ) -// errGoexit indicates the runtime.Goexit was called in -// the user given function. +// errGoexit indicates runtime.Goexit was called in +// the user-given function. var errGoexit = errors.New("runtime.Goexit was called") // A panicError is an arbitrary value recovered from a panic -// with the stack trace during the execution of given function. +// with the stack trace during the execution of the given function. type panicError struct { value any stack []byte @@ -204,7 +204,7 @@ } } -// Forget tells the singleflight to forget about a key. Future calls +// Forget tells the singleflight to forget about a key. Future calls // to Do for this key will call the function rather than waiting for // an earlier call to complete. func (g *Group) Forget(key string) {
diff --git a/syncmap/map_reference_test.go b/syncmap/map_reference_test.go index 8453174..f9b4dcf 100644 --- a/syncmap/map_reference_test.go +++ b/syncmap/map_reference_test.go
@@ -9,7 +9,7 @@ "sync/atomic" ) -// This file contains reference map implementations for unit-tests. +// This file contains reference map implementations for unit tests. // mapInterface is the interface Map implements. type mapInterface interface { @@ -82,7 +82,7 @@ } // DeepCopyMap is an implementation of mapInterface using a Mutex and -// atomic.Value. It makes deep copies of the map on every write to avoid +// atomic.Value. It makes deep copies of the map on every write to avoid // acquiring the Mutex in Load. type DeepCopyMap struct { mu sync.Mutex