From a071a949d3b745d79d9be32029b829c1d1356fef Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Sun, 21 Apr 2024 18:18:34 +0800 Subject: [PATCH 1/3] opt: don't disable SO_REUSEPORT on DragonFlyBSD Fixes #582 --- gnet.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gnet.go b/gnet.go index c1b809649..80fe4c916 100644 --- a/gnet.go +++ b/gnet.go @@ -476,8 +476,11 @@ func createListeners(addrs []string, opts ...Option) ([]*listener, *Options, err // // Note that FreeBSD 12 introduced a new socket option named SO_REUSEPORT_LB // 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. goos := runtime.GOOS - if (options.Multicore || options.NumEventLoop > 1) && options.ReusePort && goos != "linux" && goos != "freebsd" { + if (options.Multicore || options.NumEventLoop > 1) && options.ReusePort && + goos != "linux" && goos != "dragonfly" && goos != "freebsd" { options.ReusePort = false } From c9f559cc65469b164440f9c0ac34f3e65038b04a Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Sun, 21 Apr 2024 18:23:34 +0800 Subject: [PATCH 2/3] Update comment --- gnet.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gnet.go b/gnet.go index 80fe4c916..cd5441d20 100644 --- a/gnet.go +++ b/gnet.go @@ -469,9 +469,9 @@ func createListeners(addrs []string, opts ...Option) ([]*listener, *Options, err // Linux implemented SO_REUSEPORT with load balancing for incoming connections // while *BSD implemented it for only binding to the same address and port, which // makes it pointless to enable SO_REUSEPORT on *BSD and Darwin for gnet with - // multiple event-loops because only the first event-loop will be constantly woken - // up to accept incoming connections and handle I/O events while the rest of event - // loops remain idle. + // multiple event-loops because only the first or last event-loop will be constantly + // woken up to accept incoming connections and handle I/O events while the rest of + // event-loops remain idle. // Thus, we disable SO_REUSEPORT on *BSD and Darwin by default. // // Note that FreeBSD 12 introduced a new socket option named SO_REUSEPORT_LB From d40e7536906234888ed0df3212483762de591caa Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Sun, 21 Apr 2024 19:13:58 +0800 Subject: [PATCH 3/3] opt: disable SO_REUSEPORT for Unix domain sockets 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 }