2015-10-19 16:43:56 -04:00
|
|
|
package locker
|
|
|
|
|
|
|
|
import (
|
2017-10-02 14:14:42 -04:00
|
|
|
"math/rand"
|
|
|
|
"strconv"
|
2015-11-24 11:52:18 -05:00
|
|
|
"sync"
|
2015-10-19 16:43:56 -04:00
|
|
|
"testing"
|
2015-11-11 20:49:06 -05:00
|
|
|
"time"
|
2015-10-19 16:43:56 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestLockCounter(t *testing.T) {
|
|
|
|
l := &lockCtr{}
|
|
|
|
l.inc()
|
|
|
|
|
|
|
|
if l.waiters != 1 {
|
|
|
|
t.Fatal("counter inc failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
l.dec()
|
|
|
|
if l.waiters != 0 {
|
|
|
|
t.Fatal("counter dec failed")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestLockerLock(t *testing.T) {
|
|
|
|
l := New()
|
|
|
|
l.Lock("test")
|
|
|
|
ctr := l.locks["test"]
|
|
|
|
|
|
|
|
if ctr.count() != 0 {
|
|
|
|
t.Fatalf("expected waiters to be 0, got :%d", ctr.waiters)
|
|
|
|
}
|
|
|
|
|
|
|
|
chDone := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
l.Lock("test")
|
|
|
|
close(chDone)
|
|
|
|
}()
|
|
|
|
|
2015-11-11 20:49:06 -05:00
|
|
|
chWaiting := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
for range time.Tick(1 * time.Millisecond) {
|
|
|
|
if ctr.count() == 1 {
|
|
|
|
close(chWaiting)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-chWaiting:
|
|
|
|
case <-time.After(3 * time.Second):
|
|
|
|
t.Fatal("timed out waiting for lock waiters to be incremented")
|
|
|
|
}
|
2015-10-19 16:43:56 -04:00
|
|
|
|
|
|
|
select {
|
|
|
|
case <-chDone:
|
|
|
|
t.Fatal("lock should not have returned while it was still held")
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := l.Unlock("test"); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-chDone:
|
2015-11-11 20:49:06 -05:00
|
|
|
case <-time.After(3 * time.Second):
|
|
|
|
t.Fatalf("lock should have completed")
|
2015-10-19 16:43:56 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if ctr.count() != 0 {
|
|
|
|
t.Fatalf("expected waiters to be 0, got: %d", ctr.count())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestLockerUnlock(t *testing.T) {
|
|
|
|
l := New()
|
|
|
|
|
|
|
|
l.Lock("test")
|
|
|
|
l.Unlock("test")
|
|
|
|
|
|
|
|
chDone := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
l.Lock("test")
|
|
|
|
close(chDone)
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-chDone:
|
2015-11-11 20:49:06 -05:00
|
|
|
case <-time.After(3 * time.Second):
|
2015-10-19 16:43:56 -04:00
|
|
|
t.Fatalf("lock should not be blocked")
|
|
|
|
}
|
|
|
|
}
|
2015-11-24 11:52:18 -05:00
|
|
|
|
|
|
|
func TestLockerConcurrency(t *testing.T) {
|
|
|
|
l := New()
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
for i := 0; i <= 10000; i++ {
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
l.Lock("test")
|
|
|
|
// if there is a concurrency issue, will very likely panic here
|
|
|
|
l.Unlock("test")
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
chDone := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
wg.Wait()
|
|
|
|
close(chDone)
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-chDone:
|
|
|
|
case <-time.After(10 * time.Second):
|
|
|
|
t.Fatal("timeout waiting for locks to complete")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Since everything has unlocked this should not exist anymore
|
|
|
|
if ctr, exists := l.locks["test"]; exists {
|
|
|
|
t.Fatalf("lock should not exist: %v", ctr)
|
|
|
|
}
|
|
|
|
}
|
2017-10-02 14:14:42 -04:00
|
|
|
|
|
|
|
func BenchmarkLocker(b *testing.B) {
|
|
|
|
l := New()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
l.Lock("test")
|
|
|
|
l.Unlock("test")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkLockerParallel(b *testing.B) {
|
|
|
|
l := New()
|
|
|
|
b.SetParallelism(128)
|
|
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
|
|
for pb.Next() {
|
|
|
|
l.Lock("test")
|
|
|
|
l.Unlock("test")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkLockerMoreKeys(b *testing.B) {
|
|
|
|
l := New()
|
|
|
|
var keys []string
|
|
|
|
for i := 0; i < 64; i++ {
|
|
|
|
keys = append(keys, strconv.Itoa(i))
|
|
|
|
}
|
|
|
|
b.SetParallelism(128)
|
|
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
|
|
for pb.Next() {
|
|
|
|
k := keys[rand.Intn(len(keys))]
|
|
|
|
l.Lock(k)
|
|
|
|
l.Unlock(k)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|