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

runtime/cgo: Go pointer checking fails if function has receiver #30894

Closed
KatelynHaworth opened this issue Mar 18, 2019 · 5 comments
Closed

runtime/cgo: Go pointer checking fails if function has receiver #30894

KatelynHaworth opened this issue Mar 18, 2019 · 5 comments

Comments

@KatelynHaworth
Copy link
Contributor

KatelynHaworth commented Mar 18, 2019

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

$ go version
go version go1.12.1 darwin/amd64

Does this issue reproduce with the latest release?

Yes

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

$ go env
GOARCH="amd64"
GOBIN="/Volumes/Storage/git/git/go_path/bin"
GOCACHE="/Users/liam/Library/Caches/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Volumes/Storage/git/git/go_path"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/Cellar/go/1.12.1/libexec"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.12.1/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="[REDACTED]"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/xx/mdt3785n0js3yrk67_ftskdr0000gn/T/go-build031168974=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

Working on a library to wrap calls to CoreFoundation and SystemConfiguration functions on macOS, part of the library setups up a callback for receiving system change notifications.

When setting up the notification if the callback method is plain function then the program operates fine, but if the function has a receiver then CGO complains the pointer contains a Go pointer.

This doesn't really make any sense to me as the type finger print is the same on both functions and it fails even if the receiver isn't a pointer (value copy).

I've managed to replicate the behavior with the following code

package main

// void receiver(void *info) {
//	printf("%p\n", info);
// }
import "C"
import (
	"unsafe"
)

type FunctionType func()

type Info struct {
	callback FunctionType
}

func main() {
	ctx := &Info{
		callback: test,
	}
	C.receiver((unsafe.Pointer)(ctx))

	receiver := DummyReceiver{}
	ctx2 := &Info{
		callback: receiver.test,
	}
	C.receiver((unsafe.Pointer)(ctx2))
}

func test() {}

type DummyReceiver struct {}

func (DummyReceiver) test() {}

What did you expect to see?

I would expect the second call to pass as there is no difference in the variable type and the receiver of the function isn't a pointer.

If it is supposed to fail then why do normal functions not fail as well?

What did you see instead?

0xc00009c000
panic: runtime error: cgo argument has Go pointer to Go pointer

goroutine 1 [running]:
main.main.func2(0xc00009c008)
	/Users/liam/Library/Preferences/GoLand2018.3/scratches/scratch_15.go:32 +0x48
main.main()
	/Users/liam/Library/Preferences/GoLand2018.3/scratches/scratch_15.go:32 +0xa5
@KatelynHaworth
Copy link
Contributor Author

Seems like the error has probably originated from runtime/cgocall.go.

It's catching on the fact that the function is in the heap, which makes sense somewhat as to why that is not allowed (memory could change, etc.).

What would be the recommend workaround of this, if there is one?

Closing as this isn't a bug.

@ianlancetaylor
Copy link
Contributor

If all you want to do is print a pointer value, then the workaround is to call reflect.ValueOf(v).Pointer() and pass the resulting uintptr value to C. Here v is either test or receiver.test.

@KatelynHaworth
Copy link
Contributor Author

Thanks for jumping in Ian.

I wish the solution was that easy, but the problem I am trying to solve is passing along a pointer to a struct that contains information for a callback handler.

The struct it self contains the Golang callback to invoke when the CGO code call back is invoked, this works fine if the Golang callback has a signature of func test() {} but if it has a receiver (func (someStruct) test() {}) then the CGO pointer checker fails the call.

For example, in the library I have written it sets up a DynamicStore for macOS that is used to receive notifications from the OS when a configuration item or state change occurs, the callback invoked by macOS is a CGO function.

During the creation of the DynamicStore you can set a "context" (arbitrary pointer to some data) which is then included in the parameters of the callback when it is invoked, I've set the context to the pointer of a Golang struct.

The Golang struct contains a Golang function as a variable that is called by the DynamicStore CGO callback to inform the user of the library.

This is all done to provide a bit of a "cleaner" interface to the macOS libraries from Go but I am sure there is another way I can handle this, just feels like the current form is the most idiomatic.

@ianlancetaylor
Copy link
Contributor

In the general case you need to use a map[uintptr]T on the Go side, and pass the uintptr through the C code as a handle.

@KatelynHaworth
Copy link
Contributor Author

Yeah, got the feeling that might have to be the way, was just hoping there might of been a different solution I couldn't think of.

But any ways, thank you Ian.

@golang golang locked and limited conversation to collaborators Mar 24, 2020
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