@cryptotaxi247 / kubo / commits / ee4c727c8

update reuseport for the check

Juan Batiz-Benet committed Jan 20, 2015 at 19:34 UTC ee4c727c83e41e67788e897108e820735a7dba11
5 files changed +119 -3
Godeps/Godeps.json
+1 -1
@@ -160,7 +160,7 @@
160 },
161 {
162 "ImportPath": "github.com/jbenet/go-reuseport",
163 - "Rev": "1e1968c4744fef51234e83f015aa0187b4bd796b"
163 + "Rev": "a2e454f12a99b8898c41f9dcebae6c35dc3efa3a"
164 },
165 {
166 "ImportPath": "github.com/jbenet/go-sockaddr/net",
Godeps/_workspace/src/github.com/jbenet/go-reuseport/.travis.yml
+1 -2
@@ -1,11 +1,10 @@
1 language: go
2
3 go:
4 - - 1.2
4 - 1.3
5 - 1.4
6 - release
7 - tip
8
9 script:
11 - - go test -v ./...
10 + - go test -race -cpu=5 -v ./...
Godeps/_workspace/src/github.com/jbenet/go-reuseport/available_unix.go new
+90
@@ -0,0 +1,90 @@
1 +// +build darwin freebsd dragonfly netbsd openbsd linux
2 +package reuseport
3 +
4 +import (
5 + "sync"
6 + "sync/atomic"
7 + "syscall"
8 + "time"
9 +)
10 +
11 +// checker is a struct to gather the availability check fields + funcs.
12 +// we use atomic ints because this is potentially a really hot function call.
13 +type checkerT struct {
14 + avail int32 // atomic int managed by set/isAvailable()
15 + check int32 // atomic int managed by has/checked()
16 + mu sync.Mutex // synchonizes the actual check
17 +}
18 +
19 +// the static location of the vars.
20 +var checker checkerT
21 +
22 +func (c *checkerT) isAvailable() bool {
23 + return atomic.LoadInt32(&c.avail) != 0
24 +}
25 +
26 +func (c *checkerT) setIsAvailable(b bool) {
27 + if b {
28 + atomic.StoreInt32(&c.avail, 1)
29 + } else {
30 + atomic.StoreInt32(&c.avail, 0)
31 + }
32 +}
33 +
34 +func (c *checkerT) hasChecked() bool {
35 + return atomic.LoadInt32(&c.check) != 0
36 +}
37 +
38 +func (c *checkerT) setHasChecked(b bool) {
39 + if b {
40 + atomic.StoreInt32(&c.check, 1)
41 + } else {
42 + atomic.StoreInt32(&c.check, 0)
43 + }
44 +}
45 +
46 +// Available returns whether or not SO_REUSEPORT is available in the OS.
47 +// It does so by attepting to open a tcp listener, setting the option, and
48 +// checking ENOPROTOOPT on error. After checking, the decision is cached
49 +// for the rest of the process run.
50 +func available() bool {
51 + if checker.hasChecked() {
52 + return checker.isAvailable()
53 + }
54 +
55 + // synchronize, only one should check
56 + checker.mu.Lock()
57 + defer checker.mu.Unlock()
58 +
59 + // we blocked. someone may have been gotten this.
60 + if checker.hasChecked() {
61 + return checker.isAvailable()
62 + }
63 +
64 + // there may be fluke reasons to fail to add a listener.
65 + // so we give it 5 shots. if not, give up and call it not avail.
66 + for i := 0; i < 5; i++ {
67 + // try to listen at tcp port 0.
68 + l, err := listenStream("tcp", "127.0.0.1:0")
69 + if err == nil {
70 + // no error? available.
71 + checker.setIsAvailable(true)
72 + checker.setHasChecked(true)
73 + l.Close() // Go back to the Shadow!
74 + return true
75 + }
76 +
77 + if errno, ok := err.(syscall.Errno); ok {
78 + if errno == syscall.ENOPROTOOPT {
79 + break // :( that's all folks.
80 + }
81 + }
82 +
83 + // not an errno? or not ENOPROTOOPT? retry.
84 + <-time.After(20 * time.Millisecond) // wait a bit
85 + }
86 +
87 + checker.setIsAvailable(false)
88 + checker.setHasChecked(true)
89 + return false
90 +}
Godeps/_workspace/src/github.com/jbenet/go-reuseport/impl_windows.go
+7
@@ -13,3 +13,10 @@ func listen(network, address string) (net.Listener, error) {
13 func dial(dialer net.Dialer, network, address string) (net.Conn, error) {
14 return dialer.Dial(network, address)
15 }
16 +
17 +// on windows, we just use the regular functions. sources
18 +// vary on this-- some claim port reuse behavior is on by default
19 +// on some windows systems. So we try. may the force be with you.
20 +func available() bool {
21 + return true
22 +}
Godeps/_workspace/src/github.com/jbenet/go-reuseport/interface.go
+20
@@ -20,9 +20,18 @@ package reuseport
20 import (
21 "errors"
22 "net"
23 + "syscall"
24 "time"
25 )
26
27 +// Available returns whether or not SO_REUSEPORT is available in the OS.
28 +// It does so by attepting to open a tcp listener, setting the option, and
29 +// checking ENOPROTOOPT on error. After checking, the decision is cached
30 +// for the rest of the process run.
31 +func Available() bool {
32 + return available()
33 +}
34 +
35 // ErrUnsuportedProtocol signals that the protocol is not currently
36 // supported by this package. This package currently only supports TCP.
37 var ErrUnsupportedProtocol = errors.New("protocol not yet supported")
@@ -34,6 +43,10 @@ var ErrReuseFailed = errors.New("reuse failed")
43 // Returns a net.Listener created from a file discriptor for a socket
44 // with SO_REUSEPORT and SO_REUSEADDR option set.
45 func Listen(network, address string) (net.Listener, error) {
46 + if !available() {
47 + return nil, syscall.Errno(syscall.ENOPROTOOPT)
48 + }
49 +
50 return listenStream(network, address)
51 }
52
@@ -41,6 +54,10 @@ func Listen(network, address string) (net.Listener, error) {
54 // Returns a net.Listener created from a file discriptor for a socket
55 // with SO_REUSEPORT and SO_REUSEADDR option set.
56 func ListenPacket(network, address string) (net.PacketConn, error) {
57 + if !available() {
58 + return nil, syscall.Errno(syscall.ENOPROTOOPT)
59 + }
60 +
61 return listenPacket(network, address)
62 }
63
@@ -48,6 +65,9 @@ func ListenPacket(network, address string) (net.PacketConn, error) {
65 // Returns a net.Conn created from a file discriptor for a socket
66 // with SO_REUSEPORT and SO_REUSEADDR option set.
67 func Dial(network, laddr, raddr string) (net.Conn, error) {
68 + if !available() {
69 + return nil, syscall.Errno(syscall.ENOPROTOOPT)
70 + }
71
72 var d Dialer
73 if laddr != "" {