go.temporal.io/server@v1.23.0/common/locks/priority_mutex_test.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package locks 26 27 import ( 28 "context" 29 "fmt" 30 "sync" 31 "testing" 32 33 "github.com/stretchr/testify/require" 34 "github.com/stretchr/testify/suite" 35 ) 36 37 type ( 38 priorityMutexSuite struct { 39 *require.Assertions 40 suite.Suite 41 42 lock *PriorityMutexImpl 43 } 44 ) 45 46 func BenchmarkPriorityMutex_High(b *testing.B) { 47 b.ReportAllocs() 48 49 lock := NewPriorityMutex() 50 ctx := context.Background() 51 for n := 0; n < b.N; n++ { 52 _ = lock.LockHigh(ctx) 53 lock.UnlockHigh() 54 } 55 } 56 57 func BenchmarkPriorityMutex_Low(b *testing.B) { 58 b.ReportAllocs() 59 60 lock := NewPriorityMutex() 61 ctx := context.Background() 62 for n := 0; n < b.N; n++ { 63 _ = lock.LockLow(ctx) 64 lock.UnlockLow() 65 } 66 } 67 68 func TestPriorityMutexSuite(t *testing.T) { 69 s := new(priorityMutexSuite) 70 suite.Run(t, s) 71 } 72 73 func (s *priorityMutexSuite) SetupSuite() { 74 s.Assertions = require.New(s.T()) 75 } 76 77 func (s *priorityMutexSuite) TearDownSuite() { 78 79 } 80 81 func (s *priorityMutexSuite) SetupTest() { 82 s.lock = NewPriorityMutex() 83 } 84 85 func (s *priorityMutexSuite) TearDownTest() { 86 87 } 88 89 func (s *priorityMutexSuite) TestTryLock_High() { 90 s.True(s.lock.TryLockHigh()) 91 s.False(s.lock.TryLockHigh()) 92 s.False(s.lock.TryLockLow()) 93 s.lock.UnlockHigh() 94 s.False(s.lock.IsLocked()) 95 } 96 97 func (s *priorityMutexSuite) TestLock_High_Success() { 98 ctx := context.Background() 99 err := s.lock.LockHigh(ctx) 100 s.NoError(err) 101 s.True(s.lock.IsLocked()) 102 s.lock.UnlockHigh() 103 s.False(s.lock.IsLocked()) 104 } 105 106 func (s *priorityMutexSuite) TestLock_High_Fail() { 107 ctx := context.Background() 108 ctx, cancel := context.WithCancel(ctx) 109 cancel() 110 111 err := s.lock.LockHigh(ctx) 112 s.Error(err) 113 s.False(s.lock.IsLocked()) 114 } 115 116 func (s *priorityMutexSuite) TestTryLock_Low() { 117 s.True(s.lock.TryLockLow()) 118 s.False(s.lock.TryLockHigh()) 119 s.False(s.lock.TryLockLow()) 120 s.lock.UnlockLow() 121 s.False(s.lock.IsLocked()) 122 } 123 124 func (s *priorityMutexSuite) TestLock_Low_Success() { 125 ctx := context.Background() 126 err := s.lock.LockLow(ctx) 127 s.NoError(err) 128 s.True(s.lock.IsLocked()) 129 s.lock.UnlockLow() 130 s.False(s.lock.IsLocked()) 131 } 132 133 func (s *priorityMutexSuite) TestLock_Low_Fail() { 134 ctx := context.Background() 135 ctx, cancel := context.WithCancel(ctx) 136 cancel() 137 138 err := s.lock.LockLow(ctx) 139 s.Error(err) 140 s.False(s.lock.IsLocked()) 141 } 142 143 func (s *priorityMutexSuite) Test_LockHigh_UnlockLow() { 144 ctx := context.Background() 145 146 err := s.lock.LockHigh(ctx) 147 s.NoError(err) 148 s.Panics(s.lock.UnlockLow) 149 s.lock.UnlockHigh() 150 } 151 152 func (s *priorityMutexSuite) Test_LockLow_UnlockHigh() { 153 ctx := context.Background() 154 155 err := s.lock.LockLow(ctx) 156 s.NoError(err) 157 s.Panics(s.lock.UnlockHigh) 158 s.lock.UnlockLow() 159 } 160 161 func (s *priorityMutexSuite) TestLock_Mixed() { 162 concurrency := 1024 163 ctx := context.Background() 164 err := s.lock.LockHigh(ctx) 165 s.NoError(err) 166 167 startWaitGroup := sync.WaitGroup{} 168 endWaitGroup := sync.WaitGroup{} 169 startWaitGroup.Add(concurrency * 2) 170 endWaitGroup.Add(concurrency * 2) 171 172 resultChan := make(chan int, concurrency*2) 173 174 lowFn := func() { 175 startWaitGroup.Done() 176 err := s.lock.LockLow(ctx) 177 s.NoError(err) 178 resultChan <- 0 179 180 s.lock.UnlockLow() 181 endWaitGroup.Done() 182 } 183 highFn := func() { 184 startWaitGroup.Done() 185 err := s.lock.LockHigh(ctx) 186 s.NoError(err) 187 resultChan <- 1 188 189 s.lock.UnlockHigh() 190 endWaitGroup.Done() 191 } 192 for i := 0; i < concurrency; i++ { 193 go lowFn() 194 go highFn() 195 } 196 197 startWaitGroup.Wait() 198 s.lock.UnlockHigh() 199 endWaitGroup.Wait() 200 close(resultChan) 201 202 results := make([]int, 0, 2*concurrency) 203 for result := range resultChan { 204 results = append(results, result) 205 } 206 s.Equal(2*concurrency, len(results)) 207 208 zeros := float64(0) 209 totalZeros := float64(concurrency) 210 possibility := float64(0) 211 for i := 2*concurrency - 1; i > -1; i-- { 212 switch results[i] { 213 case 0: 214 zeros += 1 215 case 1: 216 possibility += zeros / totalZeros 217 default: 218 panic("unexpected number, can only be 0 or 1") 219 } 220 } 221 222 overallPossibility := possibility / float64(concurrency) 223 fmt.Printf("overall possibility: %.2f\n", overallPossibility) 224 s.True(overallPossibility >= 0.5) 225 }