runtime.SetFinalizer: не удалось определить тип имени для C .Char - PullRequest
0 голосов
/ 20 июня 2020

Пожалуйста, обратите внимание на этот пример go кода ниже:

package main

/*
#include <stdio.h>
#include <stdlib.h>
*/
import "C"


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

func main() {
    // Convert Go string to C string using C.CString
    cString := C.CString("Wold!")
    fmt.Printf("C.CString type: %T\n", cString)
    //C.free(unsafe.Pointer(cString)) // <-- this works, but I don't want to free it manually..

    runtime.SetFinalizer(&cString, func(t *C.Char) {
        C.free(unsafe.Pointer(t))
    })
}

Я экспериментирую с cGo и пытаюсь освободить cString. Когда я пытаюсь освободить свою переменную cString, используя runtime.SetFinalizer, я получаю:

$ go build a.go 
# command-line-arguments
./a.go:22:41: could not determine kind of name for C.Char

Пожалуйста, укажите мне правильное направление. Спасибо!

1 Ответ

1 голос
/ 21 июня 2020

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

Это действительно нормально, потому что ваш код никогда бы не работал так, как вы изначально хотели. Финализатор времени выполнения запускается, когда сборщик мусора Go готов освободить объект Go, который занимает Go память, но C.Cstring возвращает указатель, который является не Go памятью. В частности, обратите внимание на следующую цитату из документации cgo :

// Go string to C string
// The C string is allocated in the C heap using malloc.
// It is the caller's responsibility to arrange for it to be
// freed, such as by calling C.free (be sure to include stdlib.h
// if C.free is needed).
func C.CString(string) *C.char

Поскольку возвращенная строка находится в куче «C "он никогда не будет завершен сборщиком мусора Go. Если бы ваш код был скомпилирован, он бы просто не работал.

Если у вас есть объект Go, время жизни которого совпадает со временем жизни объекта C, вы, возможно, могли бы его использовать. Вот выдуманный (но работающий) пример:

package main

/*
#include <stdio.h>
#include <stdlib.h>
*/
import "C"

import (
        "fmt"
        "runtime"
        "time"
        "unsafe"
)

type S struct {
        Foo    int
        ToFree unsafe.Pointer
}

func main() {
        doit()
        runtime.GC()
        time.Sleep(10 * time.Millisecond) // ugly hack
}

func doit() {
        cString := C.CString("Wold!")
        fmt.Printf("C.CString type: %T\n", cString)
        x := &S{Foo: 1, ToFree: unsafe.Pointer(cString)}
        runtime.SetFinalizer(x, func(t *S) {
                fmt.Println("freeing C string")
                C.free(t.ToFree)
        })
}

Когда выделенный объект для x выходит за рамки, он становится подходящим для G C. Фактический G C может никогда не произойти, поэтому я заставил его указать runtime.GC() в main. Это запускает финализатор:

$ ./cfree_example
C.CString type: *main._Ctype_char
freeing C string

Здесь присутствует «уродливый взлом», потому что если main возвращается до того, как вызов финализатора завершит запись сообщений freeing C string, он будет потерян. В реальной программе вам это не понадобится.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...