cmpxchg を GCC 拡張で
http://0xcc.net/blog/archives/000128.html
を見て、なんかこの手の命令はたいてい GCC 拡張にあるんだよなーとか思ってたらありました。 __sync_bool_compare_and_swap と __sync_val_compare_and_swap 。
#include <stdio.h> void once(void) { // int は atomic な読み書きが可能 static volatile int entered; // 最初は 0 int result; result = __sync_val_compare_and_swap(&entered, 0, 1); // cmpxchg になる if (result == 1) { // すでに入ったことがある場合は return; // すぐ出る } // 初回の場合のみ、何かを実行する puts("once"); } int main() { once(); once(); return 0; }
コンパイルは gcc -march=i486 once.c などと、 -march=i486 とかをつけて下さい。 386 だと見つからないとか怒られる…というか、それで結構な時間探しまわってました。ちなみに GCC-4.0.2 とかで見当たらなかったので、たぶん GCC-4.1 以降です。 x86 だけで試してたらあんま意味ないので、 Alpha でも上記コードが動くことを確認しておきました(本当に同時実行で大丈夫なのかとか調べてませんが)。見た感じ alpha と i386 と ia64 と rs6000 と s390 で動きそうです (config/*/*.md で sync_compare で grep しただけ) 。
生成されてるコードは以下みたいな。
once: pushl %ebp movl %esp, %ebp subl $24, %esp movl $0, %eax movl $1, %edx lock cmpxchgl %edx, entered.1778 movl %eax, -4(%ebp) cmpl $1, -4(%ebp) je .L4 subl $12, %esp pushl $.LC0 call puts addl $16, %esp .L4: leave ret
あ、ちなみに __builtin プレフィクスがついてないのは Itanium の ABI にあわせるためとか書いてありました。