diff --git a/gnet.go b/gnet.go index 643cd7264..c1b809649 100644 --- a/gnet.go +++ b/gnet.go @@ -18,6 +18,7 @@ import ( "context" "io" "net" + "runtime" "strings" "sync" "time" @@ -463,6 +464,23 @@ func createListeners(addrs []string, opts ...Option) ([]*listener, *Options, err options.WriteBufferCap = math.CeilToPowerOfTwo(wbc) } + // 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 + // 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. + // Thus, we disable SO_REUSEPORT on *BSD and Darwin by default. + // + // 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. + goos := runtime.GOOS + if (options.Multicore || options.NumEventLoop > 1) && options.ReusePort && goos != "linux" && goos != "freebsd" { + 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]) diff --git a/internal/socket/sockopts_freebsd.go b/internal/socket/sockopts_freebsd.go new file mode 100644 index 000000000..01026fd72 --- /dev/null +++ b/internal/socket/sockopts_freebsd.go @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 The Gnet Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package socket + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// SetReuseport enables SO_REUSEPORT_LB option on socket. +func SetReuseport(fd, reusePort int) error { + return os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEPORT_LB, reusePort)) +} diff --git a/internal/socket/sockopts_posix.go b/internal/socket/sockopts_posix.go index 321ac8a1b..1280285f2 100644 --- a/internal/socket/sockopts_posix.go +++ b/internal/socket/sockopts_posix.go @@ -48,11 +48,6 @@ func SetSendBuffer(fd, size int) error { return unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_SNDBUF, size) } -// SetReuseport enables SO_REUSEPORT option on socket. -func SetReuseport(fd, reusePort int) error { - return os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEPORT, reusePort)) -} - // SetReuseAddr enables SO_REUSEADDR option on socket. func SetReuseAddr(fd, reuseAddr int) error { return os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEADDR, reuseAddr)) diff --git a/internal/socket/sockopts_unix1.go b/internal/socket/sockopts_unix1.go new file mode 100644 index 000000000..0f7be1da9 --- /dev/null +++ b/internal/socket/sockopts_unix1.go @@ -0,0 +1,29 @@ +// Copyright (c) 2021 The Gnet Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build linux || dragonfly || netbsd || openbsd || darwin +// +build linux dragonfly netbsd openbsd darwin + +package socket + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// SetReuseport enables SO_REUSEPORT option on socket. +func SetReuseport(fd, reusePort int) error { + return os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEPORT, reusePort)) +}