summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2014-04-15 20:42:39 -0400
committerRich Felker <dalias@aerifal.cx>2014-04-16 02:46:06 -0400
commit6ca7c837aa798cc610550b199d4db638605f17f2 (patch)
tree634a7d8421909b9792a5b718eff89636ba942780 /src
parent52d262fa536768ca97100877f6b023ed3ffe5ff0 (diff)
downloadmusl-6ca7c837aa798cc610550b199d4db638605f17f2.tar.gz
fix deadlock race in pthread_once
at the end of successful pthread_once, there was a race window during which another thread calling pthread_once would momentarily change the state back from 2 (finished) to 1 (in-progress). in this case, the status was immediately changed back, but with no wake call, meaning that waiters which arrived during this short window could block forever. there are two possible fixes. one would be adding the wake to the code path where it was missing. but it's better just to avoid reverting the status at all, by using compare-and-swap instead of swap. (cherry picked from commit 0d0c2f40344640a2a6942dda156509593f51db5d)
Diffstat (limited to 'src')
-rw-r--r--src/thread/pthread_once.c3
1 files changed, 1 insertions, 2 deletions
diff --git a/src/thread/pthread_once.c b/src/thread/pthread_once.c
index 41872f16..e01f6d48 100644
--- a/src/thread/pthread_once.c
+++ b/src/thread/pthread_once.c
@@ -18,7 +18,7 @@ int pthread_once(pthread_once_t *control, void (*init)(void))
* 1 - another thread is running init; wait
* 2 - another thread finished running init; just return */
- for (;;) switch (a_swap(control, 1)) {
+ for (;;) switch (a_cas(control, 0, 1)) {
case 0:
pthread_cleanup_push(undo, control);
init();
@@ -31,7 +31,6 @@ int pthread_once(pthread_once_t *control, void (*init)(void))
__wait(control, &waiters, 1, 0);
continue;
case 2:
- a_store(control, 2);
return 0;
}
}