storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/lsync/lrwmutex_test.go (about)

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