Appropriate Go type for cgo void* param that accepts either an offset or pointer?

236 views
Skip to first unread message

Eric Woroshow

unread,
Feb 19, 2018, 2:35:43 PM2/19/18
to golang-nuts
There exist some OpenGL functions (e.g., glDrawElements) where a single void* param accepts either a byte offset or a pointer value. What is the appropriate Go param type to wrap such C params? We can't use unsafe.Pointer because we can't store non-pointer values (offsets) in them. Using uintptr might work, but my concern is that passing a Go pointer into C by way of a uintptr value might confuse the GC (specifically, that the memory would fail to be pinned if/when Go implements a moving GC).

To make this discussion more concrete, using the example of glDrawElements above, I want to figure out an appropriate type for T:
  func GoGlDrawElements(uint32 mode, int32 count, uint32 type, T indices) {
    C.glDrawElementsWrapper(mode, count, type, indices)
  }

Thanks,
Eric

Jan Mercl

unread,
Feb 19, 2018, 3:03:00 PM2/19/18
to Eric Woroshow, golang-nuts
On Mon, Feb 19, 2018 at 8:35 PM Eric Woroshow <eric.w...@gmail.com> wrote:

> Using uintptr might work, but my concern is that passing a Go pointer into C by way of a uintptr value might confuse the GC (specifically, that the memory would fail to be pinned if/when Go implements a moving GC).

Uintptr is an integer type like any other. The GC does not care about integer values. However, the concern about Go objects being moved is valid as the Go compiler already does that in certain cases (stack allocs, but it's not specified when they occur) and, IIRC, pins only unsafe.Pointers passed to the CGO call. In such scenarios it might be possible to use a custom memory allocator to guarantee the stability of the passed-via-uintptr address. Or one can simply use C.malloc, provided the CGO call overhead is not prohibitive. In both cases, there's another possible problem: the out-of-Go-runtime-control allocated Go object cannot contain pointers to any Go-runtime-controlled Go objects as those pointers will not be updated when the pointees are eventually moved (credit Ian Taylor for this valuable notice).

--

-j

Eric Woroshow

unread,
Feb 19, 2018, 6:09:00 PM2/19/18
to golang-nuts
Right, declaring the type as uintptr but requiring that the memory come from C.malloc is workable, but definitely compromises ease-of-use. Typically the pointer values are to data stored in slices, and per issue 13656 there does not (yet) exist a good way to treat C memory as a slice.

Ian Lance Taylor

unread,
Feb 19, 2018, 9:00:13 PM2/19/18
to Eric Woroshow, golang-nuts
On Mon, Feb 19, 2018 at 3:09 PM, Eric Woroshow <eric.w...@gmail.com> wrote:
>
> Right, declaring the type as uintptr but requiring that the memory come from
> C.malloc is workable, but definitely compromises ease-of-use. Typically the
> pointer values are to data stored in slices, and per issue 13656 there does
> not (yet) exist a good way to treat C memory as a slice.

There just isn't any good solution here. In C it's fine for a
variable to be either a pointer or an integer. In Go it must be one
or the other.

One approach you can sometimes take is to write a little C helper
function, perhaps in the cgo comment itself, that uses the correct
type from Go's point of view and calls the real C function using a
cast to the type that C expects.

Ian



> On Monday, February 19, 2018 at 12:03:00 PM UTC-8, Jan Mercl wrote:
>>
>> On Mon, Feb 19, 2018 at 8:35 PM Eric Woroshow <eric.w...@gmail.com> wrote:
>>
>> > Using uintptr might work, but my concern is that passing a Go pointer
>> > into C by way of a uintptr value might confuse the GC (specifically, that
>> > the memory would fail to be pinned if/when Go implements a moving GC).
>>
>> Uintptr is an integer type like any other. The GC does not care about
>> integer values. However, the concern about Go objects being moved is valid
>> as the Go compiler already does that in certain cases (stack allocs, but
>> it's not specified when they occur) and, IIRC, pins only unsafe.Pointers
>> passed to the CGO call. In such scenarios it might be possible to use a
>> custom memory allocator to guarantee the stability of the passed-via-uintptr
>> address. Or one can simply use C.malloc, provided the CGO call overhead is
>> not prohibitive. In both cases, there's another possible problem: the
>> out-of-Go-runtime-control allocated Go object cannot contain pointers to any
>> Go-runtime-controlled Go objects as those pointers will not be updated when
>> the pointees are eventually moved (credit Ian Taylor for this valuable
>> notice).
>>
>> --
>>
>> -j
>
> --
> 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.
> For more options, visit https://groups.google.com/d/optout.

Eric Woroshow

unread,
Feb 19, 2018, 9:25:41 PM2/19/18
to Ian Lance Taylor, golang-nuts
That makes sense. The upstream API implication, then, would be both GoGlDrawElements(..., unsafe.Pointer) & GoGlDrawElementsWithOffset(..., unitptr) are necessary; that is, there can be no 1:1 Go mapping of the C API. There is an argument to be made that the more explicit Go API is better anyhow.
Reply all
Reply to author
Forward
0 new messages