mirror of
https://github.com/libp2p/go-reuseport.git
synced 2025-09-28 11:42:10 +08:00

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
48 lines
1.1 KiB
Go
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
|
|
}
|
|
}
|