| package locker |
| |
| import ( |
| "sync" |
| "testing" |
| "time" |
| ) |
| |
| 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) |
| }() |
| |
| 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") |
| } |
| |
| 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: |
| case <-time.After(3 * time.Second): |
| t.Fatalf("lock should have completed") |
| } |
| |
| 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: |
| case <-time.After(3 * time.Second): |
| t.Fatalf("lock should not be blocked") |
| } |
| } |
| |
| 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) |
| } |
| } |