diff --git a/src/pkg/sync/mutex.go b/src/pkg/sync/mutex.go index 6c8a5d51d4..2a1270b9c4 100644 --- a/src/pkg/sync/mutex.go +++ b/src/pkg/sync/mutex.go @@ -53,9 +53,14 @@ func (m *Mutex) Lock() { // It is allowed for one goroutine to lock a Mutex and then // arrange for another goroutine to unlock it. func (m *Mutex) Unlock() { - if xadd(&m.key, -1) == 0 { + switch v := xadd(&m.key, -1); { + case v == 0: // changed from 1 to 0; no contention return + case int32(v) == -1: + // changed from 0 to -1: wasn't locked + // (or there are 4 billion goroutines waiting) + panic("sync: unlock of unlocked mutex") } runtime.Semrelease(&m.sema) } diff --git a/src/pkg/sync/mutex_test.go b/src/pkg/sync/mutex_test.go index d0e048ed7a..f5c20ca49b 100644 --- a/src/pkg/sync/mutex_test.go +++ b/src/pkg/sync/mutex_test.go @@ -89,3 +89,16 @@ func BenchmarkContendedMutex(b *testing.B) { <-c <-c } + +func TestMutexPanic(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatalf("unlock of unlocked mutex did not panic") + } + }() + + var mu Mutex + mu.Lock() + mu.Unlock() + mu.Unlock() +}