blob: 16d60a295627e0aa8e3f60fde45ac5d0ba93e8a3 [file] [log] [blame]
// Copyright 2016 The Netstack Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tmutex
import (
"sync"
"sync/atomic"
"testing"
"time"
)
func TestBasicLock(t *testing.T) {
var m Mutex
m.Init()
m.Lock()
// Try blocking lock the mutex from a different goroutine. This must
// not block because the mutex is held.
ch := make(chan struct{}, 1)
go func() {
m.Lock()
ch <- struct{}{}
m.Unlock()
ch <- struct{}{}
}()
select {
case <-ch:
t.Fatalf("Lock succeeded on locked mutex")
case <-time.After(100 * time.Millisecond):
}
// Unlock the mutex and make sure that the goroutine waiting on Lock()
// unblocks and succeeds.
m.Unlock()
select {
case <-ch:
case <-time.After(100 * time.Millisecond):
t.Fatalf("Lock failed to acquire unlocked mutex")
}
// Make sure we can lock and unlock again.
m.Lock()
m.Unlock()
}
func TestTryLock(t *testing.T) {
var m Mutex
m.Init()
// Try to lock. It should succeed.
if !m.TryLock() {
t.Fatalf("TryLock failed on unlocked mutex")
}
// Try to lock again, it should now fail.
if m.TryLock() {
t.Fatalf("TryLock succeeded on locked mutex")
}
// Try blocking lock the mutex from a different goroutine. This must
// not block because the mutex is held.
ch := make(chan struct{}, 1)
go func() {
m.Lock()
ch <- struct{}{}
m.Unlock()
}()
select {
case <-ch:
t.Fatalf("Lock succeeded on locked mutex")
case <-time.After(100 * time.Millisecond):
}
// Unlock the mutex and make sure that the goroutine waiting on Lock()
// unblocks and succeeds.
m.Unlock()
select {
case <-ch:
case <-time.After(100 * time.Millisecond):
t.Fatalf("Lock failed to acquire unlocked mutex")
}
}
func TestMutualExclusion(t *testing.T) {
var m Mutex
m.Init()
// Test mutual exclusion by running "gr" goroutines concurrently, and
// have each one increment a counter "iters" times within the critical
// section established by the mutex.
//
// If at the end the counter is not gr * iters, then we know that
// goroutines ran concurrently within the critical section.
//
// If one of the goroutines doesn't complete, it's likely a bug that
// causes to it to wait forever.
const gr = 1000
const iters = 100000
v := 0
var wg sync.WaitGroup
for i := 0; i < gr; i++ {
wg.Add(1)
go func() {
for j := 0; j < iters; j++ {
m.Lock()
v++
m.Unlock()
}
wg.Done()
}()
}
wg.Wait()
if v != gr*iters {
t.Fatalf("Bad count: got %v, want %v", v, gr*iters)
}
}
func TestMutualExclusionWithTryLock(t *testing.T) {
var m Mutex
m.Init()
// Similar to the previous, with the addition of some goroutines that
// only increment the count if TryLock succeeds.
const gr = 1000
const iters = 100000
total := int64(gr * iters)
v := int64(0)
var wg sync.WaitGroup
for i := 0; i < gr; i++ {
wg.Add(2)
go func() {
for j := 0; j < iters; j++ {
m.Lock()
v++
m.Unlock()
}
wg.Done()
}()
go func() {
local := int64(0)
for j := 0; j < iters; j++ {
if m.TryLock() {
v++
m.Unlock()
local++
}
}
atomic.AddInt64(&total, local)
wg.Done()
}()
}
wg.Wait()
if v != total {
t.Fatalf("Bad count: got %v, want %v", v, total)
}
}