Does data race happen when using cgo?

1

Goroutines runs in different stack for cgo and go:

C doesn’t know anything about Go’s calling convention or growable stacks, so a call down to C code must record all the details of the goroutine stack, switch to the C stack, and run C code which has no knowledge of how it was invoked, or the larger Go runtime in charge of the program.

Is there any possiblities to trigger data race for variables or types shared between them?

Recently, I got errors when initialize slices like:

package controlcan

import "C"

cReceive := make([]C.struct__CAN_OBJ, 2500)

or

package main

import "controlcan"

pReceive := make([]controlcan.CanObj, 2500)

Both of them complains "unexpected fault address" errors randomly:

unexpected fault address 0xffffffffffffffff
fatal error: fault
[signal 0xc0000005 code=0x0 addr=0xffffffffffffffff pc=0x41c65d]

goroutine 41 [running]:
runtime.throw(0xcc969a, 0x5)
        /usr/local/go/src/runtime/panic.go:619 +0x88 fp=0xc0428ffb38 sp=0xc0428ffb18 pc=0x42d0b8
runtime.sigpanic()
        /usr/local/go/src/runtime/signal_windows.go:170 +0x13a fp=0xc0428ffb68 sp=0xc0428ffb38 pc=0x43fcca
runtime.gcMarkRootPrepare()
        /usr/local/go/src/runtime/mgcmark.go:72 +0x5d fp=0xc0428ffb70 sp=0xc0428ffb68 pc=0x41c65d
runtime.gcStart(0x0, 0x1, 0x0, 0x0)
        /usr/local/go/src/runtime/mgc.go:1350 +0x30f fp=0xc0428ffba0 sp=0xc0428ffb70 pc=0x419b6f
runtime.mallocgc(0x10000, 0xc54660, 0xc0422ee001, 0xc0423ded60)
        /usr/local/go/src/runtime/malloc.go:803 +0x448 fp=0xc0428ffc40 sp=0xc0428ffba0 pc=0x411c48
runtime.makeslice(0xc54660, 0x9c4, 0x9c4, 0xc04202ce00, 0xc04202c000, 0x411b23)
        /usr/local/go/src/runtime/slice.go:61 +0x7e fp=0xc0428ffc70 sp=0xc0428ffc40 pc=0x43fffe
controlcan.Receive(0x4, 0x0, 0x0, 0xc04267e000, 0x9c4, 0x9c4, 0x64, 0x0, 0x0, 0x0)
        /media/sf_GOPATH0/src/controlcan/controlcan.go:262 +0x75 fp=0xc0428ffd70 sp=0xc0428ffc70 pc=0xa0d795
posam/protocol/usbcan.(*Channel).receive(0xc04229d490)
        /media/sf_GOPATH0/src/posam/protocol/usbcan/usbcan.go:469 +0x536 fp=0xc0428fffd8 sp=0xc0428ffd70 pc=0xa10926
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2361 +0x1 fp=0xc0428fffe0 sp=0xc0428fffd8 pc=0x457531
created by posam/protocol/usbcan.(*Channel).Start
        /media/sf_GOPATH0/src/posam/protocol/usbcan/usbcan.go:242 +0x3aa

Update

controlcan is a wrapper package that encapsulates functions defined in .dll or .so libraries to the ones that Golang-friendly, e.g., sending messages to, or receiving messages from CAN devices. IMO, it's really weird that data race on the statement of slice initialization, within the controlcan package and other packages imported it.

In the Receive function, the first line declares a slice called cReceive and I got the errors randomly as mentioned above. I think the reason of unexpected fault address is data race rather than memory corruption, but the resource required here is only the type C.struct__VCI_CAN_OBJ, not any variables. I hope I was wrong.

What's worse, the type CanObj, which is used in othe packages like usbcan.go, still possibly triggers that errors, e.g., the statement of pReceive. Previously, I suspected that the reason might be the constant of 2500, since the original statement is cReceive := make([]C.struct__VCI_CAN_OBJ, FRAME_LENGTH_OF_RECEPTION). But when I changed it to 2500 directly, the error occurs all the same.

controlcan.go

package controlcan

import "C"
import (
    "fmt"
    "runtime"
    "unsafe"
)

const (
    FRAME_LENGTH_OF_RECEPTION = 2500
)

