|  | // 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 poll_test | 
|  |  | 
|  | import ( | 
|  | . "internal/poll" | 
|  | "math/rand" | 
|  | "runtime" | 
|  | "strings" | 
|  | "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, 4) | 
|  | 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 TestMutexOverflowPanic(t *testing.T) { | 
|  | defer func() { | 
|  | r := recover() | 
|  | if r == nil { | 
|  | t.Fatal("did not panic") | 
|  | } | 
|  | msg, ok := r.(string) | 
|  | if !ok { | 
|  | t.Fatalf("unexpected panic type %T", r) | 
|  | } | 
|  | if !strings.Contains(msg, "too many") || strings.Contains(msg, "inconsistent") { | 
|  | t.Fatalf("wrong panic message %q", msg) | 
|  | } | 
|  | }() | 
|  |  | 
|  | var mu1 FDMutex | 
|  | for i := 0; i < 1<<21; i++ { | 
|  | mu1.Incref() | 
|  | } | 
|  | } | 
|  |  | 
|  | 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, P) | 
|  | var mu FDMutex | 
|  | var readState [2]uint64 | 
|  | var writeState [2]uint64 | 
|  | for p := 0; p < P; p++ { | 
|  | go func() { | 
|  | defer func() { | 
|  | done <- !t.Failed() | 
|  | }() | 
|  | 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 | 
|  | } | 
|  | } | 
|  | } | 
|  | }() | 
|  | } | 
|  | for p := 0; p < P; p++ { | 
|  | if !<-done { | 
|  | t.FailNow() | 
|  | } | 
|  | } | 
|  | if !mu.IncrefAndClose() { | 
|  | t.Fatal("broken") | 
|  | } | 
|  | if !mu.Decref() { | 
|  | t.Fatal("broken") | 
|  | } | 
|  | } |