X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=src%2Fthread%2Fsynccall.c;h=a6b177c06fd3d9e8d403cbc5485203af1582d629;hb=bf14ef193b4203aa9a8b173faeeea06d98397f65;hp=fd377cb360b0bdd9b8f14fc0306148c436ee28e3;hpb=2f437040e7911d9bef239588ea7ed6f4b9102922;p=musl diff --git a/src/thread/synccall.c b/src/thread/synccall.c index fd377cb3..a6b177c0 100644 --- a/src/thread/synccall.c +++ b/src/thread/synccall.c @@ -1,111 +1,120 @@ #include "pthread_impl.h" #include +#include -static struct chain { - struct chain *next; - sem_t sem, sem2; -} *head, *cur; +static void dummy_0(void) +{ +} + +weak_alias(dummy_0, __tl_lock); +weak_alias(dummy_0, __tl_unlock); +static int target_tid; static void (*callback)(void *), *context; -static int chainlen; -static sem_t chainlock, chaindone; -static pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER; +static sem_t target_sem, caller_sem; -static void handler(int sig, siginfo_t *si, void *ctx) +static void dummy(void *p) { - struct chain ch; - pthread_t self = __pthread_self(); - int old_errno = errno; +} - if (chainlen == libc.threads_minus_1) return; +static void handler(int sig) +{ + if (__pthread_self()->tid != target_tid) return; - sigqueue(self->pid, SIGSYNCCALL, (union sigval){0}); + int old_errno = errno; - /* Threads which have already decremented themselves from the - * thread count must not act. Block further receipt of signals - * and return. */ - if (self->dead) { - memset(&((ucontext_t *)ctx)->uc_sigmask, -1, 8); - errno = old_errno; - return; - } + /* Inform caller we have received signal and wait for + * the caller to let us make the callback. */ + sem_post(&caller_sem); + sem_wait(&target_sem); - sem_init(&ch.sem, 0, 0); - sem_init(&ch.sem2, 0, 0); + callback(context); - while (sem_wait(&chainlock)); - ch.next = head; - head = &ch; - if (++chainlen == libc.threads_minus_1) sem_post(&chaindone); - sem_post(&chainlock); + /* Inform caller we've complered the callback and wait + * for the caller to release us to return. */ + sem_post(&caller_sem); + sem_wait(&target_sem); - while (sem_wait(&ch.sem)); - callback(context); - sem_post(&ch.sem2); - while (sem_wait(&ch.sem)); + /* Inform caller we are returning and state is destroyable. */ + sem_post(&caller_sem); errno = old_errno; } void __synccall(void (*func)(void *), void *ctx) { - pthread_t self; - struct sigaction sa; - struct chain *next; - uint64_t oldmask; - - if (!libc.threads_minus_1) { - func(ctx); - return; - } - - pthread_rwlock_wrlock(&lock); - - __syscall(SYS_rt_sigprocmask, SIG_BLOCK, SIGALL_SET, - &oldmask, __SYSCALL_SSLEN); + sigset_t oldmask; + int cs, i, r; + struct sigaction sa = { .sa_flags = SA_RESTART | SA_ONSTACK, .sa_handler = handler }; + pthread_t self = __pthread_self(), td; + int count = 0; + + /* Blocking signals in two steps, first only app-level signals + * before taking the lock, then all signals after taking the lock, + * is necessary to achieve AS-safety. Blocking them all first would + * deadlock if multiple threads called __synccall. Waiting to block + * any until after the lock would allow re-entry in the same thread + * with the lock already held. */ + __block_app_sigs(&oldmask); + __tl_lock(); + __block_all_sigs(0); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); + + sem_init(&target_sem, 0, 0); + sem_init(&caller_sem, 0, 0); + + if (!libc.threads_minus_1 || __syscall(SYS_gettid) != self->tid) + goto single_threaded; - sem_init(&chaindone, 0, 0); - sem_init(&chainlock, 0, 1); - chainlen = 0; callback = func; context = ctx; - sa.sa_flags = SA_SIGINFO | SA_RESTART; - sa.sa_sigaction = handler; - sigfillset(&sa.sa_mask); + /* Block even implementation-internal signals, so that nothing + * interrupts the SIGSYNCCALL handlers. The main possible source + * of trouble is asynchronous cancellation. */ + memset(&sa.sa_mask, -1, sizeof sa.sa_mask); __libc_sigaction(SIGSYNCCALL, &sa, 0); - self = __pthread_self(); - sigqueue(self->pid, SIGSYNCCALL, (union sigval){0}); - while (sem_wait(&chaindone)); - for (cur=head; cur; cur=cur->next) { - sem_post(&cur->sem); - while (sem_wait(&cur->sem2)); + for (td=self->next; td!=self; td=td->next) { + target_tid = td->tid; + while ((r = -__syscall(SYS_tkill, td->tid, SIGSYNCCALL)) == EAGAIN); + if (r) { + /* If we failed to signal any thread, nop out the + * callback to abort the synccall and just release + * any threads already caught. */ + callback = func = dummy; + break; + } + sem_wait(&caller_sem); + count++; } - func(ctx); + target_tid = 0; - for (cur=head; cur; cur=next) { - next = cur->next; - sem_post(&cur->sem); + /* Serialize execution of callback in caught threads, or just + * release them all if synccall is being aborted. */ + for (i=0; i