Происходит ли гонка данных при использовании cgo?

Горутины работают в разных стеках для cgo и go:

C ничего не знает о соглашении о вызовах Go или растущих стеках, поэтому вызов кода C должен записывать все детали стека goroutine, переключаться на стек C и запускать код C, который не знает, как он был вызван. , или большая среда выполнения Go, отвечающая за программу.

Есть ли возможность инициировать гонку данных для переменных или типов, разделяемых между ними?

Недавно я получил ошибки при инициализации фрагментов, например:

package controlcan

import "C"

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

or

package main

import "controlcan"

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

Оба они случайным образом жалуются на ошибки «неожиданный адрес ошибки»:

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

Обновить

controlcan — это пакет-оболочка, который инкапсулирует функции, определенные в библиотеках .dll или .so, в те, которые удобны для Golang, например, отправка сообщений или получение сообщений от устройств CAN. IMO, это действительно странно, что данные соревнуются в операторе инициализации слайса, в пакете controlcan и других пакетах, импортирующих его.

В функции Receive первая строка объявляет срез с именем cReceive, и я случайно получил ошибки, как упоминалось выше. Я думаю, что причиной unexpected fault address является гонка данных, а не повреждение памяти, но здесь требуется ресурс только типа C.struct__VCI_CAN_OBJ, а не какие-либо переменные. Надеюсь, я был неправ.

Что еще хуже, тип CanObj, который используется в других пакетах, таких как usbcan.go, по-прежнему может вызывать эти ошибки, например, оператор pReceive. Раньше я подозревал, что причиной может быть константа 2500, поскольку исходное выражение cReceive := make([]C.struct__VCI_CAN_OBJ, FRAME_LENGTH_OF_RECEPTION). Но когда я изменил его на 2500 напрямую, ошибка все равно возникает.

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,
        )
    // ...

Обновление 2:

Я перемещаю создание cReceive из Go в Cgo, украденное у Кенни Гранта, и ошибка make-slice строки cReceive устранена. .

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)
// ...

И я до сих пор не могу понять, что происходит при создании 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

person Yang    schedule 23.02.2019    source источник
comment
Ошибка говорит, что вы звоните controlcan.Recievee, который нам неизвестен. Предоставьте дополнительную информацию о пакете controlcan. Что касается гонки данных, да, Cgo может вызвать гонку данных, как и все другие коды.   -  person leaf bebop    schedule 23.02.2019
comment
Что касается общего вопроса, то да, конечно, если есть разделяемая память, могут быть условия гонки.   -  person JimB    schedule 23.02.2019
comment
@leafbebop @JimB Спасибо! Я добавил несколько кодов, и, пожалуйста, проверьте обновление, происходит ли гонка для типа CanObj или C.struct_xxx ?   -  person Yang    schedule 23.02.2019
comment
У вас не может быть гонки на типе, и аргумент pReceive здесь не используется в коде, так что мы не можем знать. Однако вы передаете указатель на слайс go в вызов функции C, поэтому вполне вероятно, что вы просто повреждаете память программы, и этот вызов случается, когда что-то терпит неудачу.   -  person JimB    schedule 25.02.2019
comment
@JimB Спасибо! Это правда, что я по ошибке повредил память программы, передав SliceHeader вместо 1-го элемента массива в функцию C, затем C перепутал непрерывные адреса в памяти, которые распознаются как доступные как в Go, так и в C. Трассировка стека показывает ошибку выделения памяти с этими адресами, ИМО. Очень легко запутаться, так как ни один из них не является реальным местом, где произошло повреждение. Еще раз спасибо!   -  person Yang    schedule 26.02.2019