github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/lsync/lrwmutex_test.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package lsync_test
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"runtime"
    24  	"sync"
    25  	"sync/atomic"
    26  	"testing"
    27  	"time"
    28  
    29  	. "github.com/minio/minio/internal/lsync"
    30  )
    31  
    32  func testSimpleWriteLock(t *testing.T, duration time.Duration) (locked bool) {
    33  	ctx := context.Background()
    34  	lrwm := NewLRWMutex()
    35  
    36  	if !lrwm.GetRLock(ctx, "", "object1", time.Second) {
    37  		panic("Failed to acquire read lock")
    38  	}
    39  	// fmt.Println("1st read lock acquired, waiting...")
    40  
    41  	if !lrwm.GetRLock(ctx, "", "object1", time.Second) {
    42  		panic("Failed to acquire read lock")
    43  	}
    44  	// fmt.Println("2nd read lock acquired, waiting...")
    45  
    46  	go func() {
    47  		time.Sleep(2 * time.Second)
    48  		lrwm.RUnlock()
    49  		// fmt.Println("1st read lock released, waiting...")
    50  	}()
    51  
    52  	go func() {
    53  		time.Sleep(3 * time.Second)
    54  		lrwm.RUnlock()
    55  		// fmt.Println("2nd read lock released, waiting...")
    56  	}()
    57  
    58  	// fmt.Println("Trying to acquire write lock, waiting...")
    59  	locked = lrwm.GetLock(ctx, "", "", duration)
    60  	if locked {
    61  		// fmt.Println("Write lock acquired, waiting...")
    62  		time.Sleep(1 * time.Second)
    63  
    64  		lrwm.Unlock()
    65  	} else {
    66  		t.Log("Write lock failed due to timeout")
    67  	}
    68  	return
    69  }
    70  
    71  func TestSimpleWriteLockAcquired(t *testing.T) {
    72  	locked := testSimpleWriteLock(t, 5*time.Second)
    73  
    74  	expected := true
    75  	if locked != expected {
    76  		t.Errorf("TestSimpleWriteLockAcquired(): \nexpected %#v\ngot      %#v", expected, locked)
    77  	}
    78  }
    79  
    80  func TestSimpleWriteLockTimedOut(t *testing.T) {
    81  	locked := testSimpleWriteLock(t, time.Second)
    82  
    83  	expected := false
    84  	if locked != expected {
    85  		t.Errorf("TestSimpleWriteLockTimedOut(): \nexpected %#v\ngot      %#v", expected, locked)
    86  	}
    87  }
    88  
    89  func testDualWriteLock(t *testing.T, duration time.Duration) (locked bool) {
    90  	ctx := context.Background()
    91  	lrwm := NewLRWMutex()
    92  
    93  	// fmt.Println("Getting initial write lock")
    94  	if !lrwm.GetLock(ctx, "", "", time.Second) {
    95  		panic("Failed to acquire initial write lock")
    96  	}
    97  
    98  	go func() {
    99  		time.Sleep(2 * time.Second)
   100  		lrwm.Unlock()
   101  		// fmt.Println("Initial write lock released, waiting...")
   102  	}()
   103  
   104  	// fmt.Println("Trying to acquire 2nd write lock, waiting...")
   105  	locked = lrwm.GetLock(ctx, "", "", duration)
   106  	if locked {
   107  		// fmt.Println("2nd write lock acquired, waiting...")
   108  		time.Sleep(time.Second)
   109  
   110  		lrwm.Unlock()
   111  	} else {
   112  		t.Log("2nd write lock failed due to timeout")
   113  	}
   114  	return
   115  }
   116  
   117  func TestDualWriteLockAcquired(t *testing.T) {
   118  	locked := testDualWriteLock(t, 3*time.Second)
   119  
   120  	expected := true
   121  	if locked != expected {
   122  		t.Errorf("TestDualWriteLockAcquired(): \nexpected %#v\ngot      %#v", expected, locked)
   123  	}
   124  }
   125  
   126  func TestDualWriteLockTimedOut(t *testing.T) {
   127  	locked := testDualWriteLock(t, time.Second)
   128  
   129  	expected := false
   130  	if locked != expected {
   131  		t.Errorf("TestDualWriteLockTimedOut(): \nexpected %#v\ngot      %#v", expected, locked)
   132  	}
   133  }
   134  
   135  // Test cases below are copied 1 to 1 from sync/rwmutex_test.go (adapted to use LRWMutex)
   136  
   137  // Borrowed from rwmutex_test.go
   138  func parallelReader(ctx context.Context, m *LRWMutex, clocked, cunlock, cdone chan bool) {
   139  	if m.GetRLock(ctx, "", "", time.Second) {
   140  		clocked <- true
   141  		<-cunlock
   142  		m.RUnlock()
   143  		cdone <- true
   144  	}
   145  }
   146  
   147  // Borrowed from rwmutex_test.go
   148  func doTestParallelReaders(numReaders, gomaxprocs int) {
   149  	runtime.GOMAXPROCS(gomaxprocs)
   150  	m := NewLRWMutex()
   151  
   152  	clocked := make(chan bool)
   153  	cunlock := make(chan bool)
   154  	cdone := make(chan bool)
   155  	for i := 0; i < numReaders; i++ {
   156  		go parallelReader(context.Background(), m, clocked, cunlock, cdone)
   157  	}
   158  	// Wait for all parallel RLock()s to succeed.
   159  	for i := 0; i < numReaders; i++ {
   160  		<-clocked
   161  	}
   162  	for i := 0; i < numReaders; i++ {
   163  		cunlock <- true
   164  	}
   165  	// Wait for the goroutines to finish.
   166  	for i := 0; i < numReaders; i++ {
   167  		<-cdone
   168  	}
   169  }
   170  
   171  // Borrowed from rwmutex_test.go
   172  func TestParallelReaders(t *testing.T) {
   173  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
   174  	doTestParallelReaders(1, 4)
   175  	doTestParallelReaders(3, 4)
   176  	doTestParallelReaders(4, 2)
   177  }
   178  
   179  // Borrowed from rwmutex_test.go
   180  func reader(rwm *LRWMutex, numIterations int, activity *int32, cdone chan bool) {
   181  	for i := 0; i < numIterations; i++ {
   182  		if rwm.GetRLock(context.Background(), "", "", time.Second) {
   183  			n := atomic.AddInt32(activity, 1)
   184  			if n < 1 || n >= 10000 {
   185  				panic(fmt.Sprintf("wlock(%d)\n", n))
   186  			}
   187  			for i := 0; i < 100; i++ {
   188  			}
   189  			atomic.AddInt32(activity, -1)
   190  			rwm.RUnlock()
   191  		}
   192  	}
   193  	cdone <- true
   194  }
   195  
   196  // Borrowed from rwmutex_test.go
   197  func writer(rwm *LRWMutex, numIterations int, activity *int32, cdone chan bool) {
   198  	for i := 0; i < numIterations; i++ {
   199  		if rwm.GetLock(context.Background(), "", "", time.Second) {
   200  			n := atomic.AddInt32(activity, 10000)
   201  			if n != 10000 {
   202  				panic(fmt.Sprintf("wlock(%d)\n", n))
   203  			}
   204  			for i := 0; i < 100; i++ {
   205  			}
   206  			atomic.AddInt32(activity, -10000)
   207  			rwm.Unlock()
   208  		}
   209  	}
   210  	cdone <- true
   211  }
   212  
   213  // Borrowed from rwmutex_test.go
   214  func HammerRWMutex(gomaxprocs, numReaders, numIterations int) {
   215  	runtime.GOMAXPROCS(gomaxprocs)
   216  	// Number of active readers + 10000 * number of active writers.
   217  	var activity int32
   218  	rwm := NewLRWMutex()
   219  	cdone := make(chan bool)
   220  	go writer(rwm, numIterations, &activity, cdone)
   221  	var i int
   222  	for i = 0; i < numReaders/2; i++ {
   223  		go reader(rwm, numIterations, &activity, cdone)
   224  	}
   225  	go writer(rwm, numIterations, &activity, cdone)
   226  	for ; i < numReaders; i++ {
   227  		go reader(rwm, numIterations, &activity, cdone)
   228  	}
   229  	// Wait for the 2 writers and all readers to finish.
   230  	for i := 0; i < 2+numReaders; i++ {
   231  		<-cdone
   232  	}
   233  }
   234  
   235  // Borrowed from rwmutex_test.go
   236  func TestRWMutex(t *testing.T) {
   237  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
   238  	n := 1000
   239  	if testing.Short() {
   240  		n = 5
   241  	}
   242  	HammerRWMutex(1, 1, n)
   243  	HammerRWMutex(1, 3, n)
   244  	HammerRWMutex(1, 10, n)
   245  	HammerRWMutex(4, 1, n)
   246  	HammerRWMutex(4, 3, n)
   247  	HammerRWMutex(4, 10, n)
   248  	HammerRWMutex(10, 1, n)
   249  	HammerRWMutex(10, 3, n)
   250  	HammerRWMutex(10, 10, n)
   251  	HammerRWMutex(10, 5, n)
   252  }
   253  
   254  // Borrowed from rwmutex_test.go
   255  func TestDRLocker(t *testing.T) {
   256  	wl := NewLRWMutex()
   257  	var rl sync.Locker
   258  	wlocked := make(chan bool, 1)
   259  	rlocked := make(chan bool, 1)
   260  	rl = wl.DRLocker()
   261  	n := 10
   262  	go func() {
   263  		for i := 0; i < n; i++ {
   264  			rl.Lock()
   265  			rl.Lock()
   266  			rlocked <- true
   267  			wl.Lock()
   268  			wlocked <- true
   269  		}
   270  	}()
   271  	for i := 0; i < n; i++ {
   272  		<-rlocked
   273  		rl.Unlock()
   274  		select {
   275  		case <-wlocked:
   276  			t.Fatal("RLocker() didn't read-lock it")
   277  		default:
   278  		}
   279  		rl.Unlock()
   280  		<-wlocked
   281  		select {
   282  		case <-rlocked:
   283  			t.Fatal("RLocker() didn't respect the write lock")
   284  		default:
   285  		}
   286  		wl.Unlock()
   287  	}
   288  }
   289  
   290  // Borrowed from rwmutex_test.go
   291  func TestUnlockPanic(t *testing.T) {
   292  	defer func() {
   293  		if recover() == nil {
   294  			t.Fatalf("unlock of unlocked RWMutex did not panic")
   295  		}
   296  	}()
   297  	mu := NewLRWMutex()
   298  	mu.Unlock()
   299  }
   300  
   301  // Borrowed from rwmutex_test.go
   302  func TestUnlockPanic2(t *testing.T) {
   303  	defer func() {
   304  		if recover() == nil {
   305  			t.Fatalf("unlock of unlocked RWMutex did not panic")
   306  		}
   307  	}()
   308  	mu := NewLRWMutex()
   309  	mu.RLock()
   310  	mu.Unlock()
   311  }
   312  
   313  // Borrowed from rwmutex_test.go
   314  func TestRUnlockPanic(t *testing.T) {
   315  	defer func() {
   316  		if recover() == nil {
   317  			t.Fatalf("read unlock of unlocked RWMutex did not panic")
   318  		}
   319  	}()
   320  	mu := NewLRWMutex()
   321  	mu.RUnlock()
   322  }
   323  
   324  // Borrowed from rwmutex_test.go
   325  func TestRUnlockPanic2(t *testing.T) {
   326  	defer func() {
   327  		if recover() == nil {
   328  			t.Fatalf("read unlock of unlocked RWMutex did not panic")
   329  		}
   330  	}()
   331  	mu := NewLRWMutex()
   332  	mu.Lock()
   333  	mu.RUnlock()
   334  }