github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/kbfssync/leveled_mutex_test.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package kbfssync
     6  
     7  import (
     8  	"fmt"
     9  	"sync"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  func TestExclusiveLock(t *testing.T) {
    16  	el := makeExclusiveLock()
    17  	el.lock()
    18  	defer el.unlock()
    19  
    20  	// This must leave el unlocked.
    21  	require.Panics(t, func() {
    22  		el.lock()
    23  	})
    24  }
    25  
    26  type testMutexLevel MutexLevel
    27  
    28  const (
    29  	testFirst  testMutexLevel = 1
    30  	testSecond testMutexLevel = 2
    31  	testThird  testMutexLevel = 3
    32  )
    33  
    34  func (o testMutexLevel) String() string {
    35  	return fmt.Sprintf("test-lock-%d", int(o))
    36  }
    37  
    38  func testMutexLevelToString(o MutexLevel) string {
    39  	return (testMutexLevel(o)).String()
    40  }
    41  
    42  func TestLeveledMutexSingleFlow(t *testing.T) {
    43  	mu1 := MakeLeveledMutex(MutexLevel(testFirst), &sync.Mutex{})
    44  	mu2 := MakeLeveledMutex(MutexLevel(testSecond), &sync.Mutex{})
    45  	mu3 := MakeLeveledMutex(MutexLevel(testThird), &sync.Mutex{})
    46  
    47  	state := MakeLevelState(testMutexLevelToString)
    48  
    49  	for _, mu := range []LeveledMutex{mu1, mu2, mu3} {
    50  		mu.AssertUnlocked(state)
    51  		mu.Lock(state)
    52  		mu.AssertLocked(state)
    53  
    54  		defer func(mu LeveledMutex) {
    55  			mu.AssertLocked(state)
    56  			mu.Unlock(state)
    57  			mu.AssertUnlocked(state)
    58  		}(mu)
    59  	}
    60  }
    61  
    62  func TestLeveledMutexIncorrect(t *testing.T) {
    63  	mu1 := MakeLeveledMutex(MutexLevel(testFirst), &sync.Mutex{})
    64  	mu2 := MakeLeveledMutex(MutexLevel(testSecond), &sync.Mutex{})
    65  	mu3 := MakeLeveledMutex(MutexLevel(testThird), &sync.Mutex{})
    66  
    67  	state := MakeLevelState(testMutexLevelToString)
    68  
    69  	require.Panics(t, func() {
    70  		mu1.AssertLocked(state)
    71  	})
    72  
    73  	mu2.Lock(state)
    74  
    75  	require.Panics(t, func() {
    76  		mu2.AssertUnlocked(state)
    77  	})
    78  
    79  	defer func() {
    80  		mu2.Unlock(state)
    81  
    82  		require.Panics(t, func() {
    83  			mu2.AssertLocked(state)
    84  		})
    85  	}()
    86  
    87  	// This must leave mu1 unlocked.
    88  	require.Panics(t, func() {
    89  		mu1.Lock(state)
    90  	})
    91  
    92  	mu3.Lock(state)
    93  	defer mu3.Unlock(state)
    94  
    95  	// This must leave mu2 locked.
    96  	require.Panics(t, func() {
    97  		mu2.Unlock(state)
    98  	})
    99  }
   100  
   101  // runLockSubsequences() runs all possible subsequences of {mu1, mu2,
   102  // mu3}.Lock() under the given WaitGroup.
   103  func runLockSubsequences(wg *sync.WaitGroup, mu1, mu2, mu3 LeveledLocker) {
   104  	wg.Add(1)
   105  	go func() {
   106  		defer wg.Done()
   107  		state := MakeLevelState(testMutexLevelToString)
   108  		mu1.Lock(state)
   109  		defer mu1.Unlock(state)
   110  		mu2.Lock(state)
   111  		defer mu2.Unlock(state)
   112  		mu3.Lock(state)
   113  		defer mu3.Unlock(state)
   114  	}()
   115  
   116  	wg.Add(1)
   117  	go func() {
   118  		defer wg.Done()
   119  		state := MakeLevelState(testMutexLevelToString)
   120  		mu1.Lock(state)
   121  		defer mu1.Unlock(state)
   122  		mu2.Lock(state)
   123  		defer mu2.Unlock(state)
   124  	}()
   125  
   126  	wg.Add(1)
   127  	go func() {
   128  		defer wg.Done()
   129  		state := MakeLevelState(testMutexLevelToString)
   130  		mu1.Lock(state)
   131  		defer mu1.Unlock(state)
   132  		mu3.Lock(state)
   133  		defer mu3.Unlock(state)
   134  	}()
   135  
   136  	wg.Add(1)
   137  	go func() {
   138  		defer wg.Done()
   139  		state := MakeLevelState(testMutexLevelToString)
   140  		mu2.Lock(state)
   141  		defer mu2.Unlock(state)
   142  		mu3.Lock(state)
   143  		defer mu3.Unlock(state)
   144  	}()
   145  
   146  	wg.Add(1)
   147  	go func() {
   148  		defer wg.Done()
   149  		state := MakeLevelState(testMutexLevelToString)
   150  		mu1.Lock(state)
   151  		defer mu1.Unlock(state)
   152  	}()
   153  
   154  	wg.Add(1)
   155  	go func() {
   156  		defer wg.Done()
   157  		state := MakeLevelState(testMutexLevelToString)
   158  		mu2.Lock(state)
   159  		defer mu2.Unlock(state)
   160  	}()
   161  
   162  	wg.Add(1)
   163  	go func() {
   164  		defer wg.Done()
   165  		state := MakeLevelState(testMutexLevelToString)
   166  		mu3.Lock(state)
   167  		defer mu3.Unlock(state)
   168  	}()
   169  }
   170  
   171  func TestLeveledMutexMultiFlow(t *testing.T) {
   172  	mu1 := MakeLeveledMutex(MutexLevel(testFirst), &sync.Mutex{})
   173  	mu2 := MakeLeveledMutex(MutexLevel(testSecond), &sync.Mutex{})
   174  	mu3 := MakeLeveledMutex(MutexLevel(testThird), &sync.Mutex{})
   175  
   176  	var wg sync.WaitGroup
   177  	runLockSubsequences(&wg, mu1, mu2, mu3)
   178  	wg.Wait()
   179  }
   180  
   181  func TestLeveledRWMutexSingleFlow(t *testing.T) {
   182  	mu1 := MakeLeveledRWMutex(MutexLevel(testFirst), &sync.RWMutex{})
   183  	mu2 := MakeLeveledRWMutex(MutexLevel(testSecond), &sync.RWMutex{})
   184  	mu3 := MakeLeveledRWMutex(MutexLevel(testThird), &sync.RWMutex{})
   185  
   186  	state := MakeLevelState(testMutexLevelToString)
   187  
   188  	mu1.AssertUnlocked(state)
   189  	mu1.Lock(state)
   190  	mu1.AssertLocked(state)
   191  	mu1.AssertAnyLocked(state)
   192  
   193  	defer func() {
   194  		mu1.AssertLocked(state)
   195  		mu1.AssertAnyLocked(state)
   196  		mu1.Unlock(state)
   197  		mu1.AssertUnlocked(state)
   198  	}()
   199  
   200  	mu2.AssertUnlocked(state)
   201  	mu2.RLock(state)
   202  	mu2.AssertRLocked(state)
   203  	mu2.AssertAnyLocked(state)
   204  
   205  	defer func() {
   206  		mu2.AssertRLocked(state)
   207  		mu2.AssertAnyLocked(state)
   208  		mu2.RUnlock(state)
   209  		mu2.AssertUnlocked(state)
   210  	}()
   211  
   212  	mu3.AssertUnlocked(state)
   213  	mu3.Lock(state)
   214  	mu3.AssertLocked(state)
   215  	mu3.AssertAnyLocked(state)
   216  
   217  	defer func() {
   218  		mu3.AssertLocked(state)
   219  		mu3.AssertAnyLocked(state)
   220  		mu3.Unlock(state)
   221  		mu3.AssertUnlocked(state)
   222  	}()
   223  }
   224  
   225  func TestLeveledRWMutexIncorrect(t *testing.T) {
   226  	mu1 := MakeLeveledRWMutex(MutexLevel(testFirst), &sync.RWMutex{})
   227  	mu2 := MakeLeveledRWMutex(MutexLevel(testSecond), &sync.RWMutex{})
   228  	mu3 := MakeLeveledRWMutex(MutexLevel(testThird), &sync.RWMutex{})
   229  
   230  	state := MakeLevelState(testMutexLevelToString)
   231  
   232  	require.Panics(t, func() {
   233  		mu1.AssertLocked(state)
   234  	})
   235  	require.Panics(t, func() {
   236  		mu1.AssertRLocked(state)
   237  	})
   238  	require.Panics(t, func() {
   239  		mu1.AssertAnyLocked(state)
   240  	})
   241  
   242  	mu2.RLock(state)
   243  
   244  	require.Panics(t, func() {
   245  		mu2.AssertUnlocked(state)
   246  	})
   247  
   248  	defer func() {
   249  		mu2.RUnlock(state)
   250  	}()
   251  
   252  	require.Panics(t, func() {
   253  		mu2.AssertLocked(state)
   254  
   255  		require.Panics(t, func() {
   256  			mu2.AssertLocked(state)
   257  		})
   258  		require.Panics(t, func() {
   259  			mu2.AssertRLocked(state)
   260  		})
   261  		require.Panics(t, func() {
   262  			mu2.AssertAnyLocked(state)
   263  		})
   264  	})
   265  
   266  	// This must leave mu2 read-locked.
   267  	require.Panics(t, func() {
   268  		mu2.RLock(state)
   269  	})
   270  
   271  	// These must leave mu1 unlocked.
   272  	require.Panics(t, func() {
   273  		mu1.RLock(state)
   274  	})
   275  	require.Panics(t, func() {
   276  		mu1.Lock(state)
   277  	})
   278  
   279  	mu3.Lock(state)
   280  	defer mu3.Unlock(state)
   281  
   282  	require.Panics(t, func() {
   283  		mu3.AssertRLocked(state)
   284  	})
   285  
   286  	// This must leave mu3 locked.
   287  	require.Panics(t, func() {
   288  		mu3.RUnlock(state)
   289  	})
   290  
   291  	// These must leave mu2 read-locked.
   292  	require.Panics(t, func() {
   293  		mu2.Unlock(state)
   294  	})
   295  	require.Panics(t, func() {
   296  		mu2.RUnlock(state)
   297  	})
   298  }
   299  
   300  func TestLeveledRWMutexMultiFlow(t *testing.T) {
   301  	mu1 := MakeLeveledMutex(MutexLevel(testFirst), &sync.Mutex{})
   302  	mu2 := MakeLeveledRWMutex(MutexLevel(testSecond), &sync.RWMutex{})
   303  	mu3 := MakeLeveledRWMutex(MutexLevel(testThird), &sync.RWMutex{})
   304  
   305  	var wg sync.WaitGroup
   306  
   307  	runLockSubsequences(&wg, mu1, mu2, mu3)
   308  	runLockSubsequences(&wg, mu1, mu2.RLocker(), mu3)
   309  	runLockSubsequences(&wg, mu1, mu2, mu3.RLocker())
   310  	runLockSubsequences(&wg, mu1, mu2.RLocker(), mu3.RLocker())
   311  
   312  	wg.Wait()
   313  }