github.com/prysmaticlabs/prysm@v1.4.4/shared/mputil/multilock_test.go (about) 1 /* 2 Copyright 2017 Albert Tedja 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 http://www.apache.org/licenses/LICENSE-2.0 7 Unless required by applicable law or agreed to in writing, software 8 distributed under the License is distributed on an "AS IS" BASIS, 9 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 See the License for the specific language governing permissions and 11 limitations under the License. 12 */ 13 package mputil 14 15 import ( 16 "sync" 17 "testing" 18 "time" 19 20 "github.com/stretchr/testify/assert" 21 ) 22 23 func TestUnique(t *testing.T) { 24 var arr []string 25 assert := assert.New(t) 26 27 arr = []string{"a", "b", "c"} 28 assert.Equal(arr, unique(arr)) 29 30 arr = []string{"a", "a", "a"} 31 assert.Equal([]string{"a"}, unique(arr)) 32 33 arr = []string{"a", "a", "b"} 34 assert.Equal([]string{"a", "b"}, unique(arr)) 35 36 arr = []string{"a", "b", "a"} 37 assert.Equal([]string{"a", "b"}, unique(arr)) 38 39 arr = []string{"a", "b", "c", "b", "d"} 40 assert.Equal([]string{"a", "b", "c", "d"}, unique(arr)) 41 } 42 43 func TestGetChan(t *testing.T) { 44 ch1 := getChan("a") 45 ch2 := getChan("aa") 46 ch3 := getChan("a") 47 48 assert := assert.New(t) 49 assert.NotEqual(ch1, ch2) 50 assert.Equal(ch1, ch3) 51 } 52 53 func TestLockUnlock(t *testing.T) { 54 var wg sync.WaitGroup 55 56 wg.Add(5) 57 58 go func() { 59 lock := NewMultilock("dog", "cat", "owl") 60 lock.Lock() 61 defer lock.Unlock() 62 63 <-time.After(100 * time.Millisecond) 64 wg.Done() 65 }() 66 67 go func() { 68 lock := NewMultilock("cat", "dog", "bird") 69 lock.Lock() 70 defer lock.Unlock() 71 72 <-time.After(100 * time.Millisecond) 73 wg.Done() 74 }() 75 76 go func() { 77 lock := NewMultilock("cat", "bird", "owl") 78 lock.Lock() 79 defer lock.Unlock() 80 81 <-time.After(100 * time.Millisecond) 82 wg.Done() 83 }() 84 85 go func() { 86 lock := NewMultilock("bird", "owl", "snake") 87 lock.Lock() 88 defer lock.Unlock() 89 90 <-time.After(100 * time.Millisecond) 91 wg.Done() 92 }() 93 94 go func() { 95 lock := NewMultilock("owl", "snake") 96 lock.Lock() 97 defer lock.Unlock() 98 99 <-time.After(1 * time.Second) 100 wg.Done() 101 }() 102 103 wg.Wait() 104 } 105 106 func TestLockUnlock_CleansUnused(t *testing.T) { 107 var wg sync.WaitGroup 108 wg.Add(1) 109 go func() { 110 lock := NewMultilock("dog", "cat", "owl") 111 lock.Lock() 112 assert.Equal(t, 3, len(locks.list)) 113 defer lock.Unlock() 114 115 <-time.After(100 * time.Millisecond) 116 wg.Done() 117 }() 118 wg.Wait() 119 // We expect that unlocking completely cleared the locks list 120 // given all 3 lock keys were unused at time of unlock. 121 assert.Equal(t, 0, len(locks.list)) 122 } 123 124 func TestLockUnlock_DoesNotCleanIfHeldElsewhere(t *testing.T) { 125 var wg sync.WaitGroup 126 wg.Add(2) 127 go func() { 128 lock := NewMultilock("cat") 129 lock.Lock() 130 // We take 200 milliseconds to release the lock on "cat" 131 <-time.After(200 * time.Millisecond) 132 lock.Unlock() 133 // Assert that at the end of this goroutine, all locks are cleared. 134 assert.Equal(t, 0, len(locks.list)) 135 wg.Done() 136 }() 137 go func() { 138 lock := NewMultilock("dog", "cat", "owl") 139 lock.Lock() 140 // We release the locks after 100 milliseconds, and check that "cat" is not 141 // cleared as a lock for it is still held by the previous goroutine. 142 <-time.After(100 * time.Millisecond) 143 lock.Unlock() 144 assert.Equal(t, 1, len(locks.list)) 145 _, ok := locks.list["cat"] 146 assert.Equal(t, true, ok) 147 wg.Done() 148 }() 149 wg.Wait() 150 // We expect that at the end of this test, all locks are cleared. 151 assert.Equal(t, 0, len(locks.list)) 152 } 153 154 func TestYield(t *testing.T) { 155 var wg sync.WaitGroup 156 157 wg.Add(2) 158 var resources = map[string]int{} 159 160 go func() { 161 lock := NewMultilock("A", "C") 162 lock.Lock() 163 defer lock.Unlock() 164 165 for resources["ac"] == 0 { 166 lock.Yield() 167 } 168 resources["dc"] = 10 169 170 wg.Done() 171 }() 172 173 go func() { 174 lock := NewMultilock("D", "C") 175 lock.Lock() 176 defer lock.Unlock() 177 178 resources["ac"] = 5 179 for resources["dc"] == 0 { 180 lock.Yield() 181 } 182 183 wg.Done() 184 }() 185 186 wg.Wait() 187 188 assert.Equal(t, 5, resources["ac"]) 189 assert.Equal(t, 10, resources["dc"]) 190 } 191 192 func TestClean(t *testing.T) { 193 var wg sync.WaitGroup 194 195 wg.Add(3) 196 197 // some goroutine that holds multiple locks 198 go1done := make(chan bool, 1) 199 go func() { 200 Loop: 201 for { 202 select { 203 case <-go1done: 204 break Loop 205 default: 206 lock := NewMultilock("A", "B", "C", "E", "Z") 207 lock.Lock() 208 <-time.After(30 * time.Millisecond) 209 lock.Unlock() 210 } 211 } 212 wg.Done() 213 }() 214 215 // another goroutine 216 go2done := make(chan bool, 1) 217 go func() { 218 Loop: 219 for { 220 select { 221 case <-go2done: 222 break Loop 223 default: 224 lock := NewMultilock("B", "C", "K", "L", "Z") 225 lock.Lock() 226 <-time.After(200 * time.Millisecond) 227 lock.Unlock() 228 } 229 } 230 wg.Done() 231 }() 232 233 // this one cleans up the locks every 100 ms 234 done := make(chan bool, 1) 235 go func() { 236 c := time.Tick(100 * time.Millisecond) 237 Loop: 238 for { 239 select { 240 case <-done: 241 break Loop 242 case <-c: 243 Clean() 244 } 245 } 246 wg.Done() 247 }() 248 249 <-time.After(2 * time.Second) 250 go1done <- true 251 go2done <- true 252 <-time.After(1 * time.Second) 253 done <- true 254 wg.Wait() 255 assert.Equal(t, []string{}, Clean()) 256 } 257 258 func TestBankAccountProblem(t *testing.T) { 259 var wg sync.WaitGroup 260 wg.Add(3) 261 262 joe := 50.0 263 susan := 100.0 264 265 // withdraw $80 from joe, only if balance is sufficient 266 go func() { 267 lock := NewMultilock("joe") 268 lock.Lock() 269 defer lock.Unlock() 270 271 for joe < 80.0 { 272 lock.Yield() 273 } 274 joe -= 80.0 275 276 wg.Done() 277 }() 278 279 // transfer $200 from susan to joe, only if balance is sufficient 280 go func() { 281 lock := NewMultilock("joe", "susan") 282 lock.Lock() 283 defer lock.Unlock() 284 285 for susan < 200.0 { 286 lock.Yield() 287 } 288 289 susan -= 200.0 290 joe += 200.0 291 292 wg.Done() 293 }() 294 295 // susan deposit $300 to cover balance 296 go func() { 297 lock := NewMultilock("susan") 298 lock.Lock() 299 defer lock.Unlock() 300 301 susan += 300.0 302 303 wg.Done() 304 }() 305 306 wg.Wait() 307 assert.Equal(t, 170.0, joe) 308 assert.Equal(t, 200.0, susan) 309 } 310 311 func TestSyncCondCompatibility(t *testing.T) { 312 var wg sync.WaitGroup 313 wg.Add(2) 314 cond := sync.NewCond(NewMultilock("A", "C")) 315 var testValues = [3]string{"foo", "bar", "fizz!"} 316 sharedRsc := testValues[0] 317 318 go func() { 319 cond.L.Lock() 320 for sharedRsc == testValues[0] { 321 cond.Wait() 322 } 323 sharedRsc = testValues[2] 324 cond.Broadcast() 325 cond.L.Unlock() 326 wg.Done() 327 }() 328 329 go func() { 330 cond.L.Lock() 331 sharedRsc = testValues[1] 332 cond.Broadcast() 333 for sharedRsc == testValues[1] { 334 cond.Wait() 335 } 336 cond.L.Unlock() 337 wg.Done() 338 }() 339 340 wg.Wait() 341 assert.Equal(t, testValues[2], sharedRsc) 342 }