• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <time.h>
2 #include <setjmp.h>
3 #include <limits.h>
4 #include "pthread_impl.h"
5 
6 struct ksigevent {
7 	union sigval sigev_value;
8 	int sigev_signo;
9 	int sigev_notify;
10 	int sigev_tid;
11 };
12 
13 struct start_args {
14 	pthread_barrier_t b;
15 	struct sigevent *sev;
16 };
17 
dummy_0()18 static void dummy_0()
19 {
20 }
21 weak_alias(dummy_0, __pthread_tsd_run_dtors);
22 
cleanup_fromsig(void * p)23 static void cleanup_fromsig(void *p)
24 {
25 	pthread_t self = __pthread_self();
26 	__pthread_tsd_run_dtors();
27 	self->cancel = 0;
28 	self->cancelbuf = 0;
29 	self->canceldisable = 0;
30 	self->cancelasync = 0;
31 	__reset_tls();
32 	longjmp(p, 1);
33 }
34 
timer_handler(int sig,siginfo_t * si,void * ctx)35 static void timer_handler(int sig, siginfo_t *si, void *ctx)
36 {
37 }
38 
install_handler()39 static void install_handler()
40 {
41 	struct sigaction sa = {
42 		.sa_sigaction = timer_handler,
43 		.sa_flags = SA_SIGINFO | SA_RESTART
44 	};
45 	__libc_sigaction(SIGTIMER, &sa, 0);
46 }
47 
start(void * arg)48 static void *start(void *arg)
49 {
50 	pthread_t self = __pthread_self();
51 	struct start_args *args = arg;
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, self->timer_id & INT_MAX);
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