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に渡すのも基本ダメ
どうすれば良いか。
コードに手をいれない方法
GODEBUG という環境変数に cgocheck=0
を設定します。
export GODEBUG=cgocheck=0
例えばいつも使っているプログラムやパッケージが go1.6 をサポートしていないが、どうしても go1.6 を使わざるを得ない場合など。
コードに手を入れる方法
型を uintptr にする事で、cgoCheckPointer によるポインタチェックを回避出来ます。unsafe.Pointer
から uintptr
に変換し、C側には C.uintptr_t
を経由して渡します。
もちろん本来渡すはずだった引数の型と異なるのでCコンパイラがエラーを出します。そういった場合は呼び出したい関数の wrapper を書く必要があります。
以下、本件を直す方法の一例です。
もちろんここで渡すポインタが解放されてもいいかどうかは呼び出し元が把握しなければなりません。
結論
つらい