Skip to content

Commit

Permalink
internal/socket: add message IO functionality
Browse files Browse the repository at this point in the history
This change adds portable message IO methods of Conn, IO message and
control message types, and parse methods for the types to provide
those functionality to the ipv4 and ipv6 packages.

With this change, the ipv4 and ipv6 packages can provide low-level
(but less heap allocation and the cost of invoking system calls) API
such as read and write operations for a batch of IO messages.

On vanilla linux/amd64 virtual machine:
BenchmarkUDP/Iter-1-2            1000000              8068 ns/op             408 B/op         14 allocs/op
BenchmarkUDP/Batch-1-2           1000000              8610 ns/op             440 B/op         14 allocs/op
BenchmarkUDP/Iter-2-2             500000             15390 ns/op             816 B/op         28 allocs/op
BenchmarkUDP/Batch-2-2            500000             12715 ns/op             696 B/op         20 allocs/op
BenchmarkUDP/Iter-4-2             200000             30763 ns/op            1632 B/op         56 allocs/op
BenchmarkUDP/Batch-4-2            300000             21853 ns/op            1216 B/op         32 allocs/op
BenchmarkUDP/Iter-8-2             100000             61460 ns/op            3264 B/op        112 allocs/op
BenchmarkUDP/Batch-8-2            200000             39048 ns/op            2256 B/op         56 allocs/op
BenchmarkUDP/Iter-16-2             50000            122408 ns/op            6528 B/op        224 allocs/op
BenchmarkUDP/Batch-16-2           100000             72728 ns/op            4336 B/op        104 allocs/op
BenchmarkUDP/Iter-32-2             30000            243137 ns/op           13056 B/op        448 allocs/op
BenchmarkUDP/Batch-32-2            50000            141332 ns/op            8496 B/op        200 allocs/op
BenchmarkUDP/Iter-64-2             20000            488125 ns/op           26112 B/op        896 allocs/op
BenchmarkUDP/Batch-64-2            30000            282078 ns/op           16816 B/op        392 allocs/op
BenchmarkUDP/Iter-128-2            10000            973752 ns/op           52224 B/op       1792 allocs/op
BenchmarkUDP/Batch-128-2           10000            551021 ns/op           33456 B/op        776 allocs/op
BenchmarkUDP/Iter-256-2             3000           1977852 ns/op          104448 B/op       3584 allocs/op
BenchmarkUDP/Batch-256-2           10000           1252596 ns/op           66736 B/op       1544 allocs/op
BenchmarkUDP/Iter-512-2             2000           4147495 ns/op          208896 B/op       7168 allocs/op
BenchmarkUDP/Batch-512-2            3000           2175774 ns/op          121128 B/op       2612 allocs/op

Change-Id: I3e08b28917b62dbea7936a86acb24e25ccaf5365
Reviewed-on: https://go-review.googlesource.com/38212
Run-TryBot: Mikio Hara <mikioh.mikioh@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
  • Loading branch information
cixtor committed May 26, 2017
1 parent f61a773 commit b8b1343
Show file tree
Hide file tree
Showing 43 changed files with 1,602 additions and 9 deletions.
11 changes: 11 additions & 0 deletions internal/socket/cmsghdr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2017 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 darwin dragonfly freebsd linux netbsd openbsd solaris

package socket

func (h *cmsghdr) len() int { return int(h.Len) }
func (h *cmsghdr) lvl() int { return int(h.Level) }
func (h *cmsghdr) typ() int { return int(h.Type) }
13 changes: 13 additions & 0 deletions internal/socket/cmsghdr_bsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2017 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 darwin dragonfly freebsd netbsd openbsd

package socket

func (h *cmsghdr) set(l, lvl, typ int) {
h.Len = uint32(l)
h.Level = int32(lvl)
h.Type = int32(typ)
}
14 changes: 14 additions & 0 deletions internal/socket/cmsghdr_linux_32bit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2017 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 arm mips mipsle 386
// +build linux

package socket

func (h *cmsghdr) set(l, lvl, typ int) {
h.Len = uint32(l)
h.Level = int32(lvl)
h.Type = int32(typ)
}
14 changes: 14 additions & 0 deletions internal/socket/cmsghdr_linux_64bit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2017 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 arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
// +build linux

package socket

func (h *cmsghdr) set(l, lvl, typ int) {
h.Len = uint64(l)
h.Level = int32(lvl)
h.Type = int32(typ)
}
14 changes: 14 additions & 0 deletions internal/socket/cmsghdr_solaris_64bit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2017 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 amd64
// +build solaris

package socket

func (h *cmsghdr) set(l, lvl, typ int) {
h.Len = uint32(l)
h.Level = int32(lvl)
h.Type = int32(typ)
}
17 changes: 17 additions & 0 deletions internal/socket/cmsghdr_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2017 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 !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris

package socket

type cmsghdr struct{}

const sizeofCmsghdr = 0

func (h *cmsghdr) len() int { return 0 }
func (h *cmsghdr) lvl() int { return 0 }
func (h *cmsghdr) typ() int { return 0 }

func (h *cmsghdr) set(l, lvl, typ int) {}
15 changes: 15 additions & 0 deletions internal/socket/iovec_32bit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2017 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 arm mips mipsle 386
// +build darwin dragonfly freebsd linux netbsd openbsd

package socket

import "unsafe"

func (v *iovec) set(b []byte) {
v.Base = (*byte)(unsafe.Pointer(&b[0]))
v.Len = uint32(len(b))
}
15 changes: 15 additions & 0 deletions internal/socket/iovec_64bit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2017 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 arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
// +build darwin dragonfly freebsd linux netbsd openbsd

package socket

import "unsafe"

func (v *iovec) set(b []byte) {
v.Base = (*byte)(unsafe.Pointer(&b[0]))
v.Len = uint64(len(b))
}
15 changes: 15 additions & 0 deletions internal/socket/iovec_solaris_64bit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2017 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 amd64
// +build solaris

package socket

import "unsafe"

func (v *iovec) set(b []byte) {
v.Base = (*int8)(unsafe.Pointer(&b[0]))
v.Len = uint64(len(b))
}
11 changes: 11 additions & 0 deletions internal/socket/iovec_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2017 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 !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris

package socket

type iovec struct{}

func (v *iovec) set(b []byte) {}
21 changes: 21 additions & 0 deletions internal/socket/mmsghdr_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2017 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 !linux,!netbsd

package socket

import "net"

type mmsghdr struct{}

type mmsghdrs []mmsghdr

func (hs mmsghdrs) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr) []byte) error {
return nil
}

func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error {
return nil
}
42 changes: 42 additions & 0 deletions internal/socket/mmsghdr_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2017 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 linux netbsd

package socket

import "net"

type mmsghdrs []mmsghdr

func (hs mmsghdrs) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr) []byte) error {
for i := range hs {
vs := make([]iovec, len(ms[i].Buffers))
var sa []byte
if parseFn != nil {
sa = make([]byte, sizeofSockaddrInet6)
}
if marshalFn != nil {
sa = marshalFn(ms[i].Addr)
}
hs[i].Hdr.pack(vs, ms[i].Buffers, ms[i].OOB, sa)
}
return nil
}

func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error {
for i := range hs {
ms[i].N = int(hs[i].Len)
ms[i].NN = hs[i].Hdr.controllen()
ms[i].Flags = hs[i].Hdr.flags()
if parseFn != nil {
var err error
ms[i].Addr, err = parseFn(hs[i].Hdr.name(), hint)
if err != nil {
return err
}
}
}
return nil
}
39 changes: 39 additions & 0 deletions internal/socket/msghdr_bsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2017 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 darwin dragonfly freebsd netbsd openbsd

package socket

import "unsafe"

func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
for i := range vs {
vs[i].set(bs[i])
}
h.setIov(vs)
if len(oob) > 0 {
h.Control = (*byte)(unsafe.Pointer(&oob[0]))
h.Controllen = uint32(len(oob))
}
if sa != nil {
h.Name = (*byte)(unsafe.Pointer(&sa[0]))
h.Namelen = uint32(len(sa))
}
}

func (h *msghdr) name() []byte {
if h.Name != nil && h.Namelen > 0 {
return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen]
}
return nil
}

func (h *msghdr) controllen() int {
return int(h.Controllen)
}

func (h *msghdr) flags() int {
return int(h.Flags)
}
12 changes: 12 additions & 0 deletions internal/socket/msghdr_bsdvar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright 2017 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 darwin dragonfly freebsd netbsd

package socket

func (h *msghdr) setIov(vs []iovec) {
h.Iov = &vs[0]
h.Iovlen = int32(len(vs))
}
36 changes: 36 additions & 0 deletions internal/socket/msghdr_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2017 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.

package socket

import "unsafe"

func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
for i := range vs {
vs[i].set(bs[i])
}
h.setIov(vs)
if len(oob) > 0 {
h.setControl(oob)
}
if sa != nil {
h.Name = (*byte)(unsafe.Pointer(&sa[0]))
h.Namelen = uint32(len(sa))
}
}

func (h *msghdr) name() []byte {
if h.Name != nil && h.Namelen > 0 {
return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen]
}
return nil
}

func (h *msghdr) controllen() int {
return int(h.Controllen)
}

func (h *msghdr) flags() int {
return int(h.Flags)
}
20 changes: 20 additions & 0 deletions internal/socket/msghdr_linux_32bit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2017 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 arm mips mipsle 386
// +build linux

package socket

import "unsafe"

func (h *msghdr) setIov(vs []iovec) {
h.Iov = &vs[0]
h.Iovlen = uint32(len(vs))
}

func (h *msghdr) setControl(b []byte) {
h.Control = (*byte)(unsafe.Pointer(&b[0]))
h.Controllen = uint32(len(b))
}
20 changes: 20 additions & 0 deletions internal/socket/msghdr_linux_64bit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2017 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 arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
// +build linux

package socket

import "unsafe"

func (h *msghdr) setIov(vs []iovec) {
h.Iov = &vs[0]
h.Iovlen = uint64(len(vs))
}

func (h *msghdr) setControl(b []byte) {
h.Control = (*byte)(unsafe.Pointer(&b[0]))
h.Controllen = uint64(len(b))
}
10 changes: 10 additions & 0 deletions internal/socket/msghdr_openbsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright 2017 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.

package socket

func (h *msghdr) setIov(vs []iovec) {
h.Iov = &vs[0]
h.Iovlen = uint32(len(vs))
}
34 changes: 34 additions & 0 deletions internal/socket/msghdr_solaris_64bit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2017 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 amd64
// +build solaris

package socket

import "unsafe"

func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
for i := range vs {
vs[i].set(bs[i])
}
h.Iov = &vs[0]
h.Iovlen = int32(len(vs))
if len(oob) > 0 {
h.Accrights = (*int8)(unsafe.Pointer(&oob[0]))
h.Accrightslen = int32(len(oob))
}
if sa != nil {
h.Name = (*byte)(unsafe.Pointer(&sa[0]))
h.Namelen = uint32(len(sa))
}
}

func (h *msghdr) controllen() int {
return int(h.Accrightslen)
}

func (h *msghdr) flags() int {
return int(NativeEndian.Uint32(h.Pad_cgo_2[:]))
}
Loading

0 comments on commit b8b1343

Please sign in to comment.