back to select

This commit is contained in:
Juan Batiz-Benet
2015-01-22 15:26:51 -08:00
parent 82f6151ac4
commit 6924153ade
3 changed files with 58 additions and 21 deletions

View File

@@ -3,6 +3,7 @@
package reuseport
import (
"fmt"
"net"
"os"
"strconv"
@@ -129,6 +130,11 @@ func dial(dialer net.Dialer, netw, addr string) (c net.Conn, err error) {
}
}
if err = syscall.SetNonblock(fd, true); err != nil {
syscall.Close(fd)
return nil, err
}
if err = connect(fd, remoteSockaddr, deadline); err != nil {
syscall.Close(fd)
continue // try again.
@@ -140,11 +146,6 @@ func dial(dialer net.Dialer, netw, addr string) (c net.Conn, err error) {
return nil, err
}
if err = syscall.SetNonblock(fd, true); err != nil {
syscall.Close(fd)
return nil, err
}
if rprotocol == syscall.IPPROTO_TCP {
// by default golang/net sets TCP no delay to true.
if err = setNoDelay(fd, true); err != nil {
@@ -311,31 +312,58 @@ func listenUDP(netw, addr string) (c net.Conn, err error) {
// this is close to the connect() function inside stdlib/net
func connect(fd int, ra syscall.Sockaddr, deadline time.Time) error {
done := make(chan struct{}, 1)
if !deadline.IsZero() {
go func() {
timeout := deadline.Sub(time.Now())
select {
case <-done:
case <-time.After(timeout):
syscall.Close(fd) // unblock the connect.
}
}()
}
switch err := syscall.Connect(fd, ra); err {
case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
case nil, syscall.EISCONN:
done <- struct{}{}
if !deadline.IsZero() && deadline.Before(time.Now()) {
return errTimeout
}
return nil
default:
done <- struct{}{}
return err
}
var err error
var to *syscall.Timeval
var pw syscall.FdSet
FD_SET(uintptr(fd), &pw)
for {
// wait until the fd is ready to read or write.
if !deadline.IsZero() {
to2 := syscall.NsecToTimeval(deadline.Sub(time.Now()).Nanoseconds())
to = &to2
}
if _, err = Select(fd+1, nil, &pw, nil, to); err != nil {
fmt.Println(err)
return err
}
// if err := fd.pd.WaitWrite(); err != nil {
// return err
// }
// i'd use the above fd.pd.WaitWrite to poll io correctly, just like net sockets...
// but of course, it uses the damn runtime_* functions that _cannot_ be used by
// non-go-stdlib source... seriously guys, this is not nice.
// we're relegated to using syscall.Select (what nightmare that is) or using
// a simple but totally bogus time-based wait. such garbage.
var nerr int
nerr, err = syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_ERROR)
if err != nil {
return err
}
switch err = syscall.Errno(nerr); err {
case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
continue
case syscall.Errno(0), syscall.EISCONN:
if !deadline.IsZero() && deadline.Before(time.Now()) {
return errTimeout
}
return nil
default:
return err
}
}
}
var errTimeout = &timeoutError{}