This patch to libgo fixes the net package to work correctly on Solaris. The main fix is to the select code, to fix the case where one goroutine closes a file descriptor while another goroutine is waiting for I/O on the descriptor. Bootstrapped and ran Go testsuite on sparc-sun-solaris2.11. Committed to mainline and 4.7 branch.
Ian
diff -r 19c80c28161e libgo/Makefile.am --- a/libgo/Makefile.am Tue Apr 24 13:11:46 2012 -0700 +++ b/libgo/Makefile.am Tue Apr 24 20:10:20 2012 -0700 @@ -654,9 +654,9 @@ else if LIBGO_IS_SOLARIS go_net_cgo_file = go/net/cgo_linux.go -go_net_sock_file = go/net/sock_linux.go -go_net_sockopt_file = go/net/sockopt_linux.go -go_net_sockoptip_file = go/net/sockoptip_linux.go +go_net_sock_file = go/net/sock_solaris.go +go_net_sockopt_file = go/net/sockopt_bsd.go +go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_solaris.go else if LIBGO_IS_FREEBSD go_net_cgo_file = go/net/cgo_bsd.go diff -r 19c80c28161e libgo/Makefile.in --- a/libgo/Makefile.in Tue Apr 24 13:11:46 2012 -0700 +++ b/libgo/Makefile.in Tue Apr 24 20:10:20 2012 -0700 @@ -1019,17 +1019,17 @@ @LIBGO_IS_LINUX_TRUE@go_net_cgo_file = go/net/cgo_linux.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go @LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go -@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sock_file = go/net/sock_linux.go +@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sock_file = go/net/sock_solaris.go @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sock_file = go/net/sock_linux.go @LIBGO_IS_LINUX_TRUE@go_net_sock_file = go/net/sock_linux.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go @LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go -@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockopt_file = go/net/sockopt_linux.go +@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockopt_file = go/net/sockopt_bsd.go @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockopt_file = go/net/sockopt_linux.go @LIBGO_IS_LINUX_TRUE@go_net_sockopt_file = go/net/sockopt_linux.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_netbsd.go @LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_freebsd.go -@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockoptip_file = go/net/sockoptip_linux.go +@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_solaris.go @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockoptip_file = go/net/sockoptip_linux.go @LIBGO_IS_LINUX_TRUE@go_net_sockoptip_file = go/net/sockoptip_linux.go @LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_stub.go diff -r 19c80c28161e libgo/go/net/dial_test.go --- a/libgo/go/net/dial_test.go Tue Apr 24 13:11:46 2012 -0700 +++ b/libgo/go/net/dial_test.go Tue Apr 24 20:10:20 2012 -0700 @@ -130,7 +130,7 @@ n = 1000 } switch runtime.GOOS { - case "darwin", "freebsd", "openbsd", "windows": + case "darwin", "freebsd", "openbsd", "solaris", "windows": // Non-Linux systems take a long time to figure // out that there is nothing listening on localhost. n = 100 diff -r 19c80c28161e libgo/go/net/fd_select.go --- a/libgo/go/net/fd_select.go Tue Apr 24 13:11:46 2012 -0700 +++ b/libgo/go/net/fd_select.go Tue Apr 24 20:10:20 2012 -0700 @@ -102,7 +102,27 @@ break } } - if e != nil { + if e == syscall.EBADF { + // Some file descriptor has been closed. + tmpReadFds = syscall.FdSet{} + tmpWriteFds = syscall.FdSet{} + n = 0 + for i := 0; i < p.maxFd+1; i++ { + if syscall.FDIsSet(i, p.readFds) { + var s syscall.Stat_t + if syscall.Fstat(i, &s) == syscall.EBADF { + syscall.FDSet(i, &tmpReadFds) + n++ + } + } else if syscall.FDIsSet(i, p.writeFds) { + var s syscall.Stat_t + if syscall.Fstat(i, &s) == syscall.EBADF { + syscall.FDSet(i, &tmpWriteFds) + n++ + } + } + } + } else if e != nil { return -1, 0, os.NewSyscallError("select", e) } if n == 0 { diff -r 19c80c28161e libgo/go/net/multicast_test.go --- a/libgo/go/net/multicast_test.go Tue Apr 24 13:11:46 2012 -0700 +++ b/libgo/go/net/multicast_test.go Tue Apr 24 20:10:20 2012 -0700 @@ -46,7 +46,7 @@ // listener with same address family, same group address and same port. func TestMulticastListener(t *testing.T) { switch runtime.GOOS { - case "netbsd", "openbsd", "plan9", "windows": + case "netbsd", "openbsd", "plan9", "solaris", "windows": t.Logf("skipping test on %q", runtime.GOOS) return case "linux": diff -r 19c80c28161e libgo/go/net/sock_solaris.go --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgo/go/net/sock_solaris.go Tue Apr 24 20:10:20 2012 -0700 @@ -0,0 +1,47 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build solaris + +// Sockets for Solaris + +package net + +import ( + "syscall" +) + +func maxListenerBacklog() int { + // The kernel does not track the limit. + return syscall.SOMAXCONN +} + +func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) { + a := toAddr(la) + if a == nil { + return la, nil + } + switch v := a.(type) { + case *TCPAddr, *UnixAddr: + err := setDefaultListenerSockopts(s) + if err != nil { + return nil, err + } + case *UDPAddr: + if v.IP.IsMulticast() { + err := setDefaultMulticastSockopts(s) + if err != nil { + return nil, err + } + switch f { + case syscall.AF_INET: + v.IP = IPv4zero + case syscall.AF_INET6: + v.IP = IPv6unspecified + } + return v.sockaddr(f) + } + } + return la, nil +} diff -r 19c80c28161e libgo/go/net/sockoptip_solaris.go --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgo/go/net/sockoptip_solaris.go Tue Apr 24 20:10:20 2012 -0700 @@ -0,0 +1,90 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// IP-level socket options for Solaris + +package net + +import ( + "os" + "syscall" +) + +func ipv4MulticastInterface(fd *netFD) (*Interface, error) { + if err := fd.incref(false); err != nil { + return nil, err + } + defer fd.decref() + a, err := syscall.GetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF) + if err != nil { + return nil, os.NewSyscallError("getsockopt", err) + } + return ipv4AddrToInterface(IPv4(a[0], a[1], a[2], a[3])) +} + +func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { + ip, err := interfaceToIPv4Addr(ifi) + if err != nil { + return os.NewSyscallError("setsockopt", err) + } + var x [4]byte + copy(x[:], ip.To4()) + if err := fd.incref(false); err != nil { + return err + } + defer fd.decref() + err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x) + if err != nil { + return os.NewSyscallError("setsockopt", err) + } + return nil +} + +func ipv4MulticastLoopback(fd *netFD) (bool, error) { + if err := fd.incref(false); err != nil { + return false, err + } + defer fd.decref() + v, err := syscall.GetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP) + if err != nil { + return false, os.NewSyscallError("getsockopt", err) + } + return v == 1, nil +} + +func setIPv4MulticastLoopback(fd *netFD, v bool) error { + if err := fd.incref(false); err != nil { + return err + } + defer fd.decref() + err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v))) + if err != nil { + return os.NewSyscallError("setsockopt", err) + } + return nil +} + +func ipv4ReceiveInterface(fd *netFD) (bool, error) { + if err := fd.incref(false); err != nil { + return false, err + } + defer fd.decref() + v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF) + if err != nil { + return false, os.NewSyscallError("getsockopt", err) + } + return v == 1, nil +} + +func setIPv4ReceiveInterface(fd *netFD, v bool) error { + if err := fd.incref(false); err != nil { + return err + } + defer fd.decref() + err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v)) + if err != nil { + return os.NewSyscallError("setsockopt", err) + } + return nil +}