blob: 8aaa2f11d0f8a391055e69d177b78de531e01064 [file] [log] [blame]
Asim Shankare48c3ba2014-07-13 21:40:55 -07001// +build linux
2
3package bluetooth
4
5import (
6 "runtime"
7 "syscall"
8 "testing"
9)
10
11// TestConnConcurrency attempts to tests that methods on the *conn type be
12// friendly to concurrent invocation. Unable to figure out a clean way to do
13// this, the author has resorted to just firing up a bunch of goroutines and
14// hoping that failures will manifest often.
15func TestConnConcurrency(t *testing.T) {
16 const (
17 // These numbers were tuned to make the test fail "often"
18 // without the accompanying change to conn.go in the commit
19 // that added this test on the machine that the author was
20 // using at the time.
21 nConcurrentReaders = 30
22 nConcurrentClosers = 10
23 )
24 mp := runtime.GOMAXPROCS(nConcurrentReaders)
25 defer runtime.GOMAXPROCS(mp)
26
27 pipe := func() (rfd, wfd int) {
28 var fds [2]int
29 if err := syscall.Pipe(fds[:]); err != nil {
30 t.Fatal(err)
31 }
32 return fds[0], fds[1]
33 }
34 rfd, wfd := pipe()
35 rConn, _ := newConn(rfd, nil, nil)
36 wConn, _ := newConn(wfd, nil, nil)
37 const (
38 bugs = "bugs bunny"
39 daffy = "daffy duck"
40 )
41 rchan := make(chan string)
42 // Write a bunch of times
43 for i := 0; i < nConcurrentReaders; i++ {
44 go wConn.Write([]byte(bugs))
45 }
46 read := func() {
47 buf := make([]byte, len(bugs))
48 if n, err := rConn.Read(buf); err == nil {
49 rchan <- string(buf[:n])
50 return
51 }
52 rchan <- ""
53 }
54 // Fire up half the readers before Close
55 for i := 0; i < nConcurrentReaders; i += 2 {
56 go read()
57 }
58 // Fire up the closers (and attempt to reassign the file descriptors to
59 // something new).
60 for i := 0; i < nConcurrentClosers; i++ {
61 go func() {
62 rConn.Close()
63 // Create new FDs, which may re-use the closed file descriptors
64 // and write something else to them.
65 rfd, wfd := pipe()
66 syscall.Write(wfd, []byte(daffy))
67 syscall.Close(wfd)
68 syscall.Close(rfd)
69 }()
70 }
71 // And then the remaining readers
72 for i := 1; i < nConcurrentReaders; i += 2 {
73 go read()
74 }
75 // Now read from the channel, should either see full bugs bunnies or empty strings.
76 nEmpty := 0
77 for i := 0; i < nConcurrentReaders; i++ {
78 got := <-rchan
79 switch {
80 case len(got) == 0:
81 nEmpty++
82 case got != bugs:
83 t.Errorf("Read %q, wanted %q or empty string", got, bugs)
84 }
85 }
86 t.Logf("Read returned non-empty %d/%d times", (nConcurrentReaders - nEmpty), nConcurrentReaders)
87}