Files
libp2p/available_unix.go
Steven Allen 1fdf8e59b2 check for reuseport support by opening a socket, not listening.
This ensures that, even if we can't bind to a random port on localhost for some
reason, we can still correctly detect if reuse port is supported.

Thanks to @Coderlane for suggesting this fix.

fixes #24
2018-01-28 23:19:36 -08:00

48 lines
1.1 KiB
Go

// +build darwin freebsd dragonfly netbsd openbsd linux
package reuseport
import (
"golang.org/x/sys/unix"
"sync"
"syscall"
"time"
sockaddrnet "github.com/libp2p/go-sockaddr/net"
)
var (
hasReusePort bool
didReusePort sync.Once
)
// Available returns whether or not SO_REUSEPORT is available in the OS.
// It does so by attepting to open a tcp socket, setting the option, and
// checking ENOPROTOOPT on error. After checking, the decision is cached
// for the rest of the process run.
func available() bool {
didReusePort.Do(checkReusePort)
return hasReusePort
}
func checkReusePort() {
// there may be fluke reasons to fail to open a socket.
// so we give it 5 shots. if not, give up and call it not avail.
for i := 0; i < 5; i++ {
// try to setup a TCP socket.
fd, err := socket(sockaddrnet.AF_INET, sockaddrnet.SOCK_STREAM, sockaddrnet.IPPROTO_TCP)
if err == nil {
unix.Close(fd)
hasReusePort = true
return
}
if errno, ok := err.(syscall.Errno); ok && errno == unix.ENOPROTOOPT {
return // :( that's all folks.
}
// not an errno? or not ENOPROTOOPT? retry.
time.Sleep(20 * time.Millisecond) // wait a bit
}
}