// Copyright 2013 The Go 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 net import ( "math/rand" "runtime" "testing" "time" ) func TestMutexLock(t *testing.T) { var mu fdMutex if !mu.Incref() { t.Fatal("broken") } if mu.Decref() { t.Fatal("broken") } if !mu.RWLock(true) { t.Fatal("broken") } if mu.RWUnlock(true) { t.Fatal("broken") } if !mu.RWLock(false) { t.Fatal("broken") } if mu.RWUnlock(false) { t.Fatal("broken") } } func TestMutexClose(t *testing.T) { var mu fdMutex if !mu.IncrefAndClose() { t.Fatal("broken") } if mu.Incref() { t.Fatal("broken") } if mu.RWLock(true) { t.Fatal("broken") } if mu.RWLock(false) { t.Fatal("broken") } if mu.IncrefAndClose() { t.Fatal("broken") } } func TestMutexCloseUnblock(t *testing.T) { c := make(chan bool) var mu fdMutex mu.RWLock(true) for i := 0; i < 4; i++ { go func() { if mu.RWLock(true) { t.Error("broken") return } c <- true }() } // Concurrent goroutines must not be able to read lock the mutex. time.Sleep(time.Millisecond) select { case <-c: t.Fatal("broken") default: } mu.IncrefAndClose() // Must unblock the readers. for i := 0; i < 4; i++ { select { case <-c: case <-time.After(10 * time.Second): t.Fatal("broken") } } if mu.Decref() { t.Fatal("broken") } if !mu.RWUnlock(true) { t.Fatal("broken") } } func TestMutexPanic(t *testing.T) { ensurePanics := func(f func()) { defer func() { if recover() == nil { t.Fatal("does not panic") } }() f() } var mu fdMutex ensurePanics(func() { mu.Decref() }) ensurePanics(func() { mu.RWUnlock(true) }) ensurePanics(func() { mu.RWUnlock(false) }) ensurePanics(func() { mu.Incref(); mu.Decref(); mu.Decref() }) ensurePanics(func() { mu.RWLock(true); mu.RWUnlock(true); mu.RWUnlock(true) }) ensurePanics(func() { mu.RWLock(false); mu.RWUnlock(false); mu.RWUnlock(false) }) // ensure that it's still not broken mu.Incref() mu.Decref() mu.RWLock(true) mu.RWUnlock(true) mu.RWLock(false) mu.RWUnlock(false) } func TestMutexStress(t *testing.T) { P := 8 N := int(1e6) if testing.Short() { P = 4 N = 1e4 } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P)) done := make(chan bool) var mu fdMutex var readState [2]uint64 var writeState [2]uint64 for p := 0; p < P; p++ { go func() { r := rand.New(rand.NewSource(rand.Int63())) for i := 0; i < N; i++ { switch r.Intn(3) { case 0: if !mu.Incref() { t.Error("broken") return } if mu.Decref() { t.Error("broken") return } case 1: if !mu.RWLock(true) { t.Error("broken") return } // Ensure that it provides mutual exclusion for readers. if readState[0] != readState[1] { t.Error("broken") return } readState[0]++ readState[1]++ if mu.RWUnlock(true) { t.Error("broken") return } case 2: if !mu.RWLock(false) { t.Error("broken") return } // Ensure that it provides mutual exclusion for writers. if writeState[0] != writeState[1] { t.Error("broken") return } writeState[0]++ writeState[1]++ if mu.RWUnlock(false) { t.Error("broken") return } } } done <- true }() } for p := 0; p < P; p++ { <-done } if !mu.IncrefAndClose() { t.Fatal("broken") } if !mu.Decref() { t.Fatal("broken") } }