1#include "pthread_impl.h" 2#include <semaphore.h> 3#include <string.h> 4 5static void dummy_0(void) 6{ 7} 8 9weak_alias(dummy_0, __tl_lock); 10weak_alias(dummy_0, __tl_unlock); 11 12static int target_tid; 13static void (*callback)(void *), *context; 14static sem_t target_sem, caller_sem; 15 16static void dummy(void *p) 17{ 18} 19 20static void handler(int sig) 21{ 22 if (__pthread_self()->tid != target_tid) return; 23 24 int old_errno = errno; 25 26 /* Inform caller we have received signal and wait for 27 * the caller to let us make the callback. */ 28 sem_post(&caller_sem); 29 sem_wait(&target_sem); 30 31 callback(context); 32 33 /* Inform caller we've complered the callback and wait 34 * for the caller to release us to return. */ 35 sem_post(&caller_sem); 36 sem_wait(&target_sem); 37 38 /* Inform caller we are returning and state is destroyable. */ 39 sem_post(&caller_sem); 40 41 errno = old_errno; 42} 43 44void __synccall(void (*func)(void *), void *ctx) 45{ 46 sigset_t oldmask; 47 int cs, i, r; 48 struct sigaction sa = { .sa_flags = SA_RESTART | SA_ONSTACK, .sa_handler = handler }; 49 pthread_t self = __pthread_self(), td; 50 int count = 0; 51 52 /* Blocking signals in two steps, first only app-level signals 53 * before taking the lock, then all signals after taking the lock, 54 * is necessary to achieve AS-safety. Blocking them all first would 55 * deadlock if multiple threads called __synccall. Waiting to block 56 * any until after the lock would allow re-entry in the same thread 57 * with the lock already held. */ 58 __block_app_sigs(&oldmask); 59 __tl_lock(); 60 __block_all_sigs(0); 61 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); 62 63 sem_init(&target_sem, 0, 0); 64 sem_init(&caller_sem, 0, 0); 65 66 if (!libc.threads_minus_1 || __syscall(SYS_gettid) != self->tid) 67 goto single_threaded; 68 69 callback = func; 70 context = ctx; 71 72 /* Block even implementation-internal signals, so that nothing 73 * interrupts the SIGSYNCCALL handlers. The main possible source 74 * of trouble is asynchronous cancellation. */ 75 memset(&sa.sa_mask, -1, sizeof sa.sa_mask); 76 __libc_sigaction(SIGSYNCCALL, &sa, 0); 77 78 79 for (td=self->next; td!=self; td=td->next) { 80 target_tid = td->tid; 81 while ((r = -__syscall(SYS_tkill, td->tid, SIGSYNCCALL)) == EAGAIN); 82 if (r) { 83 /* If we failed to signal any thread, nop out the 84 * callback to abort the synccall and just release 85 * any threads already caught. */ 86 callback = func = dummy; 87 break; 88 } 89 sem_wait(&caller_sem); 90 count++; 91 } 92 target_tid = 0; 93 94 /* Serialize execution of callback in caught threads, or just 95 * release them all if synccall is being aborted. */ 96 for (i=0; i<count; i++) { 97 sem_post(&target_sem); 98 sem_wait(&caller_sem); 99 } 100 101 sa.sa_handler = SIG_IGN; 102 __libc_sigaction(SIGSYNCCALL, &sa, 0); 103 104single_threaded: 105 func(ctx); 106 107 /* Only release the caught threads once all threads, including the 108 * caller, have returned from the callback function. */ 109 for (i=0; i<count; i++) 110 sem_post(&target_sem); 111 for (i=0; i<count; i++) 112 sem_wait(&caller_sem); 113 114 sem_destroy(&caller_sem); 115 sem_destroy(&target_sem); 116 117 pthread_setcancelstate(cs, 0); 118 __tl_unlock(); 119 __restore_sigs(&oldmask); 120} 121