From 26e7bc8143f19e249e8bf7e06bc6f0a0780f640e Mon Sep 17 00:00:00 2001 From: yohimik Date: Sun, 30 Aug 2026 15:36:08 +0400 Subject: [PATCH] net: extend the host netdev to darwin The host netdev was for linux only, so a macOS binary registered no netdev and every Dial reported "Netdev not set". Nothing in it is specific to linux. Darwin also keeps the standard library syscall package, where the TinyGo compiler routes the libc_*_trampoline symbols to libSystem, so the same raw-socket implementation works there. Widen the build constraint to (linux || darwin) and move the one part that is different, the socket options for every socket, into per-OS setSockDefaults functions. Linux keeps the same SO_REUSEADDR as before. Darwin adds SO_NOSIGPIPE, because a BSD socket raises SIGPIPE on a write to a closed connection and darwin has no MSG_NOSIGNAL to prevent it for one write. Accept now retries on EINTR, as Connect, Send and Recv already do. Under the threads scheduler a GC cycle interrupts a blocking call with a signal, and a server must not see that as a failed accept. An accepted socket gets the same defaults as a created one, which puts SO_NOSIGPIPE on the connections that a server writes to. --- netdev_native.go | 44 ++++++++++++++++++++++++++--------------- netdev_native_darwin.go | 16 +++++++++++++++ netdev_native_linux.go | 15 ++++++++++++++ 3 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 netdev_native_darwin.go create mode 100644 netdev_native_linux.go diff --git a/netdev_native.go b/netdev_native.go index e522a08..5f15f22 100644 --- a/netdev_native.go +++ b/netdev_native.go @@ -1,17 +1,21 @@ -//go:build linux && !baremetal && !nintendoswitch && !wasm_unknown && !tinygo.wasm +//go:build (linux || darwin) && !baremetal && !nintendoswitch && !wasm_unknown && !tinygo.wasm -// TINYGO: Native (host) netdev for the TinyGo "linux" target. +// TINYGO: Native (host) netdev for the TinyGo "linux" and "darwin" targets. // -// On the native linux target TinyGo does NOT override the "syscall" package, so -// the standard library's syscall.Socket/Connect/Bind/... are available and the -// TinyGo compiler lowers syscall.Syscall/RawSyscall into real inline-asm system -// calls (see compiler/syscall.go). That means we can implement the netdever -// interface directly on top of raw Linux sockets, without needing a network -// driver or musl's (omitted) src/network module. +// On those native targets TinyGo does NOT override the "syscall" package, so +// the standard library's syscall.Socket/Connect/Bind/... are available: on +// linux the TinyGo compiler lowers syscall.Syscall/RawSyscall into real +// inline-asm system calls, and on darwin it routes the libc_*_trampoline +// symbols to libSystem (see compiler/syscall.go). Either way we can implement +// the netdever interface directly on top of raw host sockets, without needing a +// network driver or musl's (omitted) src/network module. // // This file registers that implementation as the default netdev, so that -// net.Dial/Listen/Lookup just work on a regular Linux host. See +// net.Dial/Listen/Lookup just work on a regular Linux or macOS host. See // https://github.com/skycoin/skycoin/issues/2902. +// +// The per-OS socket defaults are in netdev_native_linux.go and +// netdev_native_darwin.go. Everything else here is common to the two. package net @@ -31,7 +35,7 @@ func init() { useNetdev(&hostNetdev{}) } -// hostNetdev implements netdever using raw Linux sockets via the syscall +// hostNetdev implements netdever using raw host sockets via the syscall // package. The "sockfd" values it returns are plain OS file descriptors. // // Deadlines are implemented with the per-socket SO_RCVTIMEO/SO_SNDTIMEO @@ -95,11 +99,7 @@ func (*hostNetdev) Socket(domain, stype, protocol int) (int, error) { return -1, err } - // Allow quick rebind of listening sockets (e.g. restarting a server), - // matching the standard library's behaviour. - if stype == syscall.SOCK_STREAM { - syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) - } + setSockDefaults(fd, stype) return fd, nil } @@ -133,10 +133,22 @@ func (*hostNetdev) Listen(sockfd int, backlog int) error { } func (*hostNetdev) Accept(sockfd int) (int, netip.AddrPort, error) { - nfd, sa, err := syscall.Accept(sockfd) + var nfd int + var sa syscall.Sockaddr + var err error + for { + // A signal from the runtime interrupts a blocking accept, so retry + // instead of a report of a failure that the caller cannot act on. + nfd, sa, err = syscall.Accept(sockfd) + if err == syscall.EINTR { + continue + } + break + } if err != nil { return -1, netip.AddrPort{}, err } + setSockDefaults(nfd, syscall.SOCK_STREAM) var raddr netip.AddrPort switch s := sa.(type) { case *syscall.SockaddrInet4: diff --git a/netdev_native_darwin.go b/netdev_native_darwin.go new file mode 100644 index 0000000..98b1da9 --- /dev/null +++ b/netdev_native_darwin.go @@ -0,0 +1,16 @@ +//go:build darwin && !baremetal && !nintendoswitch && !wasm_unknown && !tinygo.wasm + +package net + +import "syscall" + +// setSockDefaults applies the socket options that the host netdev wants on +// every socket. SO_REUSEADDR allows a quick rebind of a listening socket, as on +// linux. SO_NOSIGPIPE is necessary because a BSD socket raises SIGPIPE on a +// write to a closed connection and darwin has no MSG_NOSIGNAL. +func setSockDefaults(fd int, stype int) { + if stype == syscall.SOCK_STREAM { + syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) + } + syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_NOSIGPIPE, 1) +} diff --git a/netdev_native_linux.go b/netdev_native_linux.go new file mode 100644 index 0000000..514e5b7 --- /dev/null +++ b/netdev_native_linux.go @@ -0,0 +1,15 @@ +//go:build linux && !baremetal && !nintendoswitch && !wasm_unknown && !tinygo.wasm + +package net + +import "syscall" + +// setSockDefaults applies the socket options that the host netdev wants on +// every socket. On linux that is only SO_REUSEADDR on a stream socket, which +// allows a quick rebind of a listening socket. This is the same set of options +// that the netdev applied before the darwin split. +func setSockDefaults(fd int, stype int) { + if stype == syscall.SOCK_STREAM { + syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) + } +}