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  func TestClosingMutexLockStarvation(t *testing.T) {
    94  	synctest.Test(t, func(t *testing.T) {
    95  		// Run this test for a few iterations, to avoid racy successes.
    96  		for range 100 {
    97  			var m closingMutex
    98  
    99  			// With the mutex RLocked, try to Lock it. Lock blocks.
   100  			m.RLock()
   101  			locked := false
   102  			go func() {
   103  				m.Lock()
   104  				locked = true
   105  				m.Unlock()
   106  			}()
   107  			synctest.Wait()
   108  			if locked {
   109  				t.Errorf("lock acquired while mutex is rlocked")
   110  			}
   111  
   112  			// Add and drop another RLock. Lock is still blocking.
   113  			m.RLock()
   114  			m.RUnlock()
   115  			if locked {
   116  				t.Errorf("lock acquired while mutex is double-rlocked")
   117  			}
   118  
   119  			// Drop and reacquire the RLock.
   120  			// The blocking Lock should always acquire the mutex
   121  			// before the RLock succeeds.
   122  			m.RUnlock()
   123  			m.RLock()
   124  			if !locked {
   125  				t.Errorf("lock not acquired when rlock dropped")
   126  			}
   127  			m.RUnlock()
   128  		}
   129  	})
   130  }
   131  
   132  func TestClosingMutexPanics(t *testing.T) {
   133  	for _, test := range []struct {
   134  		name string
   135  		f    func()
   136  	}{{
   137  		name: "double RUnlock",
   138  		f: func() {
   139  			var m closingMutex
   140  			m.RLock()
   141  			m.RUnlock()
   142  			m.RUnlock()
   143  		},
   144  	}, {
   145  		name: "double Unlock",
   146  		f: func() {
   147  			var m closingMutex
   148  			m.Lock()
   149  			m.Unlock()
   150  			m.Unlock()
   151  		},
   152  	}} {
   153  		var got any
   154  		func() {
   155  			defer func() {
   156  				got = recover()
   157  			}()
   158  			test.f()
   159  		}()
   160  		if got == nil {
   161  			t.Errorf("no panic, want one")
   162  		}
   163  	}
   164  }
   165  

View as plain text