github.com/whoyao/protocol@v0.0.0-20230519045905-2d8ace718ca5/utils/lock_tracker_test.go (about)

     1  package utils_test
     2  
     3  import (
     4  	"fmt"
     5  	"runtime"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  
    12  	"github.com/whoyao/protocol/utils"
    13  )
    14  
    15  func init() {
    16  	utils.EnableLockTracker()
    17  }
    18  
    19  func cleanupTest() {
    20  	runtime.GC()
    21  	time.Sleep(time.Millisecond)
    22  }
    23  
    24  func noop() {}
    25  
    26  func TestScanTrackedLocks(t *testing.T) {
    27  	t.Cleanup(cleanupTest)
    28  	assert.Nil(t, utils.ScanTrackedLocks(time.Millisecond))
    29  
    30  	ms := make([]*utils.Mutex, 100)
    31  	for i := range ms {
    32  		m := &utils.Mutex{}
    33  		m.Lock()
    34  		noop()
    35  		m.Unlock()
    36  		ms[i] = m
    37  	}
    38  
    39  	go func() {
    40  		ms[50].Lock()
    41  		ms[50].Lock()
    42  	}()
    43  
    44  	time.Sleep(5 * time.Millisecond)
    45  	assert.NotNil(t, utils.ScanTrackedLocks(time.Millisecond))
    46  
    47  	ms[50].Unlock()
    48  }
    49  
    50  func TestMutexFinalizer(t *testing.T) {
    51  	cleanupTest()
    52  	assert.Equal(t, 0, utils.NumMutexes())
    53  
    54  	go func() {
    55  		m := &utils.Mutex{}
    56  		m.Lock()
    57  		go func() {
    58  			m.Unlock()
    59  		}()
    60  		assert.Equal(t, 1, utils.NumMutexes())
    61  	}()
    62  
    63  	time.Sleep(time.Millisecond)
    64  	cleanupTest()
    65  
    66  	assert.Equal(t, 0, utils.NumMutexes())
    67  }
    68  
    69  func TestEmbeddedMutex(t *testing.T) {
    70  	t.Cleanup(cleanupTest)
    71  
    72  	foo := struct{ m utils.Mutex }{}
    73  	foo.m.Lock()
    74  	noop()
    75  	foo.m.Unlock()
    76  
    77  	bar := struct{ utils.Mutex }{}
    78  	bar.Lock()
    79  	noop()
    80  	bar.Unlock()
    81  }
    82  
    83  func TestContestedGlobalLock(t *testing.T) {
    84  	t.Cleanup(cleanupTest)
    85  
    86  	ms := make([]*utils.Mutex, 100)
    87  	for i := range ms {
    88  		m := &utils.Mutex{}
    89  		m.Lock()
    90  		noop()
    91  		m.Unlock()
    92  		ms[i] = m
    93  	}
    94  
    95  	var wg sync.WaitGroup
    96  	wg.Add(2)
    97  
    98  	go func() {
    99  		for i := 0; i < 100; i++ {
   100  			wg.Add(1)
   101  			go func() {
   102  				utils.ScanTrackedLocks(time.Minute)
   103  				wg.Done()
   104  			}()
   105  		}
   106  		wg.Done()
   107  	}()
   108  
   109  	go func() {
   110  		for i := 0; i < 100; i++ {
   111  			var m utils.Mutex
   112  			wg.Add(3)
   113  			for i := 0; i < 3; i++ {
   114  				go func() {
   115  					m.Lock()
   116  					noop()
   117  					m.Unlock()
   118  					wg.Done()
   119  				}()
   120  			}
   121  		}
   122  		wg.Done()
   123  	}()
   124  
   125  	wg.Wait()
   126  }
   127  
   128  func TestInitRace(t *testing.T) {
   129  	t.Cleanup(cleanupTest)
   130  
   131  	var wg sync.WaitGroup
   132  	for i := 0; i < 100; i++ {
   133  		var m utils.Mutex
   134  		wg.Add(3)
   135  		done := make(chan struct{})
   136  		for i := 0; i < 3; i++ {
   137  			go func() {
   138  				<-done
   139  				m.Lock()
   140  				noop()
   141  				m.Unlock()
   142  				wg.Done()
   143  			}()
   144  		}
   145  		close(done)
   146  		runtime.Gosched()
   147  	}
   148  
   149  	wg.Wait()
   150  }
   151  
   152  func BenchmarkLockTracker(b *testing.B) {
   153  	b.Run("wrapped mutex", func(b *testing.B) {
   154  		var m utils.Mutex
   155  		for i := 0; i < b.N; i++ {
   156  			m.Lock()
   157  			noop()
   158  			m.Unlock()
   159  		}
   160  	})
   161  	b.Run("native mutex", func(b *testing.B) {
   162  		var m sync.Mutex
   163  		for i := 0; i < b.N; i++ {
   164  			m.Lock()
   165  			noop()
   166  			m.Unlock()
   167  		}
   168  	})
   169  	b.Run("wrapped rwmutex", func(b *testing.B) {
   170  		var m utils.RWMutex
   171  		for i := 0; i < b.N; i++ {
   172  			m.Lock()
   173  			noop()
   174  			m.Unlock()
   175  		}
   176  	})
   177  	b.Run("native rwmutex", func(b *testing.B) {
   178  		var m sync.RWMutex
   179  		for i := 0; i < b.N; i++ {
   180  			m.Lock()
   181  			noop()
   182  			m.Unlock()
   183  		}
   184  	})
   185  
   186  	b.Run("wrapped mutex init", func(b *testing.B) {
   187  		for i := 0; i < b.N; i++ {
   188  			var m utils.Mutex
   189  			m.Lock()
   190  			noop()
   191  			m.Unlock()
   192  		}
   193  	})
   194  	b.Run("native mutex init", func(b *testing.B) {
   195  		for i := 0; i < b.N; i++ {
   196  			var m sync.Mutex
   197  			m.Lock()
   198  			noop()
   199  			m.Unlock()
   200  		}
   201  	})
   202  	b.Run("wrapped rwmutex init", func(b *testing.B) {
   203  		for i := 0; i < b.N; i++ {
   204  			var m utils.RWMutex
   205  			m.Lock()
   206  			noop()
   207  			m.Unlock()
   208  		}
   209  	})
   210  	b.Run("native rwmutex init", func(b *testing.B) {
   211  		for i := 0; i < b.N; i++ {
   212  			var m sync.RWMutex
   213  			m.Lock()
   214  			noop()
   215  			m.Unlock()
   216  		}
   217  	})
   218  }
   219  
   220  func BenchmarkGetBlocked(b *testing.B) {
   221  	for n := 100; n <= 1000000; n *= 100 {
   222  		n := n
   223  		b.Run(fmt.Sprintf("serial/%d", n), func(b *testing.B) {
   224  			cleanupTest()
   225  
   226  			ms := make([]*utils.Mutex, n)
   227  			for i := range ms {
   228  				m := &utils.Mutex{}
   229  				m.Lock()
   230  				noop()
   231  				m.Unlock()
   232  				ms[i] = m
   233  			}
   234  
   235  			b.ResetTimer()
   236  
   237  			for i := 0; i < b.N; i++ {
   238  				utils.ScanTrackedLocks(time.Minute)
   239  			}
   240  		})
   241  	}
   242  }