type CanObj struct { // <- accessable for other packages/goroutines
    ID         int
    TimeStamp  int
    TimeFlag   int
    SendType   byte
    RemoteFlag byte
    ExternFlag byte
    DataLen    byte
    Data       [8]byte
    Reserved   [3]byte
}

func Receive(
    devType int,
    devIndex int,
    canIndex int,
    pReceive []CanObj,
    waitTime int,
) (count int, err error) {
    cReceive := make([]C.struct__VCI_CAN_OBJ, 2500) // unexpected fault address
    cCount := C.VCI_Receive(
        C.uint(devType),
        C.uint(devIndex),
        C.uint(canIndex),
        (*C.struct__VCI_CAN_OBJ)(unsafe.Pointer(&cReceive)),
        C.uint(FRAME_LENGTH_OF_RECEPTION),
        C.int(waitTime),
    )

    // ...
}

usbcan.go

package usbcan

import "controlcan"

func (c *Channel) receive() {
    ticker := time.NewTicker(100 * time.Millisecond)
    defer ticker.Stop()
    for _ = range ticker.C {
        pReceive := make([]controlcan.CanObj, 2500) // unexpected fault address
        count, err := controlcan.Receive(
            c.DevType,
            c.DevIndex,
            c.CanIndex,
            pReceive,
            100,
        )
    // ...

Update 2:

I move the cReceive creation from Go to Cgo, stolen from Kenny Grant, and the make-slice error of cReceive line is solved.

package controlcan

// static VCI_CAN_OBJ** makeCanObjArray(){
//     return calloc(sizeof(VCI_CAN_OBJ*), 2500);
// }
// static void freeCanObjArray(VCI_CAN_OBJ **canObjArray){
//     int i;
//     for (i=0; i<2500;i++)
//         free(canObjArray[i]);
//     free(canObjArray);
// }
import "C"

func Receive(
    devType int,
    devIndex int,
    canIndex int,
    pReceive []CanObj,
    waitTime int,
) (count int, err error) {
    cReceive := C.makeCanObjArray()
    defer C.freeCanObjArray(cReceive)
// ...

And I still can't figure out what happens when creating pReceive:

pReceive := make([]controlcan.CanObj, 2500)
unexpected fault address 0xffffffffffffffff
fatal error: fault
[signal 0xc0000005 code=0x0 addr=0xffffffffffffffff pc=0x41c65d]

goroutine 28 [running]:
runtime.throw(0xcc969a, 0x5)
        /usr/local/go/src/runtime/panic.go:619 +0x88 fp=0xc04275bc38 sp=0xc04275bc18 pc=0x42d0b8
runtime.sigpanic()
        /usr/local/go/src/runtime/signal_windows.go:170 +0x13a fp=0xc04275bc68 sp=0xc04275bc38 pc=0x43fcca
runtime.gcMarkRootPrepare()
        /usr/local/go/src/runtime/mgcmark.go:72 +0x5d fp=0xc04275bc70 sp=0xc04275bc68 pc=0x41c65d
runtime.gcStart(0x0, 0x1, 0x0, 0x0)
        /usr/local/go/src/runtime/mgc.go:1350 +0x30f fp=0xc04275bca0 sp=0xc04275bc70 pc=0x419b6f
runtime.mallocgc(0x1a000, 0xc54520, 0x1, 0xa10101)
        /usr/local/go/src/runtime/malloc.go:803 +0x448 fp=0xc04275bd40 sp=0xc04275bca0 pc=0x411c48
runtime.makeslice(0xc54520, 0x9c4, 0x9c4, 0xc0420162a0, 0x8, 0x8)
        /usr/local/go/src/runtime/slice.go:61 +0x7e fp=0xc04275bd70 sp=0xc04275bd40 pc=0x43fffe
posam/protocol/usbcan.(*Channel).receive(0xc04213fdc0)
        /media/sf_GOPATH0/src/posam/protocol/usbcan/usbcan.go:464 +0x4f0 fp=0xc04275bfd8 sp=0xc04275bd70 pc=0xa108e0
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2361 +0x1 fp=0xc04275bfe0 sp=0xc04275bfd8 pc=0x457531
created by posam/protocol/usbcan.(*Channel).Start
        /media/sf_GOPATH0/src/posam/protocol/usbcan/usbcan.go:242 +0x3aa
go
can-bus
cgo
data-race
asked on Stack Overflow Feb 23, 2019 by Yang • edited Feb 25, 2019 by Yang

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0