connect timing fixes to reuseport
Juan Batiz-Benet committed
Jan 19, 2015 at 16:56 UTC
9062b9fe6cae78e3b6e24903d9368ea7dffd45fd
3 files changed
+31
-16
Godeps/Godeps.json
+1
-1
@@ -160,7 +160,7 @@
160
},
161
{
162
"ImportPath": "github.com/jbenet/go-reuseport",
163
- "Rev": "f2ab96a83e1b33b66478eedd314884755d771933"
163
+ "Rev": "1e1968c4744fef51234e83f015aa0187b4bd796b"
164
},
165
{
166
"ImportPath": "github.com/jbenet/go-sockaddr/net",
Godeps/_workspace/src/github.com/jbenet/go-reuseport/impl_unix.go
+26
-11
@@ -106,18 +106,27 @@ func dial(dialer net.Dialer, netw, addr string) (c net.Conn, err error) {
106
}
107
}
108
109
- if fd, err = socket(rfamily, socktype, rprotocol); err != nil {
110
- return nil, err
111
- }
109
+ // look at dialTCP in http://golang.org/src/net/tcpsock_posix.go .... !
110
+ // here we just try again 3 times.
111
+ for i := 0; i < 3; i++ {
112
+ if fd, err = socket(rfamily, socktype, rprotocol); err != nil {
113
+ return nil, err
114
+ }
115
113
- if err = syscall.Bind(fd, localSockaddr); err != nil {
114
- // fmt.Println("bind failed")
115
- syscall.Close(fd)
116
- return nil, err
116
+ if err = syscall.Bind(fd, localSockaddr); err != nil {
117
+ // fmt.Println("bind failed")
118
+ syscall.Close(fd)
119
+ return nil, err
120
+ }
121
+ if err = connect(fd, remoteSockaddr); err != nil {
122
+ syscall.Close(fd)
123
+ // fmt.Println("connect failed", localSockaddr, err)
124
+ continue // try again.
125
+ }
126
+
127
+ break
128
}
118
- if err = connect(fd, remoteSockaddr); err != nil {
119
- syscall.Close(fd)
120
- // fmt.Println("connect failed", localSockaddr, err)
129
+ if err != nil {
130
return nil, err
131
}
132
@@ -314,6 +323,7 @@ func connect(fd int, ra syscall.Sockaddr) error {
323
}
324
325
var err error
326
+ start := time.Now()
327
for {
328
// if err := fd.pd.WaitWrite(); err != nil {
329
// return err
@@ -321,7 +331,8 @@ func connect(fd int, ra syscall.Sockaddr) error {
331
// i'd use the above fd.pd.WaitWrite to poll io correctly, just like net sockets...
332
// but of course, it uses fucking runtime_* functions that _cannot_ be used by
333
// non-go-stdlib source... seriously guys, what kind of bullshit is that!?
324
- <-time.After(20 * time.Microsecond)
334
+ // we're relegated to using syscall.Select (what nightmare that is) or using
335
+ // a simple but totally bogus time-based wait. garbage.
336
var nerr int
337
nerr, err = syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_ERROR)
338
if err != nil {
@@ -329,6 +340,10 @@ func connect(fd int, ra syscall.Sockaddr) error {
340
}
341
switch err = syscall.Errno(nerr); err {
342
case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
343
+ if time.Now().Sub(start) > time.Second {
344
+ return err
345
+ }
346
+ <-time.After(20 * time.Microsecond)
347
case syscall.Errno(0), syscall.EISCONN:
348
return nil
349
default:
Godeps/_workspace/src/github.com/jbenet/go-reuseport/interface.go
+4
-4
@@ -71,16 +71,16 @@ type Dialer struct {
71
// Returns a net.Conn created from a file discriptor for a socket
72
// with SO_REUSEPORT and SO_REUSEADDR option set.
73
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
74
- // there's a rare case where dial returns successfully but for some reason the
75
- // RemoteAddr is not yet set. We wait here a while until it is, and if too long
76
- // passes, we fail.
74
c, err := dial(d.D, network, address)
75
if err != nil {
76
return nil, err
77
}
78
79
+ // there's a rare case where dial returns successfully but for some reason the
80
+ // RemoteAddr is not yet set. We wait here a while until it is, and if too long
81
+ // passes, we fail. This is horrendous.
82
for start := time.Now(); c.RemoteAddr() == nil; {
83
- if time.Now().Sub(start) > time.Second {
83
+ if time.Now().Sub(start) > (time.Millisecond * 500) {
84
c.Close()
85
return nil, ErrReuseFailed
86
}