1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package poll_test
6
7import (
8	. "internal/poll"
9	"math/rand"
10	"runtime"
11	"strings"
12	"testing"
13	"time"
14)
15
16func TestMutexLock(t *testing.T) {
17	var mu FDMutex
18
19	if !mu.Incref() {
20		t.Fatal("broken")
21	}
22	if mu.Decref() {
23		t.Fatal("broken")
24	}
25
26	if !mu.RWLock(true) {
27		t.Fatal("broken")
28	}
29	if mu.RWUnlock(true) {
30		t.Fatal("broken")
31	}
32
33	if !mu.RWLock(false) {
34		t.Fatal("broken")
35	}
36	if mu.RWUnlock(false) {
37		t.Fatal("broken")
38	}
39}
40
41func TestMutexClose(t *testing.T) {
42	var mu FDMutex
43	if !mu.IncrefAndClose() {
44		t.Fatal("broken")
45	}
46
47	if mu.Incref() {
48		t.Fatal("broken")
49	}
50	if mu.RWLock(true) {
51		t.Fatal("broken")
52	}
53	if mu.RWLock(false) {
54		t.Fatal("broken")
55	}
56	if mu.IncrefAndClose() {
57		t.Fatal("broken")
58	}
59}
60
61func TestMutexCloseUnblock(t *testing.T) {
62	c := make(chan bool, 4)
63	var mu FDMutex
64	mu.RWLock(true)
65	for i := 0; i < 4; i++ {
66		go func() {
67			if mu.RWLock(true) {
68				t.Error("broken")
69				return
70			}
71			c <- true
72		}()
73	}
74	// Concurrent goroutines must not be able to read lock the mutex.
75	time.Sleep(time.Millisecond)
76	select {
77	case <-c:
78		t.Fatal("broken")
79	default:
80	}
81	mu.IncrefAndClose() // Must unblock the readers.
82	for i := 0; i < 4; i++ {
83		select {
84		case <-c:
85		case <-time.After(10 * time.Second):
86			t.Fatal("broken")
87		}
88	}
89	if mu.Decref() {
90		t.Fatal("broken")
91	}
92	if !mu.RWUnlock(true) {
93		t.Fatal("broken")
94	}
95}
96
97func TestMutexPanic(t *testing.T) {
98	ensurePanics := func(f func()) {
99		defer func() {
100			if recover() == nil {
101				t.Fatal("does not panic")
102			}
103		}()
104		f()
105	}
106
107	var mu FDMutex
108	ensurePanics(func() { mu.Decref() })
109	ensurePanics(func() { mu.RWUnlock(true) })
110	ensurePanics(func() { mu.RWUnlock(false) })
111
112	ensurePanics(func() { mu.Incref(); mu.Decref(); mu.Decref() })
113	ensurePanics(func() { mu.RWLock(true); mu.RWUnlock(true); mu.RWUnlock(true) })
114	ensurePanics(func() { mu.RWLock(false); mu.RWUnlock(false); mu.RWUnlock(false) })
115
116	// ensure that it's still not broken
117	mu.Incref()
118	mu.Decref()
119	mu.RWLock(true)
120	mu.RWUnlock(true)
121	mu.RWLock(false)
122	mu.RWUnlock(false)
123}
124
125func TestMutexOverflowPanic(t *testing.T) {
126	defer func() {
127		r := recover()
128		if r == nil {
129			t.Fatal("did not panic")
130		}
131		msg, ok := r.(string)
132		if !ok {
133			t.Fatalf("unexpected panic type %T", r)
134		}
135		if !strings.Contains(msg, "too many") || strings.Contains(msg, "inconsistent") {
136			t.Fatalf("wrong panic message %q", msg)
137		}
138	}()
139
140	var mu1 FDMutex
141	for i := 0; i < 1<<21; i++ {
142		mu1.Incref()
143	}
144}
145
146func TestMutexStress(t *testing.T) {
147	P := 8
148	N := int(1e6)
149	if testing.Short() {
150		P = 4
151		N = 1e4
152	}
153	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P))
154	done := make(chan bool, P)
155	var mu FDMutex
156	var readState [2]uint64
157	var writeState [2]uint64
158	for p := 0; p < P; p++ {
159		go func() {
160			defer func() {
161				done <- !t.Failed()
162			}()
163			r := rand.New(rand.NewSource(rand.Int63()))
164			for i := 0; i < N; i++ {
165				switch r.Intn(3) {
166				case 0:
167					if !mu.Incref() {
168						t.Error("broken")
169						return
170					}
171					if mu.Decref() {
172						t.Error("broken")
173						return
174					}
175				case 1:
176					if !mu.RWLock(true) {
177						t.Error("broken")
178						return
179					}
180					// Ensure that it provides mutual exclusion for readers.
181					if readState[0] != readState[1] {
182						t.Error("broken")
183						return
184					}
185					readState[0]++
186					readState[1]++
187					if mu.RWUnlock(true) {
188						t.Error("broken")
189						return
190					}
191				case 2:
192					if !mu.RWLock(false) {
193						t.Error("broken")
194						return
195					}
196					// Ensure that it provides mutual exclusion for writers.
197					if writeState[0] != writeState[1] {
198						t.Error("broken")
199						return
200					}
201					writeState[0]++
202					writeState[1]++
203					if mu.RWUnlock(false) {
204						t.Error("broken")
205						return
206					}
207				}
208			}
209		}()
210	}
211	for p := 0; p < P; p++ {
212		if !<-done {
213			t.FailNow()
214		}
215	}
216	if !mu.IncrefAndClose() {
217		t.Fatal("broken")
218	}
219	if !mu.Decref() {
220		t.Fatal("broken")
221	}
222}
223