Navigation Menu

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/cgo: How to call a C library api like writev via cgo without allocation and copying of the whole byte slice vector? #44991

Closed
remuscl opened this issue Mar 14, 2021 · 1 comment

Comments

@remuscl
Copy link

remuscl commented Mar 14, 2021

What version of Go are you using (go version)?

$ go version
go version go1.16 linux/amd64

Does this issue reproduce with the latest release?

All version.

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/x/.cache/go-build"
GOENV="/home/x/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/x/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/x/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/x/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/x/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.16.2"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"

Question

Say here is a C api like ssize_t writev(const struct iovec *iov, int iovcnt) which the definition of iovec is like below:

struct iovec {
     uint8_t   *iov_base;  /* Base address. */
     uint64_t iov_len;    /* Length. */
 };

For C api which like ssize_t write(const void *buf, size_t nbyte), the solution would be quite straight forward:

bs := make([]byte, 1024*1024*512)
// without extra memory allocation and copying of the whole input byte slice :-D
rc := C.write(unsafe.Pointer(&bs[0]), C.int(len(bs)))

But how to do writev without extra memory allocation or copying of the whole byte slice vector?

bsv := make([][]byte, 1024)
for i := range bsv{
	bsv[i] = make([]byte, 5*1024*(rand.Intn(i)+1))
}
// assuming that allocation of a iovec array is acceptable
// but allocation and copying of all bsv[x] byte slice member is unacceptable
//
iovec := make([]syscall.Iovec, len(bsv))
for i := range bsv {
		bs := bsv[i]
		if len(bs) > 0 {
        iovec[i].iov_base = unsafe.Pointer(&bs[0])
        iovec[i].iov_len = uint64(len(bs))
		}
}
//
// rc := C.writev( /* how? :-( */)
rc := C.writev(unsafe.Pointer(&iovec[0]), C.int(len(iovec))) // Does this code is right and safe?

Does the codes above is right?

I have read cgo's docs carefully, and here is a constraint from https://golang.org/cmd/cgo/#hdr-Passing_pointers:

Go code may pass a Go pointer to C provided the Go memory to which it points does not contain any Go pointers.

And https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md#proposal:

  1. Go code may pass a Go pointer to C provided that the Go memory to which it points does not contain any Go pointers.
  • The C code must not store any Go pointers in Go memory, even temporarily.
  • When passing a pointer to a field in a struct, the Go memory in question is the memory occupied by the field, not the entire struct.
  • When passing a pointer to an element in an array or slice, the Go memory in question is the entire array or the entire backing array of the slice.
  • Passing a Go pointer to C code means that that Go pointer is visible to C code; passing one Go pointer does not cause any other Go pointers to become visible.
  • The maximum number of Go pointers that can become visible to C code in a single function call is the number of arguments to the function.

If the Go memory pointed by unsafe.Pointer(&iovec[0]) contains pointer which points to these byte slice members of bsv, so it would be Violation of the cgo constraint above. And that means you could not call C writev style API without allocation and copying of the whole vector.

Please correct me if I get something wrong. Thanks a lot :-D

@mdlayher
Copy link
Member

@golang golang locked and limited conversation to collaborators Mar 14, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants