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