1#include <time.h> 2#include <setjmp.h> 3#include "pthread_impl.h" 4 5struct ksigevent { 6 union sigval sigev_value; 7 int sigev_signo; 8 int sigev_notify; 9 int sigev_tid; 10}; 11 12struct start_args { 13 pthread_barrier_t b; 14 struct sigevent *sev; 15}; 16 17static void dummy_1(pthread_t self) 18{ 19} 20weak_alias(dummy_1, __pthread_tsd_run_dtors); 21 22void __reset_tls(); 23 24static void cleanup_fromsig(void *p) 25{ 26 pthread_t self = __pthread_self(); 27 __pthread_tsd_run_dtors(self); 28 self->cancel = 0; 29 self->cancelbuf = 0; 30 self->canceldisable = 0; 31 self->cancelasync = 0; 32 self->unblock_cancel = 0; 33 __reset_tls(); 34 longjmp(p, 1); 35} 36 37static void timer_handler(int sig, siginfo_t *si, void *ctx) 38{ 39 pthread_t self = __pthread_self(); 40 jmp_buf jb; 41 void (*notify)(union sigval) = (void (*)(union sigval))self->start; 42 union sigval val = { .sival_ptr = self->start_arg }; 43 44 if (!setjmp(jb) && si->si_code == SI_TIMER) { 45 pthread_cleanup_push(cleanup_fromsig, jb); 46 notify(val); 47 pthread_cleanup_pop(1); 48 } 49} 50 51static void install_handler() 52{ 53 struct sigaction sa = { 54 .sa_sigaction = timer_handler, 55 .sa_flags = SA_SIGINFO | SA_RESTART 56 }; 57 __libc_sigaction(SIGTIMER, &sa, 0); 58} 59 60static void *start(void *arg) 61{ 62 pthread_t self = __pthread_self(); 63 struct start_args *args = arg; 64 int id; 65 66 /* Reuse no-longer-needed thread structure fields to avoid 67 * needing the timer address in the signal handler. */ 68 self->start = (void *(*)(void *))args->sev->sigev_notify_function; 69 self->start_arg = args->sev->sigev_value.sival_ptr; 70 71 pthread_barrier_wait(&args->b); 72 if ((id = self->timer_id) >= 0) { 73 __syscall(SYS_rt_sigprocmask, SIG_UNBLOCK, 74 SIGTIMER_SET, 0, _NSIG/8); 75 __wait(&self->timer_id, 0, id, 1); 76 __syscall(SYS_timer_delete, id); 77 } 78 return 0; 79} 80 81int timer_create(clockid_t clk, struct sigevent *restrict evp, timer_t *restrict res) 82{ 83 static pthread_once_t once = PTHREAD_ONCE_INIT; 84 pthread_t td; 85 pthread_attr_t attr; 86 int r; 87 struct start_args args; 88 struct ksigevent ksev, *ksevp=0; 89 int timerid; 90 sigset_t set; 91 92 switch (evp ? evp->sigev_notify : SIGEV_SIGNAL) { 93 case SIGEV_NONE: 94 case SIGEV_SIGNAL: 95 if (evp) { 96 ksev.sigev_value = evp->sigev_value; 97 ksev.sigev_signo = evp->sigev_signo; 98 ksev.sigev_notify = evp->sigev_notify; 99 ksev.sigev_tid = 0; 100 ksevp = &ksev; 101 } 102 if (syscall(SYS_timer_create, clk, ksevp, &timerid) < 0) 103 return -1; 104 *res = (void *)(intptr_t)timerid; 105 break; 106 case SIGEV_THREAD: 107 pthread_once(&once, install_handler); 108 if (evp->sigev_notify_attributes) 109 attr = *evp->sigev_notify_attributes; 110 else 111 pthread_attr_init(&attr); 112 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 113 pthread_barrier_init(&args.b, 0, 2); 114 args.sev = evp; 115 116 __block_app_sigs(&set); 117 r = pthread_create(&td, &attr, start, &args); 118 __restore_sigs(&set); 119 if (r) { 120 errno = r; 121 return -1; 122 } 123 124 ksev.sigev_value.sival_ptr = 0; 125 ksev.sigev_signo = SIGTIMER; 126 ksev.sigev_notify = 4; /* SIGEV_THREAD_ID */ 127 ksev.sigev_tid = td->tid; 128 if (syscall(SYS_timer_create, clk, &ksev, &timerid) < 0) 129 timerid = -1; 130 td->timer_id = timerid; 131 pthread_barrier_wait(&args.b); 132 if (timerid < 0) return -1; 133 *res = (void *)(INTPTR_MIN | (uintptr_t)td>>1); 134 break; 135 default: 136 errno = EINVAL; 137 return -1; 138 } 139 140 return 0; 141} 142