xref: /freebsd/lib/libthr/thread/thr_sig.c (revision f08e1bf68286986422c2c96bd7b594898afb7fa7)
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)) {
71f08e1bf6SDavid Xu 		_thr_testcancel(curthread);
72bc414752SDavid Xu 		if (__predict_false((curthread->flags &
73bc414752SDavid Xu 		    (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED))
74bc414752SDavid Xu 			== THR_FLAGS_NEED_SUSPEND))
75a091d823SDavid Xu 			_thr_suspend_check(curthread);
76a091d823SDavid Xu 	}
77a091d823SDavid Xu }
78a091d823SDavid Xu 
79a091d823SDavid Xu void
80a091d823SDavid Xu _thr_suspend_check(struct pthread *curthread)
81a091d823SDavid Xu {
82bc414752SDavid Xu 	umtx_t cycle;
83d448272dSDavid Xu 	int err;
84a091d823SDavid Xu 
85d448272dSDavid Xu 	err = errno;
86bc414752SDavid Xu 	/*
87bc414752SDavid Xu 	 * Blocks SIGCANCEL which other threads must send.
88bc414752SDavid Xu 	 */
89a091d823SDavid Xu 	_thr_signal_block(curthread);
90bc414752SDavid Xu 
91bc414752SDavid Xu 	/*
92bc414752SDavid Xu 	 * Increase critical_count, here we don't use THR_LOCK/UNLOCK
93bc414752SDavid Xu 	 * because we are leaf code, we don't want to recursively call
94bc414752SDavid Xu 	 * ourself.
95bc414752SDavid Xu 	 */
96bc414752SDavid Xu 	curthread->critical_count++;
97bddd24cdSDavid Xu 	THR_UMUTEX_LOCK(curthread, &(curthread)->lock);
98bc414752SDavid Xu 	while ((curthread->flags & (THR_FLAGS_NEED_SUSPEND |
99bc414752SDavid Xu 		THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND) {
100bc414752SDavid Xu 		curthread->cycle++;
101a091d823SDavid Xu 		cycle = curthread->cycle;
102bc414752SDavid Xu 
103bc414752SDavid Xu 		/* Wake the thread suspending us. */
104bc414752SDavid Xu 		_thr_umtx_wake(&curthread->cycle, INT_MAX);
105bc414752SDavid Xu 
106bc414752SDavid Xu 		/*
107bc414752SDavid Xu 		 * if we are from pthread_exit, we don't want to
108bc414752SDavid Xu 		 * suspend, just go and die.
109bc414752SDavid Xu 		 */
110bc414752SDavid Xu 		if (curthread->state == PS_DEAD)
111bc414752SDavid Xu 			break;
112bc414752SDavid Xu 		curthread->flags |= THR_FLAGS_SUSPENDED;
113bddd24cdSDavid Xu 		THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock);
114a091d823SDavid Xu 		_thr_umtx_wait(&curthread->cycle, cycle, NULL);
115bddd24cdSDavid Xu 		THR_UMUTEX_LOCK(curthread, &(curthread)->lock);
116a091d823SDavid Xu 		curthread->flags &= ~THR_FLAGS_SUSPENDED;
117a091d823SDavid Xu 	}
118bddd24cdSDavid Xu 	THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock);
119bc414752SDavid Xu 	curthread->critical_count--;
120bc414752SDavid Xu 
121bc414752SDavid Xu 	/*
122bc414752SDavid Xu 	 * Unblocks SIGCANCEL, it is possible a new SIGCANCEL is ready and
123bc414752SDavid Xu 	 * a new signal frame will nest us, this seems a problem because
124bc414752SDavid Xu 	 * stack will grow and overflow, but because kernel will automatically
125bc414752SDavid Xu 	 * mask the SIGCANCEL when delivering the signal, so we at most only
126bc414752SDavid Xu 	 * have one nesting signal frame, this should be fine.
127bc414752SDavid Xu 	 */
128a091d823SDavid Xu 	_thr_signal_unblock(curthread);
129d448272dSDavid Xu 	errno = err;
130a091d823SDavid Xu }
131a091d823SDavid Xu 
132a091d823SDavid Xu void
133a091d823SDavid Xu _thr_signal_init(void)
134a091d823SDavid Xu {
135a091d823SDavid Xu 	struct sigaction act;
136a091d823SDavid Xu 
137a091d823SDavid Xu 	/* Install cancel handler. */
138a091d823SDavid Xu 	SIGEMPTYSET(act.sa_mask);
139a091d823SDavid Xu 	act.sa_flags = SA_SIGINFO | SA_RESTART;
140a091d823SDavid Xu 	act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler;
141a091d823SDavid Xu 	__sys_sigaction(SIGCANCEL, &act, NULL);
142a091d823SDavid Xu }
143a091d823SDavid Xu 
144a091d823SDavid Xu void
145a091d823SDavid Xu _thr_signal_deinit(void)
146a091d823SDavid Xu {
147a091d823SDavid Xu }
148a091d823SDavid Xu 
14905c3a5eaSDavid Xu __weak_reference(___pause, pause);
15005c3a5eaSDavid Xu 
15105c3a5eaSDavid Xu int
15205c3a5eaSDavid Xu ___pause(void)
15305c3a5eaSDavid Xu {
15405c3a5eaSDavid Xu 	struct pthread *curthread = _get_curthread();
15505c3a5eaSDavid Xu 	int	ret;
15605c3a5eaSDavid Xu 
157f08e1bf6SDavid Xu 	_thr_cancel_enter(curthread);
15805c3a5eaSDavid Xu 	ret = __pause();
159f08e1bf6SDavid Xu 	_thr_cancel_leave(curthread);
16005c3a5eaSDavid Xu 
16105c3a5eaSDavid Xu 	return ret;
16205c3a5eaSDavid Xu }
16305c3a5eaSDavid Xu 
16405c3a5eaSDavid Xu __weak_reference(_raise, raise);
16505c3a5eaSDavid Xu 
16605c3a5eaSDavid Xu int
16705c3a5eaSDavid Xu _raise(int sig)
16805c3a5eaSDavid Xu {
16905c3a5eaSDavid Xu 	int ret;
17005c3a5eaSDavid Xu 
17105c3a5eaSDavid Xu 	if (!_thr_isthreaded())
17205c3a5eaSDavid Xu 		ret = kill(getpid(), sig);
17305c3a5eaSDavid Xu 	else
17405c3a5eaSDavid Xu 		ret = _thr_send_sig(_get_curthread(), sig);
17505c3a5eaSDavid Xu 	return (ret);
17605c3a5eaSDavid Xu }
17705c3a5eaSDavid Xu 
178a091d823SDavid Xu __weak_reference(_sigaction, sigaction);
179a091d823SDavid Xu 
180a091d823SDavid Xu int
181a091d823SDavid Xu _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
182a091d823SDavid Xu {
183a091d823SDavid Xu 	/* Check if the signal number is out of range: */
184a091d823SDavid Xu 	if (sig < 1 || sig > _SIG_MAXSIG || sig == SIGCANCEL) {
185a091d823SDavid Xu 		/* Return an invalid argument: */
186a091d823SDavid Xu 		errno = EINVAL;
187a091d823SDavid Xu 		return (-1);
188a091d823SDavid Xu 	}
189a091d823SDavid Xu 
190a091d823SDavid Xu 	return __sys_sigaction(sig, act, oact);
191a091d823SDavid Xu }
192a091d823SDavid Xu 
193a091d823SDavid Xu __weak_reference(_sigprocmask, sigprocmask);
194a091d823SDavid Xu 
195a091d823SDavid Xu int
196a091d823SDavid Xu _sigprocmask(int how, const sigset_t *set, sigset_t *oset)
197a091d823SDavid Xu {
198a091d823SDavid Xu 	const sigset_t *p = set;
199a091d823SDavid Xu 	sigset_t newset;
200a091d823SDavid Xu 
201a091d823SDavid Xu 	if (how != SIG_UNBLOCK) {
202a091d823SDavid Xu 		if (set != NULL) {
203a091d823SDavid Xu 			newset = *set;
204a091d823SDavid Xu 			SIGDELSET(newset, SIGCANCEL);
205a091d823SDavid Xu 			p = &newset;
206a091d823SDavid Xu 		}
207a091d823SDavid Xu 	}
208a091d823SDavid Xu 	return (__sys_sigprocmask(how, p, oset));
209a091d823SDavid Xu }
210a091d823SDavid Xu 
211bb535300SJeff Roberson __weak_reference(_pthread_sigmask, pthread_sigmask);
212bb535300SJeff Roberson 
213bb535300SJeff Roberson int
214bb535300SJeff Roberson _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
215bb535300SJeff Roberson {
216a091d823SDavid Xu 	if (_sigprocmask(how, set, oset))
217a091d823SDavid Xu 		return (errno);
218a091d823SDavid Xu 	return (0);
219bb535300SJeff Roberson }
220bb535300SJeff Roberson 
22105c3a5eaSDavid Xu __weak_reference(__sigsuspend, sigsuspend);
222bb535300SJeff Roberson 
223bb535300SJeff Roberson int
224a091d823SDavid Xu _sigsuspend(const sigset_t * set)
225bb535300SJeff Roberson {
22605c3a5eaSDavid Xu 	sigset_t newset;
22705c3a5eaSDavid Xu 	const sigset_t *pset;
22805c3a5eaSDavid Xu 	int ret;
22905c3a5eaSDavid Xu 
23005c3a5eaSDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
23105c3a5eaSDavid Xu 		newset = *set;
23205c3a5eaSDavid Xu 		SIGDELSET(newset, SIGCANCEL);
23305c3a5eaSDavid Xu 		pset = &newset;
23405c3a5eaSDavid Xu 	} else
23505c3a5eaSDavid Xu 		pset = set;
23605c3a5eaSDavid Xu 
23705c3a5eaSDavid Xu 	ret = __sys_sigsuspend(pset);
23805c3a5eaSDavid Xu 
23905c3a5eaSDavid Xu 	return (ret);
24005c3a5eaSDavid Xu }
24105c3a5eaSDavid Xu 
24205c3a5eaSDavid Xu int
24305c3a5eaSDavid Xu __sigsuspend(const sigset_t * set)
24405c3a5eaSDavid Xu {
245a091d823SDavid Xu 	struct pthread *curthread = _get_curthread();
246a091d823SDavid Xu 	sigset_t newset;
247a091d823SDavid Xu 	const sigset_t *pset;
248a091d823SDavid Xu 	int ret;
249bb535300SJeff Roberson 
250a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
251a091d823SDavid Xu 		newset = *set;
252a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
253a091d823SDavid Xu 		pset = &newset;
254a091d823SDavid Xu 	} else
255a091d823SDavid Xu 		pset = set;
2560ad70ba9SMike Makonnen 
257f08e1bf6SDavid Xu 	_thr_cancel_enter(curthread);
258a091d823SDavid Xu 	ret = __sys_sigsuspend(pset);
259f08e1bf6SDavid Xu 	_thr_cancel_leave(curthread);
2600ad70ba9SMike Makonnen 
261a091d823SDavid Xu 	return (ret);
262a091d823SDavid Xu }
263a091d823SDavid Xu 
264a091d823SDavid Xu __weak_reference(__sigwait, sigwait);
265a091d823SDavid Xu __weak_reference(__sigtimedwait, sigtimedwait);
266a091d823SDavid Xu __weak_reference(__sigwaitinfo, sigwaitinfo);
267a091d823SDavid Xu 
268a091d823SDavid Xu int
26905c3a5eaSDavid Xu _sigtimedwait(const sigset_t *set, siginfo_t *info,
27005c3a5eaSDavid Xu 	const struct timespec * timeout)
27105c3a5eaSDavid Xu {
27205c3a5eaSDavid Xu 	sigset_t newset;
27305c3a5eaSDavid Xu 	const sigset_t *pset;
27405c3a5eaSDavid Xu 	int ret;
27505c3a5eaSDavid Xu 
27605c3a5eaSDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
27705c3a5eaSDavid Xu 		newset = *set;
27805c3a5eaSDavid Xu 		SIGDELSET(newset, SIGCANCEL);
27905c3a5eaSDavid Xu 		pset = &newset;
28005c3a5eaSDavid Xu 	} else
28105c3a5eaSDavid Xu 		pset = set;
28205c3a5eaSDavid Xu 	ret = __sys_sigtimedwait(pset, info, timeout);
28305c3a5eaSDavid Xu 	return (ret);
28405c3a5eaSDavid Xu }
28505c3a5eaSDavid Xu 
28605c3a5eaSDavid Xu int
287a091d823SDavid Xu __sigtimedwait(const sigset_t *set, siginfo_t *info,
288a091d823SDavid Xu 	const struct timespec * timeout)
289a091d823SDavid Xu {
290a091d823SDavid Xu 	struct pthread	*curthread = _get_curthread();
291a091d823SDavid Xu 	sigset_t newset;
292a091d823SDavid Xu 	const sigset_t *pset;
293a091d823SDavid Xu 	int ret;
294a091d823SDavid Xu 
295a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
296a091d823SDavid Xu 		newset = *set;
297a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
298a091d823SDavid Xu 		pset = &newset;
299a091d823SDavid Xu 	} else
300a091d823SDavid Xu 		pset = set;
301f08e1bf6SDavid Xu 	_thr_cancel_enter(curthread);
302a091d823SDavid Xu 	ret = __sys_sigtimedwait(pset, info, timeout);
303f08e1bf6SDavid Xu 	_thr_cancel_leave(curthread);
304a091d823SDavid Xu 	return (ret);
305a091d823SDavid Xu }
306a091d823SDavid Xu 
307a091d823SDavid Xu int
30805c3a5eaSDavid Xu _sigwaitinfo(const sigset_t *set, siginfo_t *info)
30905c3a5eaSDavid Xu {
31005c3a5eaSDavid Xu 	sigset_t newset;
31105c3a5eaSDavid Xu 	const sigset_t *pset;
31205c3a5eaSDavid Xu 	int ret;
31305c3a5eaSDavid Xu 
31405c3a5eaSDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
31505c3a5eaSDavid Xu 		newset = *set;
31605c3a5eaSDavid Xu 		SIGDELSET(newset, SIGCANCEL);
31705c3a5eaSDavid Xu 		pset = &newset;
31805c3a5eaSDavid Xu 	} else
31905c3a5eaSDavid Xu 		pset = set;
32005c3a5eaSDavid Xu 
32105c3a5eaSDavid Xu 	ret = __sys_sigwaitinfo(pset, info);
32205c3a5eaSDavid Xu 	return (ret);
32305c3a5eaSDavid Xu }
32405c3a5eaSDavid Xu 
32505c3a5eaSDavid Xu int
326a091d823SDavid Xu __sigwaitinfo(const sigset_t *set, siginfo_t *info)
327a091d823SDavid Xu {
328a091d823SDavid Xu 	struct pthread	*curthread = _get_curthread();
329a091d823SDavid Xu 	sigset_t newset;
330a091d823SDavid Xu 	const sigset_t *pset;
331a091d823SDavid Xu 	int ret;
332a091d823SDavid Xu 
333a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
334a091d823SDavid Xu 		newset = *set;
335a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
336a091d823SDavid Xu 		pset = &newset;
337a091d823SDavid Xu 	} else
338a091d823SDavid Xu 		pset = set;
339a091d823SDavid Xu 
340f08e1bf6SDavid Xu 	_thr_cancel_enter(curthread);
341a091d823SDavid Xu 	ret = __sys_sigwaitinfo(pset, info);
342f08e1bf6SDavid Xu 	_thr_cancel_leave(curthread);
343a091d823SDavid Xu 	return (ret);
344a091d823SDavid Xu }
345a091d823SDavid Xu 
346a091d823SDavid Xu int
34705c3a5eaSDavid Xu _sigwait(const sigset_t *set, int *sig)
34805c3a5eaSDavid Xu {
34905c3a5eaSDavid Xu 	sigset_t newset;
35005c3a5eaSDavid Xu 	const sigset_t *pset;
35105c3a5eaSDavid Xu 	int ret;
35205c3a5eaSDavid Xu 
35305c3a5eaSDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
35405c3a5eaSDavid Xu 		newset = *set;
35505c3a5eaSDavid Xu 		SIGDELSET(newset, SIGCANCEL);
35605c3a5eaSDavid Xu 		pset = &newset;
35705c3a5eaSDavid Xu 	} else
35805c3a5eaSDavid Xu 		pset = set;
35905c3a5eaSDavid Xu 
36005c3a5eaSDavid Xu 	ret = __sys_sigwait(pset, sig);
36105c3a5eaSDavid Xu 	return (ret);
36205c3a5eaSDavid Xu }
36305c3a5eaSDavid Xu 
36405c3a5eaSDavid Xu int
365a091d823SDavid Xu __sigwait(const sigset_t *set, int *sig)
366a091d823SDavid Xu {
367a091d823SDavid Xu 	struct pthread	*curthread = _get_curthread();
368a091d823SDavid Xu 	sigset_t newset;
369a091d823SDavid Xu 	const sigset_t *pset;
370a091d823SDavid Xu 	int ret;
371a091d823SDavid Xu 
372a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
373a091d823SDavid Xu 		newset = *set;
374a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
375a091d823SDavid Xu 		pset = &newset;
376a091d823SDavid Xu 	} else
377a091d823SDavid Xu 		pset = set;
378a091d823SDavid Xu 
379f08e1bf6SDavid Xu 	_thr_cancel_enter(curthread);
380a091d823SDavid Xu 	ret = __sys_sigwait(pset, sig);
381f08e1bf6SDavid Xu 	_thr_cancel_leave(curthread);
382a091d823SDavid Xu 	return (ret);
383bb535300SJeff Roberson }
384