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)
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)
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			r := rand.New(rand.NewSource(rand.Int63()))
161			for i := 0; i < N; i++ {
162				switch r.Intn(3) {
163				case 0:
164					if !mu.Incref() {
165						t.Error("broken")
166						return
167					}
168					if mu.Decref() {
169						t.Error("broken")
170						return
171					}
172				case 1:
173					if !mu.RWLock(true) {
174						t.Error("broken")
175						return
176					}
177					// Ensure that it provides mutual exclusion for readers.
178					if readState[0] != readState[1] {
179						t.Error("broken")
180						return
181					}
182					readState[0]++
183					readState[1]++
184					if mu.RWUnlock(true) {
185						t.Error("broken")
186						return
187					}
188				case 2:
189					if !mu.RWLock(false) {
190						t.Error("broken")
191						return
192					}
193					// Ensure that it provides mutual exclusion for writers.
194					if writeState[0] != writeState[1] {
195						t.Error("broken")
196						return
197					}
198					writeState[0]++
199					writeState[1]++
200					if mu.RWUnlock(false) {
201						t.Error("broken")
202						return
203					}
204				}
205			}
206			done <- true
207		}()
208	}
209	for p := 0; p < P; p++ {
210		<-done
211	}
212	if !mu.IncrefAndClose() {
213		t.Fatal("broken")
214	}
215	if !mu.Decref() {
216		t.Fatal("broken")
217	}
218}
219