From fcc7933e6cad70c78cde17dd29e5735ff31949b8 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Sun, 21 Apr 2024 20:03:52 +0800 Subject: [PATCH] opt: disable SO_REUSEPORT on Unix domain sockets (#584) Fixes #581 --- gnet.go | 29 ++++++++++++++++++----------- options.go | 2 +- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/gnet.go b/gnet.go index cd5441d20..a5214eede 100644 --- a/gnet.go +++ b/gnet.go @@ -464,6 +464,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 @@ -478,22 +488,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)) diff --git a/options.go b/options.go index 7eb355ed3..86caef7fb 100644 --- a/options.go +++ b/options.go @@ -125,7 +125,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 }