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