Skip to content

Commit

Permalink
opt: disable SO_REUSEPORT on Unix domain sockets (panjf2000#584)
Browse files Browse the repository at this point in the history
  • Loading branch information
panjf2000 authored and andyl committed Apr 22, 2024
1 parent 0e982e4 commit cb4650c
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 12 deletions.
29 changes: 18 additions & 11 deletions gnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,16 @@ func createListeners(addrs []string, opts ...Option) ([]*listener, *Options, err
options.WriteBufferCap = math.CeilToPowerOfTwo(wbc)
}

var hasUDP, hasUnix bool
for _, addr := range addrs {
proto, _, err := parseProtoAddr(addr)
if err != nil {
return nil, nil, err
}
hasUDP = hasUDP || strings.HasPrefix(proto, "udp")
hasUnix = hasUnix || proto == "unix"
}

// SO_REUSEPORT enables duplicate address and port bindings across various
// Unix-like OSs, whereas there is platform-specific inconsistency:
// Linux implemented SO_REUSEPORT with load balancing for incoming connections
Expand All @@ -483,22 +493,19 @@ func createListeners(addrs []string, opts ...Option) ([]*listener, *Options, err
// with the capability of load balancing, it's the equivalent of Linux's SO_REUSEPORT.
// Also note that DragonFlyBSD 3.6.0 extended SO_REUSEPORT to distribute workload to
// available sockets, which make it the same as Linux's SO_REUSEPORT.
// AF_LOCAL with SO_REUSEPORT enables duplicate address and port bindings without
// load balancing on Linux and *BSD. Therefore, disable it for Unix domain sockets.
goos := runtime.GOOS
if (options.Multicore || options.NumEventLoop > 1) && options.ReusePort &&
goos != "linux" && goos != "dragonfly" && goos != "freebsd" {
((goos != "linux" && goos != "dragonfly" && goos != "freebsd") || hasUnix) {
options.ReusePort = false
}

// If there is UDP listener in the list, enable SO_REUSEPORT and disable edge-triggered I/O by default.
for i := 0; (!options.ReusePort || options.EdgeTriggeredIO) && i < len(addrs); i++ {
proto, _, err := parseProtoAddr(addrs[i])
if err != nil {
return nil, nil, err
}
if strings.HasPrefix(proto, "udp") {
options.ReusePort = true
options.EdgeTriggeredIO = false
}
// If there is UDP address in the list, we have no choice but to enable SO_REUSEPORT anyway,
// also disable edge-triggered I/O for UDP by default.
if hasUDP {
options.ReusePort = true
options.EdgeTriggeredIO = false
}

listeners := make([]*listener, len(addrs))
Expand Down
2 changes: 1 addition & 1 deletion options.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ type Options struct {

// EdgeTriggeredIO enables the edge-triggered I/O for the underlying epoll/kqueue event-loop.
// Don't enable it unless you are 100% sure what you are doing.
// Note that this option is only available for TCP protocol.
// Note that this option is only available for stream-oriented protocol.
EdgeTriggeredIO bool

// TLSConfig support TLS
Expand Down

0 comments on commit cb4650c

Please sign in to comment.