xref: /freebsd/lib/libthr/thread/thr_sig.c (revision 05c3a5eab406c2e33b6ec7f5e422a93865deebe3)
1bb535300SJeff Roberson /*
2df2cf821SDavid Xu  * Copyright (c) 2005, David Xu <davidxu@freebsd.org>
3bb535300SJeff Roberson  * All rights reserved.
4bb535300SJeff Roberson  *
5bb535300SJeff Roberson  * Redistribution and use in source and binary forms, with or without
6bb535300SJeff Roberson  * modification, are permitted provided that the following conditions
7bb535300SJeff Roberson  * are met:
8bb535300SJeff Roberson  * 1. Redistributions of source code must retain the above copyright
9df2cf821SDavid Xu  *    notice unmodified, this list of conditions, and the following
10df2cf821SDavid Xu  *    disclaimer.
11bb535300SJeff Roberson  * 2. Redistributions in binary form must reproduce the above copyright
12bb535300SJeff Roberson  *    notice, this list of conditions and the following disclaimer in the
13bb535300SJeff Roberson  *    documentation and/or other materials provided with the distribution.
14bb535300SJeff Roberson  *
15df2cf821SDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16df2cf821SDavid Xu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17df2cf821SDavid Xu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18df2cf821SDavid Xu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19df2cf821SDavid Xu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20df2cf821SDavid Xu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21df2cf821SDavid Xu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22df2cf821SDavid Xu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23df2cf821SDavid Xu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24df2cf821SDavid Xu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25bb535300SJeff Roberson  *
26bb535300SJeff Roberson  * $FreeBSD$
27bb535300SJeff Roberson  */
28bb535300SJeff Roberson 
2937a6356bSDavid Xu #include "namespace.h"
30bb535300SJeff Roberson #include <sys/param.h>
31bb535300SJeff Roberson #include <sys/types.h>
32bb535300SJeff Roberson #include <sys/signalvar.h>
33bb535300SJeff Roberson #include <signal.h>
34a091d823SDavid Xu #include <errno.h>
35bb535300SJeff Roberson #include <fcntl.h>
36bb535300SJeff Roberson #include <unistd.h>
37a091d823SDavid Xu #include <string.h>
38bb535300SJeff Roberson #include <pthread.h>
3937a6356bSDavid Xu #include "un-namespace.h"
4089552201SMike Makonnen 
41bb535300SJeff Roberson #include "thr_private.h"
42bb535300SJeff Roberson 
43bb535300SJeff Roberson /* #define DEBUG_SIGNAL */
44bb535300SJeff Roberson #ifdef DEBUG_SIGNAL
45bb535300SJeff Roberson #define DBG_MSG		stdout_debug
46bb535300SJeff Roberson #else
47bb535300SJeff Roberson #define DBG_MSG(x...)
48bb535300SJeff Roberson #endif
49bb535300SJeff Roberson 
5005c3a5eaSDavid Xu extern int	__pause(void);
5105c3a5eaSDavid Xu int	___pause(void);
5205c3a5eaSDavid Xu int	_raise(int);
5337a6356bSDavid Xu int	__sigtimedwait(const sigset_t *set, siginfo_t *info,
5437a6356bSDavid Xu 	const struct timespec * timeout);
5537a6356bSDavid Xu int	__sigwaitinfo(const sigset_t *set, siginfo_t *info);
5637a6356bSDavid Xu int	__sigwait(const sigset_t *set, int *sig);
5737a6356bSDavid Xu 
58a091d823SDavid Xu static void
5937a6356bSDavid Xu sigcancel_handler(int sig __unused,
6037a6356bSDavid Xu 	siginfo_t *info __unused, ucontext_t *ucp __unused)
61a091d823SDavid Xu {
62a091d823SDavid Xu 	struct pthread *curthread = _get_curthread();
63a091d823SDavid Xu 
64bc414752SDavid Xu 	_thr_ast(curthread);
65bc414752SDavid Xu }
66bc414752SDavid Xu 
67bc414752SDavid Xu void
68bc414752SDavid Xu _thr_ast(struct pthread *curthread)
69bc414752SDavid Xu {
70bc414752SDavid Xu 	if (!THR_IN_CRITICAL(curthread)) {
71d448272dSDavid Xu 		if (__predict_false(
72d448272dSDavid Xu 		    SHOULD_ASYNC_CANCEL(curthread->cancelflags)))
73b6b894f6SDavid Xu 			_pthread_testcancel();
74bc414752SDavid Xu 		if (__predict_false((curthread->flags &
75bc414752SDavid Xu 		    (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED))
76bc414752SDavid Xu 			== THR_FLAGS_NEED_SUSPEND))
77a091d823SDavid Xu 			_thr_suspend_check(curthread);
78a091d823SDavid Xu 	}
79a091d823SDavid Xu }
80a091d823SDavid Xu 
81a091d823SDavid Xu void
82a091d823SDavid Xu _thr_suspend_check(struct pthread *curthread)
83a091d823SDavid Xu {
84bc414752SDavid Xu 	umtx_t cycle;
85d448272dSDavid Xu 	int err;
86a091d823SDavid Xu 
87d448272dSDavid Xu 	err = errno;
88bc414752SDavid Xu 	/*
89bc414752SDavid Xu 	 * Blocks SIGCANCEL which other threads must send.
90bc414752SDavid Xu 	 */
91a091d823SDavid Xu 	_thr_signal_block(curthread);
92bc414752SDavid Xu 
93bc414752SDavid Xu 	/*
94bc414752SDavid Xu 	 * Increase critical_count, here we don't use THR_LOCK/UNLOCK
95bc414752SDavid Xu 	 * because we are leaf code, we don't want to recursively call
96bc414752SDavid Xu 	 * ourself.
97bc414752SDavid Xu 	 */
98bc414752SDavid Xu 	curthread->critical_count++;
99bc414752SDavid Xu 	THR_UMTX_LOCK(curthread, &(curthread)->lock);
100bc414752SDavid Xu 	while ((curthread->flags & (THR_FLAGS_NEED_SUSPEND |
101bc414752SDavid Xu 		THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND) {
102bc414752SDavid Xu 		curthread->cycle++;
103a091d823SDavid Xu 		cycle = curthread->cycle;
104bc414752SDavid Xu 
105bc414752SDavid Xu 		/* Wake the thread suspending us. */
106bc414752SDavid Xu 		_thr_umtx_wake(&curthread->cycle, INT_MAX);
107bc414752SDavid Xu 
108bc414752SDavid Xu 		/*
109bc414752SDavid Xu 		 * if we are from pthread_exit, we don't want to
110bc414752SDavid Xu 		 * suspend, just go and die.
111bc414752SDavid Xu 		 */
112bc414752SDavid Xu 		if (curthread->state == PS_DEAD)
113bc414752SDavid Xu 			break;
114bc414752SDavid Xu 		curthread->flags |= THR_FLAGS_SUSPENDED;
115bc414752SDavid Xu 		THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
116a091d823SDavid Xu 		_thr_umtx_wait(&curthread->cycle, cycle, NULL);
117bc414752SDavid Xu 		THR_UMTX_LOCK(curthread, &(curthread)->lock);
118a091d823SDavid Xu 		curthread->flags &= ~THR_FLAGS_SUSPENDED;
119a091d823SDavid Xu 	}
120bc414752SDavid Xu 	THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
121bc414752SDavid Xu 	curthread->critical_count--;
122bc414752SDavid Xu 
123bc414752SDavid Xu 	/*
124bc414752SDavid Xu 	 * Unblocks SIGCANCEL, it is possible a new SIGCANCEL is ready and
125bc414752SDavid Xu 	 * a new signal frame will nest us, this seems a problem because
126bc414752SDavid Xu 	 * stack will grow and overflow, but because kernel will automatically
127bc414752SDavid Xu 	 * mask the SIGCANCEL when delivering the signal, so we at most only
128bc414752SDavid Xu 	 * have one nesting signal frame, this should be fine.
129bc414752SDavid Xu 	 */
130a091d823SDavid Xu 	_thr_signal_unblock(curthread);
131d448272dSDavid Xu 	errno = err;
132a091d823SDavid Xu }
133a091d823SDavid Xu 
134a091d823SDavid Xu void
135a091d823SDavid Xu _thr_signal_init(void)
136a091d823SDavid Xu {
137a091d823SDavid Xu 	struct sigaction act;
138a091d823SDavid Xu 
139a091d823SDavid Xu 	/* Install cancel handler. */
140a091d823SDavid Xu 	SIGEMPTYSET(act.sa_mask);
141a091d823SDavid Xu 	act.sa_flags = SA_SIGINFO | SA_RESTART;
142a091d823SDavid Xu 	act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler;
143a091d823SDavid Xu 	__sys_sigaction(SIGCANCEL, &act, NULL);
144a091d823SDavid Xu }
145a091d823SDavid Xu 
146a091d823SDavid Xu void
147a091d823SDavid Xu _thr_signal_deinit(void)
148a091d823SDavid Xu {
149a091d823SDavid Xu }
150a091d823SDavid Xu 
15105c3a5eaSDavid Xu __weak_reference(___pause, pause);
15205c3a5eaSDavid Xu 
15305c3a5eaSDavid Xu int
15405c3a5eaSDavid Xu ___pause(void)
15505c3a5eaSDavid Xu {
15605c3a5eaSDavid Xu 	struct pthread *curthread = _get_curthread();
15705c3a5eaSDavid Xu 	int	oldcancel;
15805c3a5eaSDavid Xu 	int	ret;
15905c3a5eaSDavid Xu 
16005c3a5eaSDavid Xu 	oldcancel = _thr_cancel_enter(curthread);
16105c3a5eaSDavid Xu 	ret = __pause();
16205c3a5eaSDavid Xu 	_thr_cancel_leave(curthread, oldcancel);
16305c3a5eaSDavid Xu 
16405c3a5eaSDavid Xu 	return ret;
16505c3a5eaSDavid Xu }
16605c3a5eaSDavid Xu 
16705c3a5eaSDavid Xu __weak_reference(_raise, raise);
16805c3a5eaSDavid Xu 
16905c3a5eaSDavid Xu int
17005c3a5eaSDavid Xu _raise(int sig)
17105c3a5eaSDavid Xu {
17205c3a5eaSDavid Xu 	int ret;
17305c3a5eaSDavid Xu 
17405c3a5eaSDavid Xu 	if (!_thr_isthreaded())
17505c3a5eaSDavid Xu 		ret = kill(getpid(), sig);
17605c3a5eaSDavid Xu 	else
17705c3a5eaSDavid Xu 		ret = _thr_send_sig(_get_curthread(), sig);
17805c3a5eaSDavid Xu 	return (ret);
17905c3a5eaSDavid Xu }
18005c3a5eaSDavid Xu 
181a091d823SDavid Xu __weak_reference(_sigaction, sigaction);
182a091d823SDavid Xu 
183a091d823SDavid Xu int
184a091d823SDavid Xu _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
185a091d823SDavid Xu {
186a091d823SDavid Xu 	/* Check if the signal number is out of range: */
187a091d823SDavid Xu 	if (sig < 1 || sig > _SIG_MAXSIG || sig == SIGCANCEL) {
188a091d823SDavid Xu 		/* Return an invalid argument: */
189a091d823SDavid Xu 		errno = EINVAL;
190a091d823SDavid Xu 		return (-1);
191a091d823SDavid Xu 	}
192a091d823SDavid Xu 
193a091d823SDavid Xu 	return __sys_sigaction(sig, act, oact);
194a091d823SDavid Xu }
195a091d823SDavid Xu 
196a091d823SDavid Xu __weak_reference(_sigprocmask, sigprocmask);
197a091d823SDavid Xu 
198a091d823SDavid Xu int
199a091d823SDavid Xu _sigprocmask(int how, const sigset_t *set, sigset_t *oset)
200a091d823SDavid Xu {
201a091d823SDavid Xu 	const sigset_t *p = set;
202a091d823SDavid Xu 	sigset_t newset;
203a091d823SDavid Xu 
204a091d823SDavid Xu 	if (how != SIG_UNBLOCK) {
205a091d823SDavid Xu 		if (set != NULL) {
206a091d823SDavid Xu 			newset = *set;
207a091d823SDavid Xu 			SIGDELSET(newset, SIGCANCEL);
208a091d823SDavid Xu 			p = &newset;
209a091d823SDavid Xu 		}
210a091d823SDavid Xu 	}
211a091d823SDavid Xu 	return (__sys_sigprocmask(how, p, oset));
212a091d823SDavid Xu }
213a091d823SDavid Xu 
214bb535300SJeff Roberson __weak_reference(_pthread_sigmask, pthread_sigmask);
215bb535300SJeff Roberson 
216bb535300SJeff Roberson int
217bb535300SJeff Roberson _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
218bb535300SJeff Roberson {
219a091d823SDavid Xu 	if (_sigprocmask(how, set, oset))
220a091d823SDavid Xu 		return (errno);
221a091d823SDavid Xu 	return (0);
222bb535300SJeff Roberson }
223bb535300SJeff Roberson 
22405c3a5eaSDavid Xu __weak_reference(__sigsuspend, sigsuspend);
225bb535300SJeff Roberson 
226bb535300SJeff Roberson int
227a091d823SDavid Xu _sigsuspend(const sigset_t * set)
228bb535300SJeff Roberson {
22905c3a5eaSDavid Xu 	sigset_t newset;
23005c3a5eaSDavid Xu 	const sigset_t *pset;
23105c3a5eaSDavid Xu 	int ret;
23205c3a5eaSDavid Xu 
23305c3a5eaSDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
23405c3a5eaSDavid Xu 		newset = *set;
23505c3a5eaSDavid Xu 		SIGDELSET(newset, SIGCANCEL);
23605c3a5eaSDavid Xu 		pset = &newset;
23705c3a5eaSDavid Xu 	} else
23805c3a5eaSDavid Xu 		pset = set;
23905c3a5eaSDavid Xu 
24005c3a5eaSDavid Xu 	ret = __sys_sigsuspend(pset);
24105c3a5eaSDavid Xu 
24205c3a5eaSDavid Xu 	return (ret);
24305c3a5eaSDavid Xu }
24405c3a5eaSDavid Xu 
24505c3a5eaSDavid Xu int
24605c3a5eaSDavid Xu __sigsuspend(const sigset_t * set)
24705c3a5eaSDavid Xu {
248a091d823SDavid Xu 	struct pthread *curthread = _get_curthread();
249a091d823SDavid Xu 	sigset_t newset;
250a091d823SDavid Xu 	const sigset_t *pset;
251a091d823SDavid Xu 	int oldcancel;
252a091d823SDavid Xu 	int ret;
253bb535300SJeff Roberson 
254a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
255a091d823SDavid Xu 		newset = *set;
256a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
257a091d823SDavid Xu 		pset = &newset;
258a091d823SDavid Xu 	} else
259a091d823SDavid Xu 		pset = set;
2600ad70ba9SMike Makonnen 
261a091d823SDavid Xu 	oldcancel = _thr_cancel_enter(curthread);
262a091d823SDavid Xu 	ret = __sys_sigsuspend(pset);
263a091d823SDavid Xu 	_thr_cancel_leave(curthread, oldcancel);
2640ad70ba9SMike Makonnen 
265a091d823SDavid Xu 	return (ret);
266a091d823SDavid Xu }
267a091d823SDavid Xu 
268a091d823SDavid Xu __weak_reference(__sigwait, sigwait);
269a091d823SDavid Xu __weak_reference(__sigtimedwait, sigtimedwait);
270a091d823SDavid Xu __weak_reference(__sigwaitinfo, sigwaitinfo);
271a091d823SDavid Xu 
272a091d823SDavid Xu int
27305c3a5eaSDavid Xu _sigtimedwait(const sigset_t *set, siginfo_t *info,
27405c3a5eaSDavid Xu 	const struct timespec * timeout)
27505c3a5eaSDavid Xu {
27605c3a5eaSDavid Xu 	sigset_t newset;
27705c3a5eaSDavid Xu 	const sigset_t *pset;
27805c3a5eaSDavid Xu 	int ret;
27905c3a5eaSDavid Xu 
28005c3a5eaSDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
28105c3a5eaSDavid Xu 		newset = *set;
28205c3a5eaSDavid Xu 		SIGDELSET(newset, SIGCANCEL);
28305c3a5eaSDavid Xu 		pset = &newset;
28405c3a5eaSDavid Xu 	} else
28505c3a5eaSDavid Xu 		pset = set;
28605c3a5eaSDavid Xu 	ret = __sys_sigtimedwait(pset, info, timeout);
28705c3a5eaSDavid Xu 	return (ret);
28805c3a5eaSDavid Xu }
28905c3a5eaSDavid Xu 
29005c3a5eaSDavid Xu int
291a091d823SDavid Xu __sigtimedwait(const sigset_t *set, siginfo_t *info,
292a091d823SDavid Xu 	const struct timespec * timeout)
293a091d823SDavid Xu {
294a091d823SDavid Xu 	struct pthread	*curthread = _get_curthread();
295a091d823SDavid Xu 	sigset_t newset;
296a091d823SDavid Xu 	const sigset_t *pset;
297a091d823SDavid Xu 	int oldcancel;
298a091d823SDavid Xu 	int ret;
299a091d823SDavid Xu 
300a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
301a091d823SDavid Xu 		newset = *set;
302a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
303a091d823SDavid Xu 		pset = &newset;
304a091d823SDavid Xu 	} else
305a091d823SDavid Xu 		pset = set;
306a091d823SDavid Xu 	oldcancel = _thr_cancel_enter(curthread);
307a091d823SDavid Xu 	ret = __sys_sigtimedwait(pset, info, timeout);
308a091d823SDavid Xu 	_thr_cancel_leave(curthread, oldcancel);
309a091d823SDavid Xu 	return (ret);
310a091d823SDavid Xu }
311a091d823SDavid Xu 
312a091d823SDavid Xu int
31305c3a5eaSDavid Xu _sigwaitinfo(const sigset_t *set, siginfo_t *info)
31405c3a5eaSDavid Xu {
31505c3a5eaSDavid Xu 	sigset_t newset;
31605c3a5eaSDavid Xu 	const sigset_t *pset;
31705c3a5eaSDavid Xu 	int ret;
31805c3a5eaSDavid Xu 
31905c3a5eaSDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
32005c3a5eaSDavid Xu 		newset = *set;
32105c3a5eaSDavid Xu 		SIGDELSET(newset, SIGCANCEL);
32205c3a5eaSDavid Xu 		pset = &newset;
32305c3a5eaSDavid Xu 	} else
32405c3a5eaSDavid Xu 		pset = set;
32505c3a5eaSDavid Xu 
32605c3a5eaSDavid Xu 	ret = __sys_sigwaitinfo(pset, info);
32705c3a5eaSDavid Xu 	return (ret);
32805c3a5eaSDavid Xu }
32905c3a5eaSDavid Xu 
33005c3a5eaSDavid Xu int
331a091d823SDavid Xu __sigwaitinfo(const sigset_t *set, siginfo_t *info)
332a091d823SDavid Xu {
333a091d823SDavid Xu 	struct pthread	*curthread = _get_curthread();
334a091d823SDavid Xu 	sigset_t newset;
335a091d823SDavid Xu 	const sigset_t *pset;
336a091d823SDavid Xu 	int oldcancel;
337a091d823SDavid Xu 	int ret;
338a091d823SDavid Xu 
339a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
340a091d823SDavid Xu 		newset = *set;
341a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
342a091d823SDavid Xu 		pset = &newset;
343a091d823SDavid Xu 	} else
344a091d823SDavid Xu 		pset = set;
345a091d823SDavid Xu 
346a091d823SDavid Xu 	oldcancel = _thr_cancel_enter(curthread);
347a091d823SDavid Xu 	ret = __sys_sigwaitinfo(pset, info);
348a091d823SDavid Xu 	_thr_cancel_leave(curthread, oldcancel);
349a091d823SDavid Xu 	return (ret);
350a091d823SDavid Xu }
351a091d823SDavid Xu 
352a091d823SDavid Xu int
35305c3a5eaSDavid Xu _sigwait(const sigset_t *set, int *sig)
35405c3a5eaSDavid Xu {
35505c3a5eaSDavid Xu 	sigset_t newset;
35605c3a5eaSDavid Xu 	const sigset_t *pset;
35705c3a5eaSDavid Xu 	int ret;
35805c3a5eaSDavid Xu 
35905c3a5eaSDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
36005c3a5eaSDavid Xu 		newset = *set;
36105c3a5eaSDavid Xu 		SIGDELSET(newset, SIGCANCEL);
36205c3a5eaSDavid Xu 		pset = &newset;
36305c3a5eaSDavid Xu 	} else
36405c3a5eaSDavid Xu 		pset = set;
36505c3a5eaSDavid Xu 
36605c3a5eaSDavid Xu 	ret = __sys_sigwait(pset, sig);
36705c3a5eaSDavid Xu 	return (ret);
36805c3a5eaSDavid Xu }
36905c3a5eaSDavid Xu 
37005c3a5eaSDavid Xu int
371a091d823SDavid Xu __sigwait(const sigset_t *set, int *sig)
372a091d823SDavid Xu {
373a091d823SDavid Xu 	struct pthread	*curthread = _get_curthread();
374a091d823SDavid Xu 	sigset_t newset;
375a091d823SDavid Xu 	const sigset_t *pset;
376a091d823SDavid Xu 	int oldcancel;
377a091d823SDavid Xu 	int ret;
378a091d823SDavid Xu 
379a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
380a091d823SDavid Xu 		newset = *set;
381a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
382a091d823SDavid Xu 		pset = &newset;
383a091d823SDavid Xu 	} else
384a091d823SDavid Xu 		pset = set;
385a091d823SDavid Xu 
386a091d823SDavid Xu 	oldcancel = _thr_cancel_enter(curthread);
387a091d823SDavid Xu 	ret = __sys_sigwait(pset, sig);
388a091d823SDavid Xu 	_thr_cancel_leave(curthread, oldcancel);
389a091d823SDavid Xu 	return (ret);
390bb535300SJeff Roberson }
391