xref: /freebsd/lib/libthr/thread/thr_sig.c (revision 922d56f9ded0217f1bbbdd6a9b9e01bb2ae25be6)
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);
55922d56f9SDavid Xu int	_sigtimedwait(const sigset_t *set, siginfo_t *info,
56922d56f9SDavid Xu 	const struct timespec * timeout);
5737a6356bSDavid Xu int	__sigwaitinfo(const sigset_t *set, siginfo_t *info);
58922d56f9SDavid Xu int	_sigwaitinfo(const sigset_t *set, siginfo_t *info);
5937a6356bSDavid Xu int	__sigwait(const sigset_t *set, int *sig);
60922d56f9SDavid Xu int	_sigwait(const sigset_t *set, int *sig);
61922d56f9SDavid Xu int	__sigsuspend(const sigset_t *sigmask);
6237a6356bSDavid Xu 
636fdfcacbSDavid Xu 
64a091d823SDavid Xu static void
6537a6356bSDavid Xu sigcancel_handler(int sig __unused,
6637a6356bSDavid Xu 	siginfo_t *info __unused, ucontext_t *ucp __unused)
67a091d823SDavid Xu {
68a091d823SDavid Xu 	struct pthread *curthread = _get_curthread();
69a091d823SDavid Xu 
70b774466bSDavid Xu 	if (curthread->cancel_defer && curthread->cancel_pending)
712bd2c907SDavid Xu 		thr_wake(curthread->tid);
72bc414752SDavid Xu 	_thr_ast(curthread);
73bc414752SDavid Xu }
74bc414752SDavid Xu 
75bc414752SDavid Xu void
76bc414752SDavid Xu _thr_ast(struct pthread *curthread)
77bc414752SDavid Xu {
78bc414752SDavid Xu 	if (!THR_IN_CRITICAL(curthread)) {
79f08e1bf6SDavid Xu 		_thr_testcancel(curthread);
80bc414752SDavid Xu 		if (__predict_false((curthread->flags &
81bc414752SDavid Xu 		    (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED))
82bc414752SDavid Xu 			== THR_FLAGS_NEED_SUSPEND))
83a091d823SDavid Xu 			_thr_suspend_check(curthread);
84a091d823SDavid Xu 	}
85a091d823SDavid Xu }
86a091d823SDavid Xu 
87a091d823SDavid Xu void
88a091d823SDavid Xu _thr_suspend_check(struct pthread *curthread)
89a091d823SDavid Xu {
906fdfcacbSDavid Xu 	long cycle;
91d448272dSDavid Xu 	int err;
92a091d823SDavid Xu 
93d448272dSDavid Xu 	err = errno;
94bc414752SDavid Xu 	/*
95bc414752SDavid Xu 	 * Blocks SIGCANCEL which other threads must send.
96bc414752SDavid Xu 	 */
97a091d823SDavid Xu 	_thr_signal_block(curthread);
98bc414752SDavid Xu 
99bc414752SDavid Xu 	/*
100bc414752SDavid Xu 	 * Increase critical_count, here we don't use THR_LOCK/UNLOCK
101bc414752SDavid Xu 	 * because we are leaf code, we don't want to recursively call
102bc414752SDavid Xu 	 * ourself.
103bc414752SDavid Xu 	 */
104bc414752SDavid Xu 	curthread->critical_count++;
105bddd24cdSDavid Xu 	THR_UMUTEX_LOCK(curthread, &(curthread)->lock);
106bc414752SDavid Xu 	while ((curthread->flags & (THR_FLAGS_NEED_SUSPEND |
107bc414752SDavid Xu 		THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND) {
108bc414752SDavid Xu 		curthread->cycle++;
109a091d823SDavid Xu 		cycle = curthread->cycle;
110bc414752SDavid Xu 
111bc414752SDavid Xu 		/* Wake the thread suspending us. */
112bc414752SDavid Xu 		_thr_umtx_wake(&curthread->cycle, INT_MAX);
113bc414752SDavid Xu 
114bc414752SDavid Xu 		/*
115bc414752SDavid Xu 		 * if we are from pthread_exit, we don't want to
116bc414752SDavid Xu 		 * suspend, just go and die.
117bc414752SDavid Xu 		 */
118bc414752SDavid Xu 		if (curthread->state == PS_DEAD)
119bc414752SDavid Xu 			break;
120bc414752SDavid Xu 		curthread->flags |= THR_FLAGS_SUSPENDED;
121bddd24cdSDavid Xu 		THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock);
122a091d823SDavid Xu 		_thr_umtx_wait(&curthread->cycle, cycle, NULL);
123bddd24cdSDavid Xu 		THR_UMUTEX_LOCK(curthread, &(curthread)->lock);
124a091d823SDavid Xu 		curthread->flags &= ~THR_FLAGS_SUSPENDED;
125a091d823SDavid Xu 	}
126bddd24cdSDavid Xu 	THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock);
127bc414752SDavid Xu 	curthread->critical_count--;
128bc414752SDavid Xu 
129bc414752SDavid Xu 	/*
130bc414752SDavid Xu 	 * Unblocks SIGCANCEL, it is possible a new SIGCANCEL is ready and
131bc414752SDavid Xu 	 * a new signal frame will nest us, this seems a problem because
132bc414752SDavid Xu 	 * stack will grow and overflow, but because kernel will automatically
133bc414752SDavid Xu 	 * mask the SIGCANCEL when delivering the signal, so we at most only
134bc414752SDavid Xu 	 * have one nesting signal frame, this should be fine.
135bc414752SDavid Xu 	 */
136a091d823SDavid Xu 	_thr_signal_unblock(curthread);
137d448272dSDavid Xu 	errno = err;
138a091d823SDavid Xu }
139a091d823SDavid Xu 
140a091d823SDavid Xu void
141a091d823SDavid Xu _thr_signal_init(void)
142a091d823SDavid Xu {
143a091d823SDavid Xu 	struct sigaction act;
144a091d823SDavid Xu 
145a091d823SDavid Xu 	/* Install cancel handler. */
146a091d823SDavid Xu 	SIGEMPTYSET(act.sa_mask);
147a091d823SDavid Xu 	act.sa_flags = SA_SIGINFO | SA_RESTART;
148a091d823SDavid Xu 	act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler;
149a091d823SDavid Xu 	__sys_sigaction(SIGCANCEL, &act, NULL);
150a091d823SDavid Xu }
151a091d823SDavid Xu 
152a091d823SDavid Xu void
153a091d823SDavid Xu _thr_signal_deinit(void)
154a091d823SDavid Xu {
155a091d823SDavid Xu }
156a091d823SDavid Xu 
15705c3a5eaSDavid Xu __weak_reference(___pause, pause);
15805c3a5eaSDavid Xu 
15905c3a5eaSDavid Xu int
16005c3a5eaSDavid Xu ___pause(void)
16105c3a5eaSDavid Xu {
16205c3a5eaSDavid Xu 	struct pthread *curthread = _get_curthread();
16305c3a5eaSDavid Xu 	int	ret;
16405c3a5eaSDavid Xu 
165f08e1bf6SDavid Xu 	_thr_cancel_enter(curthread);
16605c3a5eaSDavid Xu 	ret = __pause();
167f08e1bf6SDavid Xu 	_thr_cancel_leave(curthread);
16805c3a5eaSDavid Xu 
16905c3a5eaSDavid Xu 	return ret;
17005c3a5eaSDavid Xu }
17105c3a5eaSDavid Xu 
17205c3a5eaSDavid Xu __weak_reference(_raise, raise);
17305c3a5eaSDavid Xu 
17405c3a5eaSDavid Xu int
17505c3a5eaSDavid Xu _raise(int sig)
17605c3a5eaSDavid Xu {
17705c3a5eaSDavid Xu 	int ret;
17805c3a5eaSDavid Xu 
17905c3a5eaSDavid Xu 	if (!_thr_isthreaded())
18005c3a5eaSDavid Xu 		ret = kill(getpid(), sig);
18105c3a5eaSDavid Xu 	else
18205c3a5eaSDavid Xu 		ret = _thr_send_sig(_get_curthread(), sig);
18305c3a5eaSDavid Xu 	return (ret);
18405c3a5eaSDavid Xu }
18505c3a5eaSDavid Xu 
186a091d823SDavid Xu __weak_reference(_sigaction, sigaction);
187a091d823SDavid Xu 
188a091d823SDavid Xu int
189a091d823SDavid Xu _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
190a091d823SDavid Xu {
191a091d823SDavid Xu 	/* Check if the signal number is out of range: */
192a091d823SDavid Xu 	if (sig < 1 || sig > _SIG_MAXSIG || sig == SIGCANCEL) {
193a091d823SDavid Xu 		/* Return an invalid argument: */
194a091d823SDavid Xu 		errno = EINVAL;
195a091d823SDavid Xu 		return (-1);
196a091d823SDavid Xu 	}
197a091d823SDavid Xu 
198a091d823SDavid Xu 	return __sys_sigaction(sig, act, oact);
199a091d823SDavid Xu }
200a091d823SDavid Xu 
201a091d823SDavid Xu __weak_reference(_sigprocmask, sigprocmask);
202a091d823SDavid Xu 
203a091d823SDavid Xu int
204a091d823SDavid Xu _sigprocmask(int how, const sigset_t *set, sigset_t *oset)
205a091d823SDavid Xu {
206a091d823SDavid Xu 	const sigset_t *p = set;
207a091d823SDavid Xu 	sigset_t newset;
208a091d823SDavid Xu 
209a091d823SDavid Xu 	if (how != SIG_UNBLOCK) {
210a091d823SDavid Xu 		if (set != NULL) {
211a091d823SDavid Xu 			newset = *set;
212a091d823SDavid Xu 			SIGDELSET(newset, SIGCANCEL);
213a091d823SDavid Xu 			p = &newset;
214a091d823SDavid Xu 		}
215a091d823SDavid Xu 	}
216a091d823SDavid Xu 	return (__sys_sigprocmask(how, p, oset));
217a091d823SDavid Xu }
218a091d823SDavid Xu 
219bb535300SJeff Roberson __weak_reference(_pthread_sigmask, pthread_sigmask);
220bb535300SJeff Roberson 
221bb535300SJeff Roberson int
222bb535300SJeff Roberson _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
223bb535300SJeff Roberson {
224a091d823SDavid Xu 	if (_sigprocmask(how, set, oset))
225a091d823SDavid Xu 		return (errno);
226a091d823SDavid Xu 	return (0);
227bb535300SJeff Roberson }
228bb535300SJeff Roberson 
22905c3a5eaSDavid Xu __weak_reference(__sigsuspend, sigsuspend);
230bb535300SJeff Roberson 
231bb535300SJeff Roberson int
232a091d823SDavid Xu _sigsuspend(const sigset_t * set)
233bb535300SJeff Roberson {
23405c3a5eaSDavid Xu 	sigset_t newset;
23505c3a5eaSDavid Xu 	const sigset_t *pset;
23605c3a5eaSDavid Xu 	int ret;
23705c3a5eaSDavid Xu 
23805c3a5eaSDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
23905c3a5eaSDavid Xu 		newset = *set;
24005c3a5eaSDavid Xu 		SIGDELSET(newset, SIGCANCEL);
24105c3a5eaSDavid Xu 		pset = &newset;
24205c3a5eaSDavid Xu 	} else
24305c3a5eaSDavid Xu 		pset = set;
24405c3a5eaSDavid Xu 
24505c3a5eaSDavid Xu 	ret = __sys_sigsuspend(pset);
24605c3a5eaSDavid Xu 
24705c3a5eaSDavid Xu 	return (ret);
24805c3a5eaSDavid Xu }
24905c3a5eaSDavid Xu 
25005c3a5eaSDavid Xu int
25105c3a5eaSDavid Xu __sigsuspend(const sigset_t * set)
25205c3a5eaSDavid Xu {
253a091d823SDavid Xu 	struct pthread *curthread = _get_curthread();
254a091d823SDavid Xu 	sigset_t newset;
255a091d823SDavid Xu 	const sigset_t *pset;
256a091d823SDavid Xu 	int ret;
257bb535300SJeff Roberson 
258a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
259a091d823SDavid Xu 		newset = *set;
260a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
261a091d823SDavid Xu 		pset = &newset;
262a091d823SDavid Xu 	} else
263a091d823SDavid Xu 		pset = set;
2640ad70ba9SMike Makonnen 
265f08e1bf6SDavid Xu 	_thr_cancel_enter(curthread);
266a091d823SDavid Xu 	ret = __sys_sigsuspend(pset);
267f08e1bf6SDavid Xu 	_thr_cancel_leave(curthread);
2680ad70ba9SMike Makonnen 
269a091d823SDavid Xu 	return (ret);
270a091d823SDavid Xu }
271a091d823SDavid Xu 
272a091d823SDavid Xu __weak_reference(__sigwait, sigwait);
273a091d823SDavid Xu __weak_reference(__sigtimedwait, sigtimedwait);
274a091d823SDavid Xu __weak_reference(__sigwaitinfo, sigwaitinfo);
275a091d823SDavid Xu 
276a091d823SDavid Xu int
27705c3a5eaSDavid Xu _sigtimedwait(const sigset_t *set, siginfo_t *info,
27805c3a5eaSDavid Xu 	const struct timespec * timeout)
27905c3a5eaSDavid Xu {
28005c3a5eaSDavid Xu 	sigset_t newset;
28105c3a5eaSDavid Xu 	const sigset_t *pset;
28205c3a5eaSDavid Xu 	int ret;
28305c3a5eaSDavid Xu 
28405c3a5eaSDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
28505c3a5eaSDavid Xu 		newset = *set;
28605c3a5eaSDavid Xu 		SIGDELSET(newset, SIGCANCEL);
28705c3a5eaSDavid Xu 		pset = &newset;
28805c3a5eaSDavid Xu 	} else
28905c3a5eaSDavid Xu 		pset = set;
29005c3a5eaSDavid Xu 	ret = __sys_sigtimedwait(pset, info, timeout);
29105c3a5eaSDavid Xu 	return (ret);
29205c3a5eaSDavid Xu }
29305c3a5eaSDavid Xu 
29405c3a5eaSDavid Xu int
295a091d823SDavid Xu __sigtimedwait(const sigset_t *set, siginfo_t *info,
296a091d823SDavid Xu 	const struct timespec * timeout)
297a091d823SDavid Xu {
298a091d823SDavid Xu 	struct pthread	*curthread = _get_curthread();
299a091d823SDavid Xu 	sigset_t newset;
300a091d823SDavid Xu 	const sigset_t *pset;
301a091d823SDavid Xu 	int ret;
302a091d823SDavid Xu 
303a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
304a091d823SDavid Xu 		newset = *set;
305a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
306a091d823SDavid Xu 		pset = &newset;
307a091d823SDavid Xu 	} else
308a091d823SDavid Xu 		pset = set;
309f08e1bf6SDavid Xu 	_thr_cancel_enter(curthread);
310a091d823SDavid Xu 	ret = __sys_sigtimedwait(pset, info, timeout);
311f08e1bf6SDavid Xu 	_thr_cancel_leave(curthread);
312a091d823SDavid Xu 	return (ret);
313a091d823SDavid Xu }
314a091d823SDavid Xu 
315a091d823SDavid Xu int
31605c3a5eaSDavid Xu _sigwaitinfo(const sigset_t *set, siginfo_t *info)
31705c3a5eaSDavid Xu {
31805c3a5eaSDavid Xu 	sigset_t newset;
31905c3a5eaSDavid Xu 	const sigset_t *pset;
32005c3a5eaSDavid Xu 	int ret;
32105c3a5eaSDavid Xu 
32205c3a5eaSDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
32305c3a5eaSDavid Xu 		newset = *set;
32405c3a5eaSDavid Xu 		SIGDELSET(newset, SIGCANCEL);
32505c3a5eaSDavid Xu 		pset = &newset;
32605c3a5eaSDavid Xu 	} else
32705c3a5eaSDavid Xu 		pset = set;
32805c3a5eaSDavid Xu 
32905c3a5eaSDavid Xu 	ret = __sys_sigwaitinfo(pset, info);
33005c3a5eaSDavid Xu 	return (ret);
33105c3a5eaSDavid Xu }
33205c3a5eaSDavid Xu 
33305c3a5eaSDavid Xu int
334a091d823SDavid Xu __sigwaitinfo(const sigset_t *set, siginfo_t *info)
335a091d823SDavid Xu {
336a091d823SDavid Xu 	struct pthread	*curthread = _get_curthread();
337a091d823SDavid Xu 	sigset_t newset;
338a091d823SDavid Xu 	const sigset_t *pset;
339a091d823SDavid Xu 	int ret;
340a091d823SDavid Xu 
341a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
342a091d823SDavid Xu 		newset = *set;
343a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
344a091d823SDavid Xu 		pset = &newset;
345a091d823SDavid Xu 	} else
346a091d823SDavid Xu 		pset = set;
347a091d823SDavid Xu 
348f08e1bf6SDavid Xu 	_thr_cancel_enter(curthread);
349a091d823SDavid Xu 	ret = __sys_sigwaitinfo(pset, info);
350f08e1bf6SDavid Xu 	_thr_cancel_leave(curthread);
351a091d823SDavid Xu 	return (ret);
352a091d823SDavid Xu }
353a091d823SDavid Xu 
354a091d823SDavid Xu int
35505c3a5eaSDavid Xu _sigwait(const sigset_t *set, int *sig)
35605c3a5eaSDavid Xu {
35705c3a5eaSDavid Xu 	sigset_t newset;
35805c3a5eaSDavid Xu 	const sigset_t *pset;
35905c3a5eaSDavid Xu 	int ret;
36005c3a5eaSDavid Xu 
36105c3a5eaSDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
36205c3a5eaSDavid Xu 		newset = *set;
36305c3a5eaSDavid Xu 		SIGDELSET(newset, SIGCANCEL);
36405c3a5eaSDavid Xu 		pset = &newset;
36505c3a5eaSDavid Xu 	} else
36605c3a5eaSDavid Xu 		pset = set;
36705c3a5eaSDavid Xu 
36805c3a5eaSDavid Xu 	ret = __sys_sigwait(pset, sig);
36905c3a5eaSDavid Xu 	return (ret);
37005c3a5eaSDavid Xu }
37105c3a5eaSDavid Xu 
37205c3a5eaSDavid Xu int
373a091d823SDavid Xu __sigwait(const sigset_t *set, int *sig)
374a091d823SDavid Xu {
375a091d823SDavid Xu 	struct pthread	*curthread = _get_curthread();
376a091d823SDavid Xu 	sigset_t newset;
377a091d823SDavid Xu 	const sigset_t *pset;
378a091d823SDavid Xu 	int ret;
379a091d823SDavid Xu 
380a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
381a091d823SDavid Xu 		newset = *set;
382a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
383a091d823SDavid Xu 		pset = &newset;
384a091d823SDavid Xu 	} else
385a091d823SDavid Xu 		pset = set;
386a091d823SDavid Xu 
387f08e1bf6SDavid Xu 	_thr_cancel_enter(curthread);
388a091d823SDavid Xu 	ret = __sys_sigwait(pset, sig);
389f08e1bf6SDavid Xu 	_thr_cancel_leave(curthread);
390a091d823SDavid Xu 	return (ret);
391bb535300SJeff Roberson }
392