semaphore: panic on negative capacity

NewWeighted currently accepts a negative capacity, creating a
semaphore for which even a zero-weight acquisition cannot succeed.
Reject negative capacities at construction time while continuing to
allow zero capacity.

Updates golang/go#80183

Change-Id: Id819efed370bb81e8bf13891d781f90e298a2a07
Reviewed-on: https://go-review.googlesource.com/c/sync/+/808920
Auto-Submit: Alan Donovan <adonovan@google.com>
Reviewed-by: race quite <quiterace@gmail.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Reviewed-by: David Chase <drchase@google.com>
diff --git a/semaphore/semaphore.go b/semaphore/semaphore.go
index 96a035a..f2a7d3f 100644
--- a/semaphore/semaphore.go
+++ b/semaphore/semaphore.go
@@ -17,10 +17,13 @@
 }
 
 // NewWeighted creates a new weighted semaphore with the given
-// maximum combined weight for concurrent access.
+// maximum combined weight for concurrent access. NewWeighted panics if n is
+// negative.
 func NewWeighted(n int64) *Weighted {
-	w := &Weighted{size: n}
-	return w
+	if n < 0 {
+		panic("semaphore: size < 0")
+	}
+	return &Weighted{size: n}
 }
 
 // Weighted provides a way to bound concurrent access to a resource.
diff --git a/semaphore/semaphore_test.go b/semaphore/semaphore_test.go
index 1a2eb7a..058a382 100644
--- a/semaphore/semaphore_test.go
+++ b/semaphore/semaphore_test.go
@@ -56,12 +56,32 @@
 	w.Release(1)
 }
 
+func TestWeightedNegativeSizePanic(t *testing.T) {
+	t.Parallel()
+
+	defer func() {
+		if recover() == nil {
+			t.Fatal("NewWeighted with negative size did not panic")
+		}
+	}()
+	semaphore.NewWeighted(-1)
+}
+
+func TestWeightedZeroSize(t *testing.T) {
+	t.Parallel()
+
+	w := semaphore.NewWeighted(0)
+	if !w.TryAcquire(0) {
+		t.Fatal("TryAcquire on a zero-sized semaphore with zero weight failed")
+	}
+}
+
 func TestWeightedNegativeWeightPanic(t *testing.T) {
 	t.Parallel()
 
 	ctx := context.Background()
 	w := semaphore.NewWeighted(1)
-	
+
 	func() {
 		defer func() {
 			if recover() == nil {