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

     1  /*
     2   * Minio Cloud Storage, (C) 2016 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  package dsync_test
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"runtime"
    23  	"sync/atomic"
    24  	"testing"
    25  	"time"
    26  
    27  	. "storj.io/minio/pkg/dsync"
    28  )
    29  
    30  const (
    31  	id     = "1234-5678"
    32  	source = "main.go"
    33  )
    34  
    35  func testSimpleWriteLock(t *testing.T, duration time.Duration) (locked bool) {
    36  
    37  	drwm := NewDRWMutex(ds, "simplelock")
    38  
    39  	ctx1, cancel1 := context.WithCancel(context.Background())
    40  	if !drwm.GetRLock(ctx1, cancel1, id, source, Options{Timeout: time.Second}) {
    41  		panic("Failed to acquire read lock")
    42  	}
    43  	// fmt.Println("1st read lock acquired, waiting...")
    44  
    45  	ctx2, cancel2 := context.WithCancel(context.Background())
    46  	if !drwm.GetRLock(ctx2, cancel2, id, source, Options{Timeout: time.Second}) {
    47  		panic("Failed to acquire read lock")
    48  	}
    49  	// fmt.Println("2nd read lock acquired, waiting...")
    50  
    51  	go func() {
    52  		time.Sleep(2 * time.Second)
    53  		drwm.RUnlock()
    54  		// fmt.Println("1st read lock released, waiting...")
    55  	}()
    56  
    57  	go func() {
    58  		time.Sleep(3 * time.Second)
    59  		drwm.RUnlock()
    60  		// fmt.Println("2nd read lock released, waiting...")
    61  	}()
    62  
    63  	// fmt.Println("Trying to acquire write lock, waiting...")
    64  	ctx3, cancel3 := context.WithCancel(context.Background())
    65  	locked = drwm.GetLock(ctx3, cancel3, id, source, Options{Timeout: duration})
    66  	if locked {
    67  		// fmt.Println("Write lock acquired, waiting...")
    68  		time.Sleep(time.Second)
    69  
    70  		drwm.Unlock()
    71  	}
    72  	// fmt.Println("Write lock failed due to timeout")
    73  	return
    74  }
    75  
    76  func TestSimpleWriteLockAcquired(t *testing.T) {
    77  	locked := testSimpleWriteLock(t, 5*time.Second)
    78  
    79  	expected := true
    80  	if locked != expected {
    81  		t.Errorf("TestSimpleWriteLockAcquired(): \nexpected %#v\ngot      %#v", expected, locked)
    82  	}
    83  }
    84  
    85  func TestSimpleWriteLockTimedOut(t *testing.T) {
    86  	locked := testSimpleWriteLock(t, time.Second)
    87  
    88  	expected := false
    89  	if locked != expected {
    90  		t.Errorf("TestSimpleWriteLockTimedOut(): \nexpected %#v\ngot      %#v", expected, locked)
    91  	}
    92  }
    93  
    94  func testDualWriteLock(t *testing.T, duration time.Duration) (locked bool) {
    95  
    96  	drwm := NewDRWMutex(ds, "duallock")
    97  
    98  	// fmt.Println("Getting initial write lock")
    99  	ctx1, cancel1 := context.WithCancel(context.Background())
   100  	if !drwm.GetLock(ctx1, cancel1, id, source, Options{Timeout: time.Second}) {
   101  		panic("Failed to acquire initial write lock")
   102  	}
   103  
   104  	go func() {
   105  		time.Sleep(2 * time.Second)
   106  		drwm.Unlock()
   107  		// fmt.Println("Initial write lock released, waiting...")
   108  	}()
   109  
   110  	// fmt.Println("Trying to acquire 2nd write lock, waiting...")
   111  	ctx2, cancel2 := context.WithCancel(context.Background())
   112  	locked = drwm.GetLock(ctx2, cancel2, id, source, Options{Timeout: duration})
   113  	if locked {
   114  		// fmt.Println("2nd write lock acquired, waiting...")
   115  		time.Sleep(time.Second)
   116  
   117  		drwm.Unlock()
   118  	}
   119  	// fmt.Println("2nd write lock failed due to timeout")
   120  	return
   121  }
   122  
   123  func TestDualWriteLockAcquired(t *testing.T) {
   124  	locked := testDualWriteLock(t, 5*time.Second)
   125  
   126  	expected := true
   127  	if locked != expected {
   128  		t.Errorf("TestDualWriteLockAcquired(): \nexpected %#v\ngot      %#v", expected, locked)
   129  	}
   130  
   131  }
   132  
   133  func TestDualWriteLockTimedOut(t *testing.T) {
   134  	locked := testDualWriteLock(t, time.Second)
   135  
   136  	expected := false
   137  	if locked != expected {
   138  		t.Errorf("TestDualWriteLockTimedOut(): \nexpected %#v\ngot      %#v", expected, locked)
   139  	}
   140  
   141  }
   142  
   143  // Test cases below are copied 1 to 1 from sync/rwmutex_test.go (adapted to use DRWMutex)
   144  
   145  // Borrowed from rwmutex_test.go
   146  func parallelReader(ctx context.Context, m *DRWMutex, clocked, cunlock, cdone chan bool) {
   147  	if m.GetRLock(ctx, nil, id, source, Options{Timeout: time.Second}) {
   148  		clocked <- true
   149  		<-cunlock
   150  		m.RUnlock()
   151  		cdone <- true
   152  	}
   153  }
   154  
   155  // Borrowed from rwmutex_test.go
   156  func doTestParallelReaders(numReaders, gomaxprocs int) {
   157  	runtime.GOMAXPROCS(gomaxprocs)
   158  	m := NewDRWMutex(ds, "test-parallel")
   159  
   160  	clocked := make(chan bool)
   161  	cunlock := make(chan bool)
   162  	cdone := make(chan bool)
   163  	for i := 0; i < numReaders; i++ {
   164  		go parallelReader(context.Background(), m, clocked, cunlock, cdone)
   165  	}
   166  	// Wait for all parallel RLock()s to succeed.
   167  	for i := 0; i < numReaders; i++ {
   168  		<-clocked
   169  	}
   170  	for i := 0; i < numReaders; i++ {
   171  		cunlock <- true
   172  	}
   173  	// Wait for the goroutines to finish.
   174  	for i := 0; i < numReaders; i++ {
   175  		<-cdone
   176  	}
   177  }
   178  
   179  // Borrowed from rwmutex_test.go
   180  func TestParallelReaders(t *testing.T) {
   181  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
   182  	doTestParallelReaders(1, 4)
   183  	doTestParallelReaders(3, 4)
   184  	doTestParallelReaders(4, 2)
   185  }
   186  
   187  // Borrowed from rwmutex_test.go
   188  func reader(rwm *DRWMutex, numIterations int, activity *int32, cdone chan bool) {
   189  	for i := 0; i < numIterations; i++ {
   190  		if rwm.GetRLock(context.Background(), nil, id, source, Options{Timeout: time.Second}) {
   191  			n := atomic.AddInt32(activity, 1)
   192  			if n < 1 || n >= 10000 {
   193  				panic(fmt.Sprintf("wlock(%d)\n", n))
   194  			}
   195  			for i := 0; i < 100; i++ {
   196  			}
   197  			atomic.AddInt32(activity, -1)
   198  			rwm.RUnlock()
   199  		}
   200  	}
   201  	cdone <- true
   202  }
   203  
   204  // Borrowed from rwmutex_test.go
   205  func writer(rwm *DRWMutex, numIterations int, activity *int32, cdone chan bool) {
   206  	for i := 0; i < numIterations; i++ {
   207  		if rwm.GetLock(context.Background(), nil, id, source, Options{Timeout: time.Second}) {
   208  			n := atomic.AddInt32(activity, 10000)
   209  			if n != 10000 {
   210  				panic(fmt.Sprintf("wlock(%d)\n", n))
   211  			}
   212  			for i := 0; i < 100; i++ {
   213  			}
   214  			atomic.AddInt32(activity, -10000)
   215  			rwm.Unlock()
   216  		}
   217  	}
   218  	cdone <- true
   219  }
   220  
   221  // Borrowed from rwmutex_test.go
   222  func HammerRWMutex(gomaxprocs, numReaders, numIterations int) {
   223  	runtime.GOMAXPROCS(gomaxprocs)
   224  	// Number of active readers + 10000 * number of active writers.
   225  	var activity int32
   226  	rwm := NewDRWMutex(ds, "test")
   227  	cdone := make(chan bool)
   228  	go writer(rwm, numIterations, &activity, cdone)
   229  	var i int
   230  	for i = 0; i < numReaders/2; i++ {
   231  		go reader(rwm, numIterations, &activity, cdone)
   232  	}
   233  	go writer(rwm, numIterations, &activity, cdone)
   234  	for ; i < numReaders; i++ {
   235  		go reader(rwm, numIterations, &activity, cdone)
   236  	}
   237  	// Wait for the 2 writers and all readers to finish.
   238  	for i := 0; i < 2+numReaders; i++ {
   239  		<-cdone
   240  	}
   241  }
   242  
   243  // Borrowed from rwmutex_test.go
   244  func TestRWMutex(t *testing.T) {
   245  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
   246  	n := 100
   247  	if testing.Short() {
   248  		n = 5
   249  	}
   250  	HammerRWMutex(1, 1, n)
   251  	HammerRWMutex(1, 3, n)
   252  	HammerRWMutex(1, 10, n)
   253  	HammerRWMutex(4, 1, n)
   254  	HammerRWMutex(4, 3, n)
   255  	HammerRWMutex(4, 10, n)
   256  	HammerRWMutex(10, 1, n)
   257  	HammerRWMutex(10, 3, n)
   258  	HammerRWMutex(10, 10, n)
   259  	HammerRWMutex(10, 5, n)
   260  }
   261  
   262  // Borrowed from rwmutex_test.go
   263  func TestUnlockPanic(t *testing.T) {
   264  	defer func() {
   265  		if recover() == nil {
   266  			t.Fatalf("unlock of unlocked RWMutex did not panic")
   267  		}
   268  	}()
   269  	mu := NewDRWMutex(ds, "test")
   270  	mu.Unlock()
   271  }
   272  
   273  // Borrowed from rwmutex_test.go
   274  func TestUnlockPanic2(t *testing.T) {
   275  	defer func() {
   276  		if recover() == nil {
   277  			t.Fatalf("unlock of unlocked RWMutex did not panic")
   278  		}
   279  	}()
   280  	mu := NewDRWMutex(ds, "test-unlock-panic-2")
   281  	mu.RLock(id, source)
   282  	mu.Unlock()
   283  }
   284  
   285  // Borrowed from rwmutex_test.go
   286  func TestRUnlockPanic(t *testing.T) {
   287  	defer func() {
   288  		if recover() == nil {
   289  			t.Fatalf("read unlock of unlocked RWMutex did not panic")
   290  		}
   291  	}()
   292  	mu := NewDRWMutex(ds, "test")
   293  	mu.RUnlock()
   294  }
   295  
   296  // Borrowed from rwmutex_test.go
   297  func TestRUnlockPanic2(t *testing.T) {
   298  	defer func() {
   299  		if recover() == nil {
   300  			t.Fatalf("read unlock of unlocked RWMutex did not panic")
   301  		}
   302  	}()
   303  	mu := NewDRWMutex(ds, "test-runlock-panic-2")
   304  	mu.Lock(id, source)
   305  	mu.RUnlock()
   306  }
   307  
   308  // Borrowed from rwmutex_test.go
   309  func benchmarkRWMutex(b *testing.B, localWork, writeRatio int) {
   310  	rwm := NewDRWMutex(ds, "test")
   311  	b.RunParallel(func(pb *testing.PB) {
   312  		foo := 0
   313  		for pb.Next() {
   314  			foo++
   315  			if foo%writeRatio == 0 {
   316  				rwm.Lock(id, source)
   317  				rwm.Unlock()
   318  			} else {
   319  				rwm.RLock(id, source)
   320  				for i := 0; i != localWork; i++ {
   321  					foo *= 2
   322  					foo /= 2
   323  				}
   324  				rwm.RUnlock()
   325  			}
   326  		}
   327  		_ = foo
   328  	})
   329  }
   330  
   331  // Borrowed from rwmutex_test.go
   332  func BenchmarkRWMutexWrite100(b *testing.B) {
   333  	benchmarkRWMutex(b, 0, 100)
   334  }
   335  
   336  // Borrowed from rwmutex_test.go
   337  func BenchmarkRWMutexWrite10(b *testing.B) {
   338  	benchmarkRWMutex(b, 0, 10)
   339  }
   340  
   341  // Borrowed from rwmutex_test.go
   342  func BenchmarkRWMutexWorkWrite100(b *testing.B) {
   343  	benchmarkRWMutex(b, 100, 100)
   344  }
   345  
   346  // Borrowed from rwmutex_test.go
   347  func BenchmarkRWMutexWorkWrite10(b *testing.B) {
   348  	benchmarkRWMutex(b, 100, 10)
   349  }