Edited at

Go1.6でポインタをcgoの関数へ渡す際に発生するcgoCheckPointerを回避する方法

More than 3 years have passed since last update.

go1.6 はまだβなので以下の回避方法も変わる可能性があります。(おそらくそのままだと思いますが)


そのため、下記の点を注意する必要があります


  • cgoの関数の引数経由以外の方法でGoポインタをCに渡した場合は、そのGoポインタが指すメモリは保護されない

  • cgoの関数から返ってくると保護されていたGoポインタの保護も外れるため、cgoの関数内でGoポインタをどこかに保存して次に呼ばれたcgoの関数内で再利用とかはできない

  • GoポインタへのGoポインタをcgoの関数に渡すのは基本ダメ


    • 保護されるのは引数で渡されたGoポインタが指す先のみで、その指す先の更に指す先は保護されない

    • go1.6beta2では、cgo関数呼び出し時のチェックに引っかかってcgo argument has Go pointer to Go pointerというエラーメッセージが表示される

    • Cで確保されたメモリへのポインタへのGoポインタならOK。Cで確保されたメモリはGC対象ではないため



  • GoポインタへのGoポインタがダメなのと同じ理由で、Goポインタをフィールドに持つ構造体へのGoポインタをcgoに渡すのも基本ダメ

http://qiita.com/saturday06/items/84535c61a3328c02032c


どうすれば良いか。


コードに手をいれない方法

GODEBUG という環境変数に cgocheck=0 を設定します。

export GODEBUG=cgocheck=0

例えばいつも使っているプログラムやパッケージが go1.6 をサポートしていないが、どうしても go1.6 を使わざるを得ない場合など。


コードに手を入れる方法

型を uintptr にする事で、cgoCheckPointer によるポインタチェックを回避出来ます。unsafe.Pointer から uintptr に変換し、C側には C.uintptr_t を経由して渡します。

もちろん本来渡すはずだった引数の型と異なるのでCコンパイラがエラーを出します。そういった場合は呼び出したい関数の wrapper を書く必要があります。

以下、本件を直す方法の一例です。

https://github.com/mattn/go-sqlite3/commit/e969434e25f30ad4d75486b5300808bd4ec29dbd

もちろんここで渡すポインタが解放されてもいいかどうかは呼び出し元が把握しなければなりません。


結論

つらい