github.com/zhiqiangxu/util@v0.0.0-20230112053021-0a7aee056cd5/mutex/crwmutex_test.go (about)

     1  package mutex
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  )
     8  
     9  func TestCRWMutex(t *testing.T) {
    10  	crwm := NewCRWMutex()
    11  	// test RLock/RUnlock
    12  	err := crwm.RLock(context.Background())
    13  	if err != nil {
    14  		t.FailNow()
    15  	}
    16  
    17  	crwm.RUnlock()
    18  
    19  	// test Lock/Unlock
    20  	err = crwm.Lock(context.Background())
    21  	if err != nil {
    22  		t.FailNow()
    23  	}
    24  	crwm.Unlock()
    25  
    26  	// test TryLock/Unlock
    27  	ok := crwm.TryLock()
    28  	if !ok {
    29  		t.FailNow()
    30  	}
    31  	crwm.Unlock()
    32  
    33  	// test TryRLock/Unlock
    34  	ok = crwm.TryRLock()
    35  	if !ok {
    36  		t.FailNow()
    37  	}
    38  	crwm.RUnlock()
    39  
    40  	// test RLock/canceled Lock/RUnlock
    41  	err = crwm.RLock(context.Background())
    42  	if err != nil {
    43  		t.FailNow()
    44  	}
    45  
    46  	ctx, cancelFunc := context.WithTimeout(context.Background(), time.Millisecond*300)
    47  	defer cancelFunc()
    48  	err = crwm.Lock(ctx)
    49  	if err == nil {
    50  		t.FailNow()
    51  	}
    52  
    53  	crwm.RUnlock()
    54  
    55  }
    56  
    57  func TestSemaphoreBug(t *testing.T) {
    58  	crwm := NewCRWMutex()
    59  	// hold 1 read lock
    60  	crwm.RLock(context.Background())
    61  
    62  	go func() {
    63  		ctx, cancelFunc := context.WithTimeout(context.Background(), time.Millisecond*300)
    64  		defer cancelFunc()
    65  		// start a Lock request that will giveup after 300ms
    66  		err := crwm.Lock(ctx)
    67  		if err == nil {
    68  			t.FailNow()
    69  		}
    70  	}()
    71  
    72  	// sleep 100ms, long enough for the Lock request to be queued
    73  	time.Sleep(time.Millisecond * 100)
    74  	// this channel will be closed if the following RLock succeeded
    75  	doneCh := make(chan struct{})
    76  	go func() {
    77  		// try to grab a read lock, it will be queued after the Lock request
    78  		// but should be notified when the Lock request is canceled
    79  		// this doesn't happen because there's a bug in semaphore
    80  		err := crwm.RLock(context.Background())
    81  		if err != nil {
    82  			t.FailNow()
    83  		}
    84  		crwm.RUnlock()
    85  		close(doneCh)
    86  	}()
    87  
    88  	// because of the bug in semaphore, doneCh is never closed
    89  	select {
    90  	case <-doneCh:
    91  	case <-time.After(time.Second):
    92  		t.FailNow()
    93  	}
    94  
    95  }