github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/pkg/locker/locker_test.go (about) 1 package locker // import "github.com/demonoid81/moby/pkg/locker" 2 3 import ( 4 "math/rand" 5 "strconv" 6 "sync" 7 "testing" 8 "time" 9 ) 10 11 func TestLockCounter(t *testing.T) { 12 l := &lockCtr{} 13 l.inc() 14 15 if l.waiters != 1 { 16 t.Fatal("counter inc failed") 17 } 18 19 l.dec() 20 if l.waiters != 0 { 21 t.Fatal("counter dec failed") 22 } 23 } 24 25 func TestLockerLock(t *testing.T) { 26 l := New() 27 l.Lock("test") 28 ctr := l.locks["test"] 29 30 if ctr.count() != 0 { 31 t.Fatalf("expected waiters to be 0, got :%d", ctr.waiters) 32 } 33 34 chDone := make(chan struct{}) 35 go func() { 36 l.Lock("test") 37 close(chDone) 38 }() 39 40 chWaiting := make(chan struct{}) 41 go func() { 42 for range time.Tick(1 * time.Millisecond) { 43 if ctr.count() == 1 { 44 close(chWaiting) 45 break 46 } 47 } 48 }() 49 50 select { 51 case <-chWaiting: 52 case <-time.After(3 * time.Second): 53 t.Fatal("timed out waiting for lock waiters to be incremented") 54 } 55 56 select { 57 case <-chDone: 58 t.Fatal("lock should not have returned while it was still held") 59 default: 60 } 61 62 if err := l.Unlock("test"); err != nil { 63 t.Fatal(err) 64 } 65 66 select { 67 case <-chDone: 68 case <-time.After(3 * time.Second): 69 t.Fatalf("lock should have completed") 70 } 71 72 if ctr.count() != 0 { 73 t.Fatalf("expected waiters to be 0, got: %d", ctr.count()) 74 } 75 } 76 77 func TestLockerUnlock(t *testing.T) { 78 l := New() 79 80 l.Lock("test") 81 l.Unlock("test") 82 83 chDone := make(chan struct{}) 84 go func() { 85 l.Lock("test") 86 close(chDone) 87 }() 88 89 select { 90 case <-chDone: 91 case <-time.After(3 * time.Second): 92 t.Fatalf("lock should not be blocked") 93 } 94 } 95 96 func TestLockerConcurrency(t *testing.T) { 97 l := New() 98 99 var wg sync.WaitGroup 100 for i := 0; i <= 10000; i++ { 101 wg.Add(1) 102 go func() { 103 l.Lock("test") 104 // if there is a concurrency issue, will very likely panic here 105 l.Unlock("test") 106 wg.Done() 107 }() 108 } 109 110 chDone := make(chan struct{}) 111 go func() { 112 wg.Wait() 113 close(chDone) 114 }() 115 116 select { 117 case <-chDone: 118 case <-time.After(10 * time.Second): 119 t.Fatal("timeout waiting for locks to complete") 120 } 121 122 // Since everything has unlocked this should not exist anymore 123 if ctr, exists := l.locks["test"]; exists { 124 t.Fatalf("lock should not exist: %v", ctr) 125 } 126 } 127 128 func BenchmarkLocker(b *testing.B) { 129 l := New() 130 for i := 0; i < b.N; i++ { 131 l.Lock("test") 132 l.Unlock("test") 133 } 134 } 135 136 func BenchmarkLockerParallel(b *testing.B) { 137 l := New() 138 b.SetParallelism(128) 139 b.RunParallel(func(pb *testing.PB) { 140 for pb.Next() { 141 l.Lock("test") 142 l.Unlock("test") 143 } 144 }) 145 } 146 147 func BenchmarkLockerMoreKeys(b *testing.B) { 148 l := New() 149 var keys []string 150 for i := 0; i < 64; i++ { 151 keys = append(keys, strconv.Itoa(i)) 152 } 153 b.SetParallelism(128) 154 b.RunParallel(func(pb *testing.PB) { 155 for pb.Next() { 156 k := keys[rand.Intn(len(keys))] 157 l.Lock(k) 158 l.Unlock(k) 159 } 160 }) 161 }