cgo: passing unsafe.Pointer of Slice without explicit pinning?

167 views
Skip to first unread message

Antonio Caceres Cabrera

unread,
Jul 7, 2024, 9:17:28 AM (9 days ago) Jul 7
to golang-nuts
Hi Gophers,

I'm trying to use cgo for a C library, specifically a function which takes a void pointer to a buffer, and I'm trying to pass a Go-Slice as this buffer.

```
func Write(buf []byte) (int, error) {
    // var pin rumtime.Pinner
    // pin.Pin(&buf[0])
    // defer pin.Unpin()
    // is this necessary? Is it even safe?
    ...
    ret := C.lib_read(unsafe.Pointer(&buf[0]), C.int(len(buf))) // function which reads from this buffer
    ...
}
```
Does this require explicit pinning of buf? And if so, is the commented part a valid pinning of a slice? The documentation states that pointers passed to C functions are implicitly pinned (on the top-level that is), but does this also apply to slices, i.e. if I pass a (unsafe) pointer to the first element, is the slice's entire backing array implicitly pinned?

I know that there are some runtime checks for this, but I am worried that any use of unsafe.Pointer might perhaps override these checks completely.

Thanks a lot.

Julio

Ian Lance Taylor

unread,
Jul 7, 2024, 11:38:13 AM (9 days ago) Jul 7
to Antonio Caceres Cabrera, golang-nuts
This kind of code does not require explicit pinning. As you note,
passing a pointer to C implicitly pins the memory that the pointer
points to. Further, the cgo docs say "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." In other words, yes,
the slice's entire backing array is implicitly pinned. The cgo tool
is smart enough to ignore type conversions when deciding what memory
needs to be pinned.

Ian

Antonio Caceres Cabrera

unread,
Jul 8, 2024, 3:38:16 PM (8 days ago) Jul 8
to golang-nuts
Sorry, accidentally hit the wrong response button, so I'm posting it again:

Thanks for the clarification, Ian.

Is it also possible to pin memory to local go-arrays?
The docs state
>Go values created by calling new, by taking the address of a composite literal, or by taking the address of a local variable may also have their memory pinned using runtime.Pinner.

In this example:
```
var pin runtime.Pinner
var buf [32]byte
pin.Pin(&buf[0])
C.keep_this_pointer(&buf[0])
```

Is this ok since it counts as taking the address of a local variable or would the array have to be created with `new([32]byte)` ?

Julio

Ian Lance Taylor

unread,
Jul 8, 2024, 5:56:30 PM (8 days ago) Jul 8
to Antonio Caceres Cabrera, golang-nuts
On Mon, Jul 8, 2024 at 12:38 PM Antonio Caceres Cabrera
<julioca...@gmail.com> wrote:
>
> Sorry, accidentally hit the wrong response button, so I'm posting it again:
>
> Thanks for the clarification, Ian.
>
> Is it also possible to pin memory to local go-arrays?
> The docs state
> >Go values created by calling new, by taking the address of a composite literal, or by taking the address of a local variable may also have their memory pinned using runtime.Pinner.
>
> In this example:
> ```
> var pin runtime.Pinner
> var buf [32]byte
> pin.Pin(&buf[0])
> C.keep_this_pointer(&buf[0])
> ```
>
> Is this ok since it counts as taking the address of a local variable or would the array have to be created with `new([32]byte)` ?

Yes, this is an example of taking the address of a local variable.
The runtime.Pinner is not required here.

In general a runtime.Pinner is only required when passing the address
of a value that itself contains Go pointers. In that case the
internal Go pointers need to be explicitly pinned.

Ian

Antonio Caceres Cabrera

unread,
Jul 8, 2024, 7:05:47 PM (8 days ago) Jul 8
to golang-nuts
Unless, the C function stores the pointer and it's kept after the call returns, correct? That's what I mean by calling the function keep_this_pointer in the second example. In this case, it would be necessary, I'm assuming?

Julio

Ian Lance Taylor

unread,
Jul 8, 2024, 10:18:13 PM (8 days ago) Jul 8
to Antonio Caceres Cabrera, golang-nuts
On Mon, Jul 8, 2024 at 4:06 PM Antonio Caceres Cabrera
<julioca...@gmail.com> wrote:
>
> Unless, the C function stores the pointer and it's kept after the call returns, correct? That's what I mean by calling the function keep_this_pointer in the second example. In this case, it would be necessary, I'm assuming?

Ah, yes, in that case the pointer does have to be pinned.

Ian


> On Monday, July 8, 2024 at 11:56:30 PM UTC+2 Ian Lance Taylor wrote:
>>
>> On Mon, Jul 8, 2024 at 12:38 PM Antonio Caceres Cabrera
>> <julioca...@gmail.com> wrote:
>> >
>> > Sorry, accidentally hit the wrong response button, so I'm posting it again:
>> >
>> > Thanks for the clarification, Ian.
>> >
>> > Is it also possible to pin memory to local go-arrays?
>> > The docs state
>> > >Go values created by calling new, by taking the address of a composite literal, or by taking the address of a local variable may also have their memory pinned using runtime.Pinner.
>> >
>> > In this example:
>> > ```
>> > var pin runtime.Pinner
>> > var buf [32]byte
>> > pin.Pin(&buf[0])
>> > C.keep_this_pointer(&buf[0])
>> > ```
>> >
>> > Is this ok since it counts as taking the address of a local variable or would the array have to be created with `new([32]byte)` ?
>>
>> Yes, this is an example of taking the address of a local variable.
>> The runtime.Pinner is not required here.
>>
>> In general a runtime.Pinner is only required when passing the address
>> of a value that itself contains Go pointers. In that case the
>> internal Go pointers need to be explicitly pinned.
>>
>> Ian
>
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/b3b3a36b-87ba-4271-b004-810670eb759an%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages