A better way of events notification from cgo to go side

247 views
Skip to first unread message

Remus Clearwater

unread,
Sep 17, 2017, 2:08:03 PM9/17/17
to golang-nuts
Hi, I wonder if there has a small cost, little latency and high efficient solution 
of event notification from cgo to go side.

Here is 3 ways as far as I know:

    1. Poll

        go func() {
            for {
                // fast ret from cgo 
                ret := C.sema_try_wait()
                if new events {
                    do_new_event_tasks()
                }else{
                    time.Sleep(time.Millisecond * 250)
                }
            }
        }()

        Disadvantage:
        
            Would be some overhead and constant latency.

    2. Long wait

        go func() {
            for {
                // maybe blocking at cgo side arbitrarily long time
                ret := C.sema_wait()
                do_new_event_tasks()
            }
        }()

        Disadvantage:

            As been discussed in issue 12416:

Q: What kind of errors will be returned when a C function takes too long? 
What is the story with regards to read(2) and blocking devices? (by taruti)
 
A: In the default mode, nothing will happen if a C function takes too long, 
except that your program might eventually run out of memory because the GC 
can't run (that won't happen with 1.6 but it might happen in future releases). 
In the checking mode, you will get a panic. If you want to call a C function 
that does a read on a device that blocks arbitrarily long, you should read the 
data into a buffer allocated by C. We will make the syscall.Read function do the 
right thing on a long-blocking read, whatever that turns out to be.
So, if one cgo call takes too long is not recommend, we still need a cgo notify to 
go mechanism. (by ianlancetaylor)

            So, if the cgo call takes too long to return is not recommend, we still need another cgo 
            notify to go mechanism. 

    3. Raise a signal

        c := make(chan os.Signal, 1)
        signal.Notify(c, SignalUsr1)
        for{
            // cgo just raise SignalUsr1 to itself when 
            // it want to notify go side
            s := <-c
            do_new_event_tasks()
        }

        Disadvantage:

            A little wierd, and when there is about 100000 new events to notify per seconds, the overhead would be huge.

Is there some much better methods? 

Thanks a lot.

-- Remus

Tamás Gulácsi

unread,
Sep 17, 2017, 4:41:17 PM9/17/17
to golang-nuts
C.sema_timedwait ?
No latency, and with a few seconds for timeout, not too long.

Remus Clearwater

unread,
Sep 17, 2017, 6:28:11 PM9/17/17
to golang-nuts
But according to the man sem_timedwait(3), the abs_timeout is subjected to the 
`discontinuous jumps in the system time (e.g., if the system administrator manually 
changes the clock)`, and `adjtime(3) & NTP`.

Imaging the system administrator manually put the clock say 10 seconds behind,
thus the `C.sema_timedwait ` is just nearly the same as the method 3 `long wait`.

Remus Clearwater

unread,
Sep 17, 2017, 6:43:03 PM9/17/17
to golang-nuts
In the 3rd method `long wait`, if the cgo could emit some dummy "heartbeat" events 
notification periodically (e.g., 10ms), that would be nearly the same as `C.sema_timedwait` 
although there will be a little overhead.

Remus Clearwater

unread,
Sep 17, 2017, 7:34:27 PM9/17/17
to golang-nuts
There maybe a 4th method is to use something like eventfd/socketpair between go and cgo sides,
but it still has a long-blocking call in syscall.Read unless we could add supports of epoll-eventfd/socketpair 
to the go runtime.

Sokolov Yura

unread,
Sep 18, 2017, 1:10:22 AM9/18/17
to golang-nuts
Isn't there a way to pass Go callback to Cgo? In this callback you may do anything.

Sokolov Yura

unread,
Sep 18, 2017, 1:15:05 AM9/18/17
to golang-nuts
And I agree that socketpair and regular read from will also do the job.
https://github.com/prep/socketpair/blob/master/socketpair.go

Ian Lance Taylor

unread,
Sep 18, 2017, 1:27:19 AM9/18/17
to Sokolov Yura, golang-nuts
On Sun, Sep 17, 2017 at 10:10 PM, Sokolov Yura <funny....@gmail.com> wrote:
>
> Isn't there a way to pass Go callback to Cgo? In this callback you may do anything.

You can't pass a simple callback, but you can have your C code call a
Go function whenever some event occurs. Start a new thread in C, have
it wait, and have it call Go when appropriate.

Ian

remus clearwater

unread,
Sep 22, 2017, 6:14:16 AM9/22/17
to Ian Lance Taylor, Sokolov Yura, golang-nuts
Sorry for getting back so late.

Here is the conclusion after many testing:

1. C Call Go is the best method as far as I could find (Thanks Ian!): √ 

  The latency could nearly always keep less than 100us no matter the 
  system is under light load or moderated heavy load.

2. Socketpair ( Thanks Sokolov for the recommendation):

  In fact, the net.FileConn could add a socket fd into the golang runtime 
  event poll directly, thus we could read/write a conn generated from `Socketpair` 
  without blocking the OS thread and go routine. But the latency of this method is 
  hard to control. When the system is under light load, the latency is pretty fine 
  (nearly the same as `C Call Go`), but the latency could be often more than 1ms 
  when the system is under moderated heavy load. That is due to the different 
  priority of IPCs implementation in the kernel.

Hope it will be helpful to people who meet the same question in the future.

Enjoy!


--
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+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages