golang qml (go-qml) аргумент cgo имеет указатель Go на указатель Go

Привет, я использую библиотеку qml для создания пользовательских интерфейсов. Я пытаюсь научиться передавать информацию из пользовательского интерфейса (qml), чтобы потом "что-то делать". QML работает, если это просто пользовательский интерфейс. Я могу запустить это нормально, когда я делаю:

func main() {
    if len(os.Args) != 2 {
        fmt.Fprintf(os.Stderr, "usage: %s <qml file>\n", os.Args[0])
        os.Exit(1)
    }
    if err := qml.Run(run); err != nil {
        fmt.Fprintf(os.Stderr, "error: %v\n", err)
        os.Exit(1)
    }
}

func run() error {
    engine := qml.NewEngine()

    engine.On("quit", func() { os.Exit(0) })

    component, err := engine.LoadFile(os.Args[1])
    if err != nil {
        return err
    }
    window := component.CreateWindow(nil)
    window.Show()
    window.Wait()
    return nil
}

Однако, когда я добавляю некоторый код, чтобы попытаться "изучить" что-то из пользовательского интерфейса, я получаю ошибку времени выполнения:

паника: ошибка во время выполнения: аргумент cgo имеет указатель Go на указатель Go

Код, который я добавляю:

window.On("visibleChanged", func(visible bool) {
    if (visible) {
            fmt.Println("Width:", window.Int("width"))
    }
})

Я использую "go version go1.6 darwin/amd64" на OSX El Capitan

Есть идеи почему? Google предполагает, что это было ошибкой в ​​Go 1.6 Beta, но у меня установлена ​​последняя стабильная версия (установлена ​​пару дней назад).

Если это не простое исправление, может кто-нибудь объяснить, почему это происходит?

3 ответа

Решение

Если вы просто играете вокруг, я предлагаю попробовать перейти с версии 1.5.3. Go 1.6 ввел другой набор ограничений для указателей на память при использовании cgo, более ограниченный набор, и возможно, что некоторые пакеты go, которые были разработаны для более старой версии go, теперь нарушают правило go или два.

Если это так, заставить старый пакет работать с go 1.6, где C разрешено вызывать замыкания go, может быть сложнее исправить. Но у меня пока нет личного опыта с этим.

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

Чтобы избежать этого, уловка, используемая большинством библиотек, заключалась также в том, чтобы удерживать указатель в Go (например, в глобальной карте), чтобы гарантировать защиту памяти от сборщика мусора. go-qml также использует этот трюк. Этот прием работает, но компилятор и сборщик мусора понятия не имеют, что он делает, они не могут проверить, что вы не делаете ошибку (например, удаляете указатель Go, в то время как код C все еще имеет свой указатель).

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

Вот проблема об этом: https://github.com/go-qml/qml/issues/170

Примечание: в данном конкретном случае то, что передается C, это указатель на interface{}, который сам содержит указатель на функцию. Вот почему вы получаете сообщение об ошибке "Аргумент cgo имеет указатель Go на указатель Go". Причина, по которой это не разрешено, заключается в том, что эти указатели труднее защитить от GC на время вызова C, и это того не стоит, поэтому вместо этого это запрещено ( https://github.com/golang/go/issues/12416). Однако, даже если бы это было разрешено, код все равно нарушал бы правило о том, что в коде C сохраняется копия указателя Go. На самом деле это не проблема в Go 1.6, так как он не реализует движущийся сборщик мусора, но правила были сделаны так, чтобы его можно было реализовать позже.

Спасибо за всю помощь здесь. Я написал учебник для начинающих по использованию QML с Go. Это можно посмотреть здесь. Я буду постоянно обновлять его, если столкнусь с какими-либо еще ошибками и найду исправления. Спасибо всем за вашу помощь. QML/GO - отличная комбинация.

https://golog.co/blog/article/Using_Go_with_QML_part_1

Другие вопросы по тегам