X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=src%2Fthread%2Fpthread_once.c;h=8e8d40ae8331dad752553bac7f1ccaac8e909efa;hb=b1dfb734a45d4f74c7a24c5f07d37f7e74451802;hp=41872f16cb47462845c62d70417e2143448fdfa5;hpb=7e6be42a77989c01155bdc7333ea58206e1563d4;p=musl diff --git a/src/thread/pthread_once.c b/src/thread/pthread_once.c index 41872f16..8e8d40ae 100644 --- a/src/thread/pthread_once.c +++ b/src/thread/pthread_once.c @@ -2,36 +2,49 @@ static void undo(void *control) { - a_store(control, 0); - __wake(control, 1, 0); + /* Wake all waiters, since the waiter status is lost when + * resetting control to the initial state. */ + if (a_swap(control, 0) == 3) + __wake(control, -1, 1); } -int pthread_once(pthread_once_t *control, void (*init)(void)) +hidden int __pthread_once_full(pthread_once_t *control, void (*init)(void)) { - static int waiters; - - /* Return immediately if init finished before */ - if (*control == 2) return 0; - - /* Try to enter initializing state. Three possibilities: + /* Try to enter initializing state. Four possibilities: * 0 - we're the first or the other cancelled; run init * 1 - another thread is running init; wait - * 2 - another thread finished running init; just return */ + * 2 - another thread finished running init; just return + * 3 - another thread is running init, waiters present; wait */ - for (;;) switch (a_swap(control, 1)) { + for (;;) switch (a_cas(control, 0, 1)) { case 0: pthread_cleanup_push(undo, control); init(); pthread_cleanup_pop(0); - a_store(control, 2); - if (waiters) __wake(control, -1, 0); + if (a_swap(control, 2) == 3) + __wake(control, -1, 1); return 0; case 1: - __wait(control, &waiters, 1, 0); + /* If this fails, so will __wait. */ + a_cas(control, 1, 3); + case 3: + __wait(control, 0, 3, 1); continue; case 2: - a_store(control, 2); return 0; } } + +int __pthread_once(pthread_once_t *control, void (*init)(void)) +{ + /* Return immediately if init finished before, but ensure that + * effects of the init routine are visible to the caller. */ + if (*(volatile int *)control == 2) { + a_barrier(); + return 0; + } + return __pthread_once_full(control, init); +} + +weak_alias(__pthread_once, pthread_once);