How to use atomic_int in cgo?

255 views
Skip to first unread message

changkun

unread,
Feb 15, 2021, 4:32:08 AM2/15/21
to golang-nuts
Hi golang-nuts,

I would like to call a C function from Go and get notified about the execution status in Go, say a goroutine wait until the C function finally made some progress.
Initially, I thought about passing a channel to C but the cgo document does not say anything about that:

Later, I am thinking about using an atomic variable to sync status according to its value, but somehow I end up with this warning while I am compiling a cgo program:

package main

/*
#include <stdatomic.h>
void ainit(atomic_int *val) {
    atomic_init(val, 0);
}
*/
import "C"

func main() {
    var v C.atomic_int
    C.ainit(&v)
}

cgo-gcc-prolog: In function ‘_cgo_8a67e594de48_Cfunc_ainit’:
cgo-gcc-prolog:49:14: warning: passing argument 1 of ‘ainit’ from incompatible pointer type [-Wincompatible-pointer-types]
./main.go:5:24: note: expected ‘_Atomic atomic_int *’ {aka ‘_Atomic int *’} but argument is of type ‘int *’
    5 | void ainit(atomic_int *val) {
      |            ~~~~~~~~~~~~^~~

According to the warning and note, it seems that cgo is lacking translating atomic_init? Did I do anything wrong? Or is there any better and preferred way to get notified from C function?

Ian Lance Taylor

unread,
Feb 15, 2021, 3:19:30 PM2/15/21
to changkun, golang-nuts
Even if there were a way to do this, an atomic variable is not a good
synchronization mechanism, because the other side has to poll the
variable. You can do this as a last resort, but it doesn't sound like
you are at a last resort here. I suggest that you have your C
function call a Go function to write a value on a channel.

Ian

changkun

unread,
Feb 15, 2021, 3:39:14 PM2/15/21
to golang-nuts
Hi Ian,

Thanks for the hint, but I have some follow-up questions:


Even if there were a way to do this, an atomic variable is not a good
synchronization mechanism, because the other side has to poll the
variable.
Indeed, it the other side will be a spin loop to poll the atomic variable, which ...

 
You can do this as a last resort, but it doesn't sound like
you are at a last resort here. I suggest that you have your C
function call a Go function to write a value on a channel.
seems easy to write than this (?). 

Say a Go function foo is called from the C side, to be able to send to the corresponding channel, C must pass that channel to the Go function, which brings to the initial question:  pass a channel from Go to C, is it supported at the moment (?)

How could a Go function be able to send content to differently allocated channels in correspondingly invoked C functions?


Sincerely,
Changkun

Ian Lance Taylor

unread,
Feb 15, 2021, 3:51:03 PM2/15/21
to changkun, golang-nuts
For example, on the Go side write

type chanToUse struct {
c chan int
}

//export MyGoFunction
func MyGoFunction(u unsafe.Pointer, val int) {
cu = (*chanToUse)(u)
cu.c <- val
}

ch := make(chan int)
...
C.MyCFunction(unsafe.Pointer(&chanToUse{ch}))

and on the C side write

void MyCFunction(void *goData) {
...
MyGoFunction(goData, 0);
}

Yes, it's more work. But it's not a good idea to poll an atomic
variable in Go. The Go scheduler reacts badly to busy loops.

Ian

Devon H. O'Dell

unread,
Feb 15, 2021, 3:58:30 PM2/15/21
to changkun, golang-nuts
Forgot to reply to the list. Oops. Sorry for the second delivery, Changkun.

I think this is only a problem if you need a separate channel per invocation for some reason, which seems unlikely. But if you did, you could have a  map of int to chan T and pass the integer key through C.

Kind regards,

--dho




Sincerely,
Changkun

--
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/7adba3a6-ee2f-4923-905d-9afc2b9f796an%40googlegroups.com.

Juliusz Chroboczek

unread,
Feb 18, 2021, 12:21:45 PM2/18/21
to golan...@googlegroups.com
> For example, on the Go side write

> type chanToUse struct {
> c chan int
> }

> //export MyGoFunction
> func MyGoFunction(u unsafe.Pointer, val int) {
> cu = (*chanToUse)(u)
> cu.c <- val
> }

Why the struct? Couldn't you just pass a pointer to a chan?

Ian Lance Taylor

unread,
Feb 18, 2021, 1:31:00 PM2/18/21
to Juliusz Chroboczek, golang-nuts
Sure, that should work too.

Ian

changkun

unread,
Feb 20, 2021, 11:33:44 AM2/20/21
to golang-nuts
Dear Ian, thanks for the inspiration and sorry for the late response. I just got a chance to test your suggestion.
But, it turns out that the wrapping struct can cause the following error:

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

If my understanding of Cgo constraints correctly, the panic is to prevent a potential error when GC moves the Go pointer,
although we don't manipulate the Go pointer (to the channel) from the C side.

How could we avoid this?

Ian Lance Taylor

unread,
Feb 20, 2021, 2:17:50 PM2/20/21
to changkun, golang-nuts
On Sat, Feb 20, 2021 at 8:34 AM changkun <h...@changkun.de> wrote:
>
> Dear Ian, thanks for the inspiration and sorry for the late response. I just got a chance to test your suggestion.
> But, it turns out that the wrapping struct can cause the following error:
>
> panic: runtime error: cgo argument has Go pointer to Go pointer
>
> If my understanding of Cgo constraints correctly, the panic is to prevent a potential error when GC moves the Go pointer,
> although we don't manipulate the Go pointer (to the channel) from the C side.
>
> How could we avoid this?

Oh, sorry. There are lots of ways to go, depending on the details.
The easiest is to store the channel in a global variable on the Go
side, though of course then only one goroutine can use this system at
a time. Or you can build a map on the Go side that maps integers to
channel, then pass the integer to C which will pass it back to Go and
the Go side will look in the map.

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/19463602-158d-4539-9132-fa6d5b09a8c3n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages