What are you really trying to save by doing this? Is your computation
so inexpensive, or the string so large, that attempting to do this would
compensate? Have you looked at CString to see what's really going
on there?
Note that to avoid the copy you'll actually have to dig further. The
version above will have to copy the data to convert it to []byte.
> b) The fabricated *char passed to C has AFAICS no live Go side
> reference and can be thus garbage collected at any moment - even while
> the C function is executing.
While in general I agree with the "don't do it", this is not strictly true.
cstr1 is a pointer to the underlying data, and will prevent it from getting
garbage collected while the function called from Go is executing. You're
right that it has no guarantees of staying around after that, though.
--
Gustavo Niemeyer
http://niemeyer.net
http://niemeyer.net/blog
http://niemeyer.net/twitter
While in general I agree with the "don't do it", this is not strictly true.
cstr1 is a pointer to the underlying data, and will prevent it from getting
garbage collected while the function called from Go is executing. You're
right that it has no guarantees of staying around after that, though.
Sure, this is true for any garbage collection. The issue is defining what
last possible accessibility means.
> s := "abc"
> t := s + "d"
> g(t) // s is not reachable, can be already collected before g is
> invoked.
Think about this case:
s := "abc"
t := s[1:2]
g(t) // Can s be collected here?
> In the OP case cstr1 might be out of scope after C.foo has been invoked but
> before it has returned. (...)
It can't, because there's necessarily a reference in the stack.
> IMO the compiler is free to collect cstr1 as soon as it detects it last
> possible accessibility.Sure, this is true for any garbage collection. The issue is defining what
last possible accessibility means.> s := "abc"
> t := s + "d"
> g(t) // s is not reachable, can be already collected before g is
> invoked.Think about this case:
s := "abc"
t := s[1:2]
g(t) // Can s be collected here?
> In the OP case cstr1 might be out of scope after C.foo has been invoked but
> before it has returned. (...)It can't, because there's necessarily a reference in the stack.
Yes, it's a different story. The story the OP is asking about. He's got
some data in the heap and has a pointer to it.
> Here I can't figure out the context/meaning of the above sentence. Which
> stack? Of the C function or of the Go function? Would the Go function be
cstr1 is a Go variable within a Go function.
> That's a completely different story. Here s *is* still reachable as itYes, it's a different story. The story the OP is asking about. He's got
some data in the heap and has a pointer to it.
Given a C function like this:
void foo(const char *s);
Is there a good reason to prefer:
cstr2 := C.CString(str)
defer C.free(unsafe.Pointer(cstr2))
C.foo(cstr2)
to:
cstr1 := (*C.char)(unsafe.Pointer(&([]byte)(str)[0]))
This is the context in the OP message:
cstr1 := (*C.char)(unsafe.Pointer(&([]byte)(str)[0]))
C.foo(cstr1)
This is what I said about it:
"""
cstr1 is a pointer to the underlying data, and will prevent it from getting
garbage collected while the function called from Go is executing. You're
right that it has no guarantees of staying around after that, though.
"""
It's simple. Don't argue about points which haven't been made.
> (...) The actual OP pattern could be completely different and thus safe
> in this regard.This is the context in the OP message:
cstr1 := (*C.char)(unsafe.Pointer(&([]byte)(str)[0]))
C.foo(cstr1)This is what I said about it:
"""
cstr1 is a pointer to the underlying data, and will prevent it from getting
garbage collected while the function called from Go is executing. You're
right that it has no guarantees of staying around after that, though.
"""It's simple. Don't argue about points which haven't been made.
They are.
> The second is still the same. If the next line after C.foo(cstr1) will
> be e.g. the closing right brace of a Go function then it still seems
> to me, that a conforming Go compiler is allowed to collect the
> pointee of cstr1 while C.foo() is executing.(1)
Lots of things can happen in a conforming Go implementation in
terms of memory management. It can move pointers around, it
can change the representation of native types, and it doesn't even
have to implement cgo, for a start. If you are poking at memory
with unsafe, you should be aware that you're not walking into a
safe land.
> As an afterthought:
> OK, let's say unsafe.Pointers are traced by GC (as you seems to rely on that
> in your reasoning about cstr1 above ). That would imply no C function can
> ever safely free any memory pointed to by some of it parameters. But that's
Yes, no C function can free() things which were not *alloc()ed. That's
a pretty well known constraint.
> a contradiction to the C.free(ptr_got_from_C_CString) idiom being useful at
> all.
Read the code of CString.
> BTW: IMO there's nobody arguing here. It's a pretty normal discussion.
Normal discussions are made of arguments. :-)
> I don't get it. First thing is that it's not clear to me if values of type
> unsafe.Pointer are considered by the GC or not. The second is still the
> same. If the next line after C.foo(cstr1) will be e.g. the closing right
> brace of a Go function then it still seems to me, that a conforming Go
> compiler is allowed to collect the pointee of cstr1 while C.foo() is
> executing.(1)
>
> As an afterthought:
>
> OK, let's say unsafe.Pointers are traced by GC (as you seems to rely on that
> in your reasoning about cstr1 above ). That would imply no C function can
> ever safely free any memory pointed to by some of it parameters. But that's
> a contradiction to the C.free(ptr_got_from_C_CString) idiom being useful at
> all.
>
> So we have to assume unsafe.Pointer typed values are not traced by the GC.
> Then we are back at square one and the above (1) situation seems in theory
> possible.
Values of type unsafe.Pointer are tracked by the GC. However, all that
means is that, if they happen to point to some space allocated by the Go
runtime, that space will not be freed. The GC doesn't do anything about
memory allocated by the C runtime. It just ignores pointers into that
memory.
In the code cstr1 is still there in the local variable in the goroutine
while C.foo is running, so the GC can't release it until C.foo is
complete.
Ian
> I don't get it. First thing is that it's not clear to me if values of type
> unsafe.Pointer are considered by the GC or not.They are.
> As an afterthought:
> OK, let's say unsafe.Pointers are traced by GC (as you seems to rely on that
> in your reasoning about cstr1 above ). That would imply no C function can
> ever safely free any memory pointed to by some of it parameters. But that'sYes, no C function can free() things which were not *alloc()ed. That's
a pretty well known constraint.> a contradiction to the C.free(ptr_got_from_C_CString) idiom being useful at
> all.Read the code of CString.
> BTW: IMO there's nobody arguing here. It's a pretty normal discussion.
Normal discussions are made of arguments. :-)
Values of type unsafe.Pointer are tracked by the GC. ...