Pthread + Boehm GCでSIGSEGV

仕事でテスト辺りが今のメイン作業なので、気晴らしに家でプログラムを作っている。
それで、ここ数日悩んでいたのが、Pthreadを使っている環境でBoehm GCGC_malloc()を使うとSIGSEGVが発生する現象。


ネットで調べても解決しないかったので、解決した方法を書いておく。


細かく見ていくと、スレッドを使ってもmain()では発生せずにスレッド内でGC_malloc()を行うとダメらしい。
色々省略して単純に書くと以下のようなソースとなる。
(本当は色々処理がある。以下のような単純な場合のサンプルソースでは問題は出なかった。)

func(){
	//mainの#if文をコメントアウトして、ここにくるとSIGSEGV
	GC_malloc();
}

main(){
#if 1
	GC_malloc();	//ここだと問題なし。
	return 0;
#endif

	pthread_create(&thread, &attr, thread_func, NULL);
}


ここでまず疑ったのが、aptでインストールしたlibgc(Boehm GC)がスレッド未対応でコンパイルしてるかもということ。
取り敢えず本家からソースをダウンロードしてインストール。
結果は結局変わらず。


仕方がないのでさらに色々試すと、問題が見えてきた。

  • メインスレッドで使うだけでは問題なし。
  • メインスレッドでGC_malloc()を行わずにスレッド内で初めてGC_malloc()を使うと問題発生。
  • メインスレッドでGC_malloc()を行って、スレッド内で初めてGC_malloc()を使うと問題が発生しない。


ここで怪しいのはメインスレッドで一度でもGC_malloc()を使うと問題がでないということ。
つまり、GC_malloc()の中で何か(初期化とか)を行っていてこの処理はスレッド内では行えないということ。
ということは初期化を行うための関数が公開されているはずと疑ってgc.hをのぞいてみる。


GC_malloc()を検索すると以下のように定義されている。

GC_API void * GC_malloc(size_t size_in_bytes);


GC_APIというのは以下のようになっている。

#ifndef GC_API
#define GC_API extern
#endif


取り敢えず、GC_APIで検索したら公開関数があるということなので、検索するとなんと99個もある。

$ grep GC_API include/gc.h |wc -l
99


その中に、GC_init()といういかにもな関数があった。
コメントにもあるようにスレッドを使うときには必要っぽい。
英語が苦手なのでこれぐらいしか分からないけど。

/* Initialize the collector.  This is only required when using thread-local 
 * allocation, since unlike the regular allocation routines, GC_local_malloc
 * is not self-initializing.  If you use GC_local_malloc you should arrange   
 * to call this somehow (e.g. from a constructor) before doing any allocation.
 * For win32 threads, it needs to be called explicitly.
 */
GC_API void GC_init(void);


ということで、GC_init()を明示的に呼んでやることで無事解決。