Source file src/database/sql/closemu_test.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package sql
     6  
     7  import (
     8  	"testing"
     9  	"testing/synctest"
    10  )
    11  
    12  func TestClosingMutex(t *testing.T) {
    13  	start := func(t *testing.T, f func()) func() bool {
    14  		done := false
    15  		go func() {
    16  			f()
    17  			done = true
    18  		}()
    19  		return func() bool {
    20  			synctest.Wait()
    21  			return done
    22  		}
    23  	}
    24  
    25  	synctest.Test(t, func(t *testing.T) {
    26  		var m closingMutex
    27  
    28  		// RLock does not block RLock.
    29  		m.RLock()
    30  		m.RLock()
    31  		m.RUnlock()
    32  		m.RUnlock()
    33  
    34  		// RLock blocks Lock.
    35  		m.RLock()
    36  		lock1Done := start(t, m.Lock)
    37  		if lock1Done() {
    38  			t.Fatalf("m.Lock(): succeeded on RLocked mutex")
    39  		}
    40  		m.RLock()
    41  		m.RUnlock()
    42  		if lock1Done() {
    43  			t.Fatalf("m.Lock(): succeeded after one RUnlock, one RLock remains")
    44  		}
    45  		m.RUnlock()
    46  		if !lock1Done() {
    47  			t.Fatalf("m.Lock(): still blocking after all RUnlocks")
    48  		}
    49  		m.Unlock()
    50  
    51  		// Lock blocks RLock.
    52  		m.Lock()
    53  		rlock1Done := start(t, m.RLock)
    54  		rlock2Done := start(t, m.RLock)
    55  		if rlock1Done() || rlock2Done() {
    56  			t.Fatalf("m.RLock(): succeeded on Locked mutex")
    57  		}
    58  		m.Unlock()
    59  		if !rlock1Done() || !rlock2Done() {
    60  			t.Fatalf("m.RLock(): succeeded on Locked mutex")
    61  		}
    62  		m.RUnlock()
    63  		m.RUnlock()
    64  
    65  		// Lock blocks Lock.
    66  		m.Lock()
    67  		lock2Done := start(t, m.Lock)
    68  		if lock2Done() {
    69  			t.Fatalf("m.Lock(): succeeded on Locked mutex")
    70  		}
    71  		m.Unlock()
    72  		if !lock2Done() {
    73  			t.Fatalf("m.Lock(): still blocking after Unlock")
    74  		}
    75  		m.Unlock()
    76  
    77  		// Lock on RLocked mutex does not block RLock.
    78  		m.RLock()
    79  		lock3Done := start(t, m.Lock)
    80  		if lock3Done() {
    81  			t.Fatalf("m.Lock(): succeeded on RLocked mutex")
    82  		}
    83  		m.RLock()
    84  		m.RUnlock()
    85  		m.RUnlock()
    86  		if !lock3Done() {
    87  			t.Fatalf("m.Lock(): still blocking after RUnlock")
    88  		}
    89  		m.Unlock()
    90  
    91  	})
    92  }
    93  
    94  func TestClosingMutexPanics(t *testing.T) {
    95  	for _, test := range []struct {
    96  		name string
    97  		f    func()
    98  	}{{
    99  		name: "double RUnlock",
   100  		f: func() {
   101  			var m closingMutex
   102  			m.RLock()
   103  			m.RUnlock()
   104  			m.RUnlock()
   105  		},
   106  	}, {
   107  		name: "double Unlock",
   108  		f: func() {
   109  			var m closingMutex
   110  			m.Lock()
   111  			m.Unlock()
   112  			m.Unlock()
   113  		},
   114  	}} {
   115  		var got any
   116  		func() {
   117  			defer func() {
   118  				got = recover()
   119  			}()
   120  			test.f()
   121  		}()
   122  		if got == nil {
   123  			t.Errorf("no panic, want one")
   124  		}
   125  	}
   126  }
   127  

View as plain text