Правильный способ записи в память C из go

#c #go #pointers #garbage-collection #cgo

# #c #Вперед #указатели #сбор мусора #cgo

Вопрос:

Я хотел бы перенести программу go в виде библиотеки, экспортировав некоторые API-интерфейсы C. В C обычно функция принимает буфер (указатель) в качестве параметра (вместе с его размером) и записывает в него выходные данные. Например (из стандартной библиотеки C)

 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
 

Это приведет к записи в *ptr указанное вызывающим объектом не более size*nmemb байтов. Преимущество такого дизайна заключается в том, что память управляется вызывающим объектом, и, следовательно, функции не нужно иметь дело с выделением памяти. Я хочу, чтобы мой API, экспортированный из go, также выглядел так.

Однако я обнаружил, что нет прямого способа записи в указатель в go. Итак, я создал что-то вроде этого (небольшая модификация случая 6 этой документации), который создает []byte объект в go, используя базовую память C.

 // create a byte array using C memory for internal use
func createBuffer(buf unsafe.Pointer, size int) []byte {
    var res []byte
    hdr := (*reflect.SliceHeader)(unsafe.Pointer(amp;res))
    hdr.Data = uintptr(unsafe.Pointer(buf))
    hdr.Len = size
    hdr.Cap = size
    return res
}
 

Затем я экспортировал свою функцию следующим образом ( readers это фрагмент io.Reader , unsafe.Pointer будет иметь тип void* после экспорта), который будет считывать некоторые данные в память C, содержащую не более size байтов.

 //export read
func read(id int, buf unsafe.Pointer, size C.int) int {
    res, err := readers[id].Read(createBuffer(buf, int(size)))  // works
    //res, err := readers[id].Read(C.GoBytes(buf, size)) // does not work
    if err != nil {
        return -1
    }
    return res
}
 

Это работает так, как ожидалось (т. Е. В моей программе на C буфер действительно содержит нужные данные после вызова read ), но я не уверен, является ли это правильным способом сделать это (т. Е. Есть ли проблемы с памятью, особенно связанные со сборкой мусора (GC)).

Предположим, что результат []byte , возвращенный из createBuffer , больше нигде не используется после read завершения, как память будет освобождена с помощью GC? Будет ли GC также пытаться освободить hdr.Data , который должен быть частью памяти, управляемой программой C? Официальная документация содержит немало предупреждений об этом. Если это вызовет проблемы с GC, как правильно это сделать? (Я уже пробовал C.GoBytes(...) , кажется, он копирует базовый буфер и, следовательно, не работает)