http://d.hatena.ne.jp/amachang/20080612/1213244820


お気に入りなサイトのIT戦記より


// ここを volatile にする
// (この変数の値はアトミック(つまり、レジスタにだけあってメモリにないということがない変数に)になる)
volatile char* p = NULL;
pthread_mutex_t m;

void* f(void* _p) {

// ロックかからない
if (p == NULL) {
pthread_mutex_lock(&m);

// ここからはクリティカルセクション

// 一個目の初期化時にここでブロックしたスレッドのために
// もう一回 NULL チェック
if (p == NULL) {

// ここではまだ p に代入しない
// 代入したら別スレッドで初期化されていない p が返ってしまう
char* tmp = (char*)malloc(10);
strcpy(tmp, "hoge");

// クリティカルセクションの最後で代入
p = tmp;
}
pthread_mutex_unlock(&m);
}

*(char**)_p = p;
}


バグっとるよ。
コンパイラは最適化で、tmpを消してpに直接malloc結果を入れる権利があるので、
最初のロック外のif文で!NULLになっても、それは初期化が終わってないかもしれない。

基本的にメモリバリアーを理解してないと、DCLパターンは地雷。
pthread_once()推奨。
(てゆーか、まさにこのためにpthread_once()はある。pthread_mutex()から構築できるようなコンビニエンス関数は規格に入れないぜ。といっていたPOSIXがpthread_onceを入れた理由はここにある)

以下、わかりやすいまとめサイトへの誘導。

バイナリアンなmemologueさんのサイト:
http://d.hatena.ne.jp/yupo5656/20041011/p1


注意: このページではx86用のメモリバリアを
   __asm__ __volatile__ ( "" ::: "memory" );
と紹介していますが、これはユニプロセッサ用なので、Core DuoなまかーなAmachanはちゃんとlfence, sfence命令を入れないとやばいです。


Effective C++を書いたScott Meyersさんのサイト
http://www.nwcpp.org/Downloads/2004/DCLP_notes.pdf


最近、社内向けにメモリバリアのドキュメントを書いたばかりなので、反響があればブログにうpする。


バリアー
光子力バリアー! ランキング!