xref: /third_party/musl/src/time/timer_create.c (revision 570af302)
1#include <time.h>
2#include <setjmp.h>
3#include <limits.h>
4#include "pthread_impl.h"
5#include "atomic.h"
6
7struct ksigevent {
8	union sigval sigev_value;
9	int sigev_signo;
10	int sigev_notify;
11	int sigev_tid;
12};
13
14struct start_args {
15	volatile int b;
16	struct sigevent *sev;
17};
18
19/*
20* barrier val:
21* 0: init state
22* 2: child thread arrive: when child thread is the first thread to arrive, will wait, otherwise, will wake
23* 3: parent thread arrive: when parent thread is the first thread to arrive, will wait, otherwise, will wake
24* 4: parent thread should wait for the child to finish synchronizing (state: CHILD_DONE)
25* 5: child thread done
26*/
27enum ThreadState {
28	INIT_STATE = 0,
29	CHILD_ARRIVE = 2,
30	PARENT_ARRIVE,
31	PARENT_WAIT,
32	CHILD_DONE,
33};
34
35static void dummy_0()
36{
37}
38weak_alias(dummy_0, __pthread_tsd_run_dtors);
39
40static void cleanup_fromsig(void *p)
41{
42	pthread_t self = __pthread_self();
43	__pthread_tsd_run_dtors();
44#ifdef FEATURE_PTHREAD_CANCEL
45	self->cancel = 0;
46	self->canceldisable = 0;
47	self->cancelasync = 0;
48#endif
49	self->cancelbuf = 0;
50	__reset_tls();
51	longjmp(p, 1);
52}
53
54static void __child_sync(volatile int *barrier)
55{
56	if (a_swap(barrier, CHILD_ARRIVE) == INIT_STATE) {
57		__wait(barrier, 0, CHILD_ARRIVE, 0);
58	} else {
59		__wake(barrier, 1, 0);
60	}
61
62	a_swap(barrier, CHILD_DONE);
63	__wake(barrier, 1, 0);
64}
65
66static void __parent_sync(volatile int *barrier)
67{
68	if (a_swap(barrier, PARENT_ARRIVE) == CHILD_ARRIVE) {
69		__wake(barrier, 1, 0);
70	} else {
71		__wait(barrier, 0, PARENT_ARRIVE, 0);
72	}
73
74	if (a_swap(barrier, PARENT_WAIT) != CHILD_DONE) {
75		__wait(barrier, 0, PARENT_WAIT, 0);
76	}
77}
78
79static void *start(void *arg)
80{
81	pthread_t self = __pthread_self();
82	struct start_args *args = arg;
83	jmp_buf jb;
84
85	void (*notify)(union sigval) = args->sev->sigev_notify_function;
86	union sigval val = args->sev->sigev_value;
87
88	__child_sync(&args->b);
89#ifdef FEATURE_PTHREAD_CANCEL
90	if (self->cancel)
91		return 0;
92#endif
93	for (;;) {
94		siginfo_t si;
95		while (sigwaitinfo(SIGTIMER_SET, &si) < 0);
96		if (si.si_code == SI_TIMER && !setjmp(jb)) {
97#ifndef HWASAN_REMOVE_CLEANUP
98			pthread_cleanup_push(cleanup_fromsig, jb);
99#endif
100			notify(val);
101#ifndef HWASAN_REMOVE_CLEANUP
102			pthread_cleanup_pop(1);
103#endif
104		}
105		if (self->timer_id < 0) break;
106	}
107	__syscall(SYS_timer_delete, self->timer_id & INT_MAX);
108	return 0;
109}
110
111int timer_create(clockid_t clk, struct sigevent *restrict evp, timer_t *restrict res)
112{
113	static volatile int init = 0;
114	pthread_t td;
115	pthread_attr_t attr;
116	int r;
117	struct start_args args;
118	struct ksigevent ksev, *ksevp=0;
119	int timerid;
120	sigset_t set;
121
122	switch (evp ? evp->sigev_notify : SIGEV_SIGNAL) {
123	case SIGEV_NONE:
124	case SIGEV_SIGNAL:
125	case SIGEV_THREAD_ID:
126		if (evp) {
127			ksev.sigev_value = evp->sigev_value;
128			ksev.sigev_signo = evp->sigev_signo;
129			ksev.sigev_notify = evp->sigev_notify;
130			if (evp->sigev_notify == SIGEV_THREAD_ID)
131				ksev.sigev_tid = evp->sigev_notify_thread_id;
132			else
133				ksev.sigev_tid = 0;
134			ksevp = &ksev;
135		}
136		if (syscall(SYS_timer_create, clk, ksevp, &timerid) < 0)
137			return -1;
138		*res = (void *)(intptr_t)timerid;
139		break;
140	case SIGEV_THREAD:
141		if (!init) {
142			struct sigaction sa = { .sa_handler = SIG_DFL };
143			__libc_sigaction(SIGTIMER, &sa, 0);
144			a_store(&init, 1);
145		}
146		if (evp->sigev_notify_attributes)
147			attr = *evp->sigev_notify_attributes;
148		else
149			pthread_attr_init(&attr);
150		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
151		a_store(&args.b, 0);
152		args.sev = evp;
153
154		__block_app_sigs(&set);
155		__syscall(SYS_rt_sigprocmask, SIG_BLOCK, SIGTIMER_SET, 0, _NSIG/8);
156		r = pthread_create(&td, &attr, start, &args);
157		__restore_sigs(&set);
158		if (r) {
159			errno = r;
160			return -1;
161		}
162
163		ksev.sigev_value.sival_ptr = 0;
164		ksev.sigev_signo = SIGTIMER;
165		ksev.sigev_notify = SIGEV_THREAD_ID;
166		ksev.sigev_tid = td->tid;
167		if (syscall(SYS_timer_create, clk, &ksev, &timerid) < 0) {
168			timerid = -1;
169#ifdef FEATURE_PTHREAD_CANCEL
170			td->cancel = 1;
171#endif
172		}
173		td->timer_id = timerid;
174		__parent_sync(&args.b);
175		if (timerid < 0) return -1;
176		*res = (void *)(INTPTR_MIN | (uintptr_t)td>>1);
177		break;
178	default:
179		errno = EINVAL;
180		return -1;
181	}
182
183	return 0;
184}
185