flock(2) は thread 間で排他するか
「〜ったら?」と言うだけなのもアレなので、書いてみた。ちなみに向こうの comment 欄では deadlock がどうとか世迷い言を言ってるけど、気にしないように。
Thread を二つ作って、気ままに flock() させる。Thread 間のときだけ動作不定なんてことは無いだろうから、I/O error の類には遭遇しないものと仮定すると、両方 lock が成功するか、片方失敗する。両方成功した場合は、sem_trywait() で早かった方が sem_wait() で待ちに入り、もう一方が起こす。片方失敗の場合は、早かった方が sem_wait() するので、負け犬の方が flock() (を試みて失敗) するまで lock を持ちつづけることが保証される。
TODO: 思惑通りの動作をすることを証明する。
#include <sys/file.h> #include <pthread.h> #include <assert.h> #include <semaphore.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #define TEST_FILE "test.txt" static sem_t sem; void * f (void *_) { FILE *fp = fopen (TEST_FILE, "a+"); int fd = fileno (fp); puts ("Thread"); if (fd == -1) { perror (NULL); exit (1); } if (flock (fd, LOCK_EX | LOCK_NB) == 0) { /* I got the lock. The semaphore starts out with value 1, so we * receive EAGAIN iff we're the second to receive the lock. */ if (sem_trywait (&sem) == 0) { /* The second thread will sem_post() once it sees that the * file is locked, which it detects either by seeing the * semaphore's value or by the return value of flock(). */ sem_wait (&sem); } else if (errno == EWOULDBLOCK) { /* Loser thread. Feed the winner. */ puts ("Both threads successfully locked."); sem_post (&sem); } else assert (0); } else { if (errno == EWOULDBLOCK) { /* The other thread has the lock. Wake it up and exit. */ puts ("One thread couldn't lock the file."); sem_post (&sem); } else assert (0); } fclose (fp); return NULL; } int main () { pthread_t ths[2]; int i; sem_init (&sem, 0, 1); for (i = 0; i < 2; i++) pthread_create (&ths[i], NULL, f, NULL); for (i = 0; i < 2; i++) pthread_join (ths[i], NULL); return 0; }