// Copyright 2026 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package sql import ( "testing" "testing/synctest" ) func TestClosingMutex(t *testing.T) { start := func(t *testing.T, f func()) func() bool { done := false go func() { f() done = true }() return func() bool { synctest.Wait() return done } } synctest.Test(t, func(t *testing.T) { var m closingMutex // RLock does not block RLock. m.RLock() m.RLock() m.RUnlock() m.RUnlock() // RLock blocks Lock. m.RLock() lock1Done := start(t, m.Lock) if lock1Done() { t.Fatalf("m.Lock(): succeeded on RLocked mutex") } m.RLock() m.RUnlock() if lock1Done() { t.Fatalf("m.Lock(): succeeded after one RUnlock, one RLock remains") } m.RUnlock() if !lock1Done() { t.Fatalf("m.Lock(): still blocking after all RUnlocks") } m.Unlock() // Lock blocks RLock. m.Lock() rlock1Done := start(t, m.RLock) rlock2Done := start(t, m.RLock) if rlock1Done() || rlock2Done() { t.Fatalf("m.RLock(): succeeded on Locked mutex") } m.Unlock() if !rlock1Done() || !rlock2Done() { t.Fatalf("m.RLock(): succeeded on Locked mutex") } m.RUnlock() m.RUnlock() // Lock blocks Lock. m.Lock() lock2Done := start(t, m.Lock) if lock2Done() { t.Fatalf("m.Lock(): succeeded on Locked mutex") } m.Unlock() if !lock2Done() { t.Fatalf("m.Lock(): still blocking after Unlock") } m.Unlock() // Lock on RLocked mutex does not block RLock. m.RLock() lock3Done := start(t, m.Lock) if lock3Done() { t.Fatalf("m.Lock(): succeeded on RLocked mutex") } m.RLock() m.RUnlock() m.RUnlock() if !lock3Done() { t.Fatalf("m.Lock(): still blocking after RUnlock") } m.Unlock() }) } func TestClosingMutexPanics(t *testing.T) { for _, test := range []struct { name string f func() }{{ name: "double RUnlock", f: func() { var m closingMutex m.RLock() m.RUnlock() m.RUnlock() }, }, { name: "double Unlock", f: func() { var m closingMutex m.Lock() m.Unlock() m.Unlock() }, }} { var got any func() { defer func() { got = recover() }() test.f() }() if got == nil { t.Errorf("no panic, want one") } } }