mutual exclusion ã£ã¦ãªãããã£ããã
ãªããå¿ æ®ºæã½ãã
ã¾ãããã¯ããã¨ãåå¼·ä¼ã§æ¬å½ã¯ SELinux ã®åå¼·ãããã¤ããã ã£ããã ãã©ãé»è»ã®ä¸ã§ãµã¨ mutex çãªãã®ãæ¸ãããã¨ãªããªã¼ã¨æãåºããã®ã§ã¡ãã£ã¨æ¸ãã¦ã¿ã¾ããã
å ã®ããã°ã©ã ã¯ã«ã¦ã³ã¿ããããªãã¤ã
int cnt; void* count_up(void* idp) { int id = (int)idp; int i; printf("thread %d start\n", id); for (i = 0; i < 10000000; i++) { cnt++; } printf("thread %d end\n", id); return NULL; } int main() { int i; pthread_t th[NUM_THREADS]; for (i = 0; i < NUM_THREADS; i++) { pthread_create(&th[i], NULL, count_up, (void*)i); } void* dummy; for (i = 0; i < NUM_THREADS; i++) { pthread_join(th[i], &dummy); } printf("%d\n", cnt); }
ãããã¡ããã¨ããçµæãåºãããã«ãããã¨ãã話ãã¡ãªã¿ã« -O ã¨ãã¤ããã¨ããã ãã§æ£ããçããåºãããã«ãªã£ã¦ãã¾ã£ãããã¾ããããã¼ GCC ã¨ããã®ã¯ãããã§ãããã¨ãããã㧠-O ã¯ç¡ãã§ã
以ä¸ã®ã³ã¼ããã¾ã¨ãããã®ã¯ github ã«ç½®ãã¦ããã¾ãã
http://github.com/shinh/test/blob/e9f989532c3e07482ce7ddd5b7901ddaf1dd7f3e/mutex.c
pthread_mutex_t
pthread_mutex_lock(&mu); cnt++; pthread_mutex_unlock(&mu);
æ®éã
pthread_spinlock_t
pthread_spin_lock(&mu); cnt++; pthread_spin_unlock(&mu);
æ®éã pthread_mutex_t ã¯çµé¨ä¸ã¼ãåæåã§ã使ãã (PTHREAD_MUTEX_INITIALIZER ã使ã£ãæ¹ãããã®ã¯ç«ãè¦ãããæãã) ãã©ã pthread_spinlock_t ã¯ã¼ãã§ã¯ãã¡ã¿ããã ã£ãã spinlock ç¨ã® PTHREAD_SPINLOCK_INITIALIZER ã¨ãã¯è¦ã¤ããããã£ãã®ã§ã pthread_spinlock_init ããã¨ãããã¨ã®ããã ã
Compare and Swap
while (__sync_val_compare_and_swap(&mu, 0, 1)) { sched_yield(); } cnt++; mu = 0;
CAS ã¨ãç¥ãããããã x86 ã¨ãã 㨠cmpxchg ã ãã©ã GCC ã ã¨ã¾ãç°¡åã«ãããã __sync_val_compare_and_swap ã® semantics ã¯ããå¿ãããã ãã©ã以ä¸ã¨åããã¨ã atomic ã«è¡ãªãããã¨æãã°è¯ãããããä¸è¨ã§ã¯ã 2ã¤ã®ã¹ã¬ããã critical section ã«å ¥ããªãããã«ã mu ã 1 ã«ãªã£ã¦ããä»ã® thread ã«å¦çããããããã¦ã 0 ã ã£ãã 1 å ¥ã㦠critical section ã«å ¥ãã¨ãããããªãããã
template<typename T> inline T sync_val_compare_and_swap(T* ptr, T oldval, T newval) { T ret = *ptr; if (ret == oldval) { *ptr = newval; } return ret; }
ã«ã¦ã³ã¿ã¿ãããªç°¡åãªãã®ã ã£ãã critical section ãä½ã£ã¦ãããªãã¦ããã«ã¦ã³ãã¢ããèªä½ã atomic ã«ãã£ã¦ããã°è¯ãã¦ã CAS ã§ãããªãã
while (1) { int v = cnt; if (__sync_val_compare_and_swap(&cnt, v, v+1) == v) break; sched_yield(); }
ã¨ãã§ã§ããã
ãã£ã¡ç³»ã®å¦çãããæ㯠ABA problem ã¨ããã®ã«æ°ãã¤ããæ¹ãè¯ãããããæåã« load ããæã«åå¾ããå¤ã¯ A ã ã£ããã ãã©ããã®å¾ã«ä»ã®ã¹ã¬ããã B ã«å¤æ´ãã¦ããã«ä»ã®ã¹ã¬ããã A ã«æ»ããæã«ãå¤ãã£ã¦ãªããã大ä¸å¤«ã¼ã¨ä»ã®å¤ãæ¸ãã¦ãã¾ããããªåé¡ãã«ã¦ã³ã¿ã®å ´å㯠Pentium 1 ãã¿ãã«ãã¨ãã使ã£ã¦ãã¦ã load ããå¾ã« int ããªã¼ãã¼ããã¼ãã¦ä¸å¨ããããããèµ·ãããããããªããã©ããããã«ããå¤ãä¸è´ãã¦ããªãä»£å ¥ãã¹ã次ã®å¤ãä¸ç·ãªã®ã§ç¹ã«åé¡ã¯èµ·ããªãã
lock prefix
ã«ã¦ã³ã¿ã®å ´åããã£ã¨æã£åãæ©ã lock prefix ã¤ãã¦ããã°ããã GCC ã ã¨è¶ ç°¡åã
__sync_add_and_fetch(&cnt, 1);
ãã¦æè¿ã® GCC 㯠LL/SC 使ã£ã¦ãããå®è£ ããã®ãã§ããããã«ãªã£ã¦ãããããã ãã©ãæè¿ã® GCC ã使ãã PPC ãã·ã³ãç¡ãã£ãã®ã§è©¦ãã¦ãªããçç®±ã lenny ã¨ãã«ããã°ããã¨æããã ãã©ã kernel ã®ãã¼ã¸ã§ã³ä¸ããªãã¨ãããªãã£ã½ããã ããªâ¦
Load-linked / Store-conditional
PPC ã¨ã㯠CAS ãããªã㦠LL/SC ã¨ãç¥ãããããã£ã¡ã®ã¿ã¤ãããµãã¼ããã¦ããããããè¦ã¯ load ããæã«ãã®ã¢ãã¬ã¹ã« linked flag ãç«ã¦ã¦ããã¦ããããæ±ãã¦ãªããã° store conditional ã¯æåãããã£ã¦ããæãã®ãã®ãããã
ã¯ãªãã£ã«ã«ã»ã¯ã·ã§ã³ä½ãå®è£ :
int* mup; asm(" b .yielded;\n" ".yield:\n" " bl sched_yield;\n" ".yielded:\n"); mup = μ asm(".lock_cnt:\n" " lwarx 7, 0, %0;\n" " cmpwi 7, 0;\n" " bne .yield;\n" " addi 7, 7, 1;\n" " stwcx. 7, 0, %0;\n" " bne- .lock_cnt;\n" :"=r"(mup)::"7"); cnt++; mu = 0;
stwcx. ã®æå¾ã®ããªãªããå¿ è¦ã ã¨æ°ä»ãã¦ãªãã¦ããã£ãã RISC ã¯ã¡ã¢ãªã¢ãã¬ã¹ãã¬ã¸ã¹ã¿ã«ãã¼ãããã³ã¼ãã GCC ã«çªã£è¾¼ãã§ãããããå ´åã¯ã©ãããã°ãããã ãããªã¼ã
ã«ã¦ã³ã¿ã load linked ã§èªãå®è£ :
int* cntp = &cnt; asm(".lock_cnt:\n" " lwarx 7, 0, %0;\n" " addi 7, 7, 1;\n" " stwcx. 7, 0, %0;\n" " bne- .lock_cnt;\n" :"=r"(cntp)::"7");
Load linked 㧠load ããå¾ã«ã context switch ãªãããå ¥ãã¨ç¹ã«å¤ãããããã¦ãªãã¦ã store conditional ã¯å¤±æãã weak LL/SC ã¨ãããããããç¾å®ç㪠CPU ã¯ã¾ã weak ããããã¾ãããã ããªã LL ã®å¾ã§å¥ã® LL ãå ¥ã£ãããã¦ã SC ã¯å¤±æããã®ã§ã LL 2åã㦠SC 2åããã°ãæ軽ã«èªçãããããã¯ãã§ãã¡ãããã注æãã£ã¦æããããããµã¼ãããã£ã¦ã¿ããã
int main() { int i, j; int* ip = &i; int* jp = &j; asm(".lock_cnt:\n" " lwarx 7, 0, %0;\n" ".lock_cnt2:\n" " lwarx 8, 0, %1;\n" " stwcx. 8, 0, %1;\n" " bne- .lock_cnt2;\n" " stwcx. 7, 0, %0;\n" " bne- .lock_cnt;\n" :"=&r"(ip),"=&r"(jp)::"7","8"); }
C++0x ã® atomic_compare_exchange_(weak|strong)
LL/SC ã«ã¤ãã¦ããããã£ã¦ãªãã£ã段éã§ã¯ãªã㧠weak 㨠strong ãç¨æãã«ããªããã®ãããããã£ã¦ãªãã£ããã§ããã LL/SC ããã£ã¡ããã¨å½ç¶ã§ããã
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2748.html
è¦ã¯ã
ll r1, object; if (r1 != *expected) { *expected = r1; return false; } sc r1, memory; return <store succeeded>;
ã£ã¦æã㧠weak ã®æ¹ãå®è£ ãã¦ããã£ã¦è©±ãªãã ããã
ã㦠CAS ã LL/SC ã§å®è£ ããã£ã¦ã®ã¯ LL/SC ãªãã·ã³ã§æé©ãªããã©ã¼ãã³ã¹ãåºãæ段ã¨ãã¦é©åãªãã ããããã©ãããããããªãæ°ããããã ããªãä¾ãã°ä¸ã® mutex ã¿ãããªãã¤ã ã¨ã SC ã«å¤±æããæã® fail ãªãããä¸åº¦ LL ãã¦ããã°æåããå¯è½æ§ããããªãã«ãããã busy loop ãã¦ãã£ãæ¹ããããã©ã expected ãä¸è´ããªã fail ãªã lock åããã¡ãã£ã¦ããã ããä»ã®ã¹ã¬ããã«å¦çæã渡ã㦠unlock ãã¦ããã£ãæ¹ããããã¿ãããªãã¨ãããã¨æãããããå¦çããããã° expected ãä¸è´ãã¦ããã©ããããã§ãã¯ãã«ããªããã¨æããã ãã©ãããã£ã¦æããã«ç¡é§ã ãããã¨ãããããªã
çã: x86 以å¤ã® CPU ã¯çµ¶æ» ããã®ã§ããã©ãã§ãããã
追è¨:
ããã¤ã mu = 0 ã§ããã¯ãæ¾ãã¦ããã®ãããã¾ãããããã 㨠WAW ã®é åºä¿è¨¼ãç¡ãç°å¢ã 㨠cnt++ ã¨å ¥ãå¤ãã£ã¡ãããããã¼ã¨ãããããªãã¨ã Cryolite ããã«ææããã ãã¾ãããå ¨ãã ã¨æãã¾ãããã åç¾ç°å¢ã¨ãç¡ãã®ã大å¤ãªæãã§ããã