xref: /freebsd/lib/libthr/thread/thr_sig.c (revision 37a6356bbed1e94fd2b0d9d02a29508465584c19)
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 
5037a6356bSDavid Xu int	__sigtimedwait(const sigset_t *set, siginfo_t *info,
5137a6356bSDavid Xu 	const struct timespec * timeout);
5237a6356bSDavid Xu int	__sigwaitinfo(const sigset_t *set, siginfo_t *info);
5337a6356bSDavid Xu int	__sigwait(const sigset_t *set, int *sig);
5437a6356bSDavid Xu 
55a091d823SDavid Xu static void
5637a6356bSDavid Xu sigcancel_handler(int sig __unused,
5737a6356bSDavid Xu 	siginfo_t *info __unused, ucontext_t *ucp __unused)
58a091d823SDavid Xu {
59a091d823SDavid Xu 	struct pthread *curthread = _get_curthread();
60a091d823SDavid Xu 
61bc414752SDavid Xu 	_thr_ast(curthread);
62bc414752SDavid Xu }
63bc414752SDavid Xu 
64bc414752SDavid Xu void
65bc414752SDavid Xu _thr_ast(struct pthread *curthread)
66bc414752SDavid Xu {
67bc414752SDavid Xu 	if (!THR_IN_CRITICAL(curthread)) {
68d448272dSDavid Xu 		if (__predict_false(
69d448272dSDavid Xu 		    SHOULD_ASYNC_CANCEL(curthread->cancelflags)))
70b6b894f6SDavid Xu 			_pthread_testcancel();
71bc414752SDavid Xu 		if (__predict_false((curthread->flags &
72bc414752SDavid Xu 		    (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED))
73bc414752SDavid Xu 			== THR_FLAGS_NEED_SUSPEND))
74a091d823SDavid Xu 			_thr_suspend_check(curthread);
75a091d823SDavid Xu 	}
76a091d823SDavid Xu }
77a091d823SDavid Xu 
78a091d823SDavid Xu void
79a091d823SDavid Xu _thr_suspend_check(struct pthread *curthread)
80a091d823SDavid Xu {
81bc414752SDavid Xu 	umtx_t cycle;
82d448272dSDavid Xu 	int err;
83a091d823SDavid Xu 
84d448272dSDavid Xu 	err = errno;
85bc414752SDavid Xu 	/*
86bc414752SDavid Xu 	 * Blocks SIGCANCEL which other threads must send.
87bc414752SDavid Xu 	 */
88a091d823SDavid Xu 	_thr_signal_block(curthread);
89bc414752SDavid Xu 
90bc414752SDavid Xu 	/*
91bc414752SDavid Xu 	 * Increase critical_count, here we don't use THR_LOCK/UNLOCK
92bc414752SDavid Xu 	 * because we are leaf code, we don't want to recursively call
93bc414752SDavid Xu 	 * ourself.
94bc414752SDavid Xu 	 */
95bc414752SDavid Xu 	curthread->critical_count++;
96bc414752SDavid Xu 	THR_UMTX_LOCK(curthread, &(curthread)->lock);
97bc414752SDavid Xu 	while ((curthread->flags & (THR_FLAGS_NEED_SUSPEND |
98bc414752SDavid Xu 		THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND) {
99bc414752SDavid Xu 		curthread->cycle++;
100a091d823SDavid Xu 		cycle = curthread->cycle;
101bc414752SDavid Xu 
102bc414752SDavid Xu 		/* Wake the thread suspending us. */
103bc414752SDavid Xu 		_thr_umtx_wake(&curthread->cycle, INT_MAX);
104bc414752SDavid Xu 
105bc414752SDavid Xu 		/*
106bc414752SDavid Xu 		 * if we are from pthread_exit, we don't want to
107bc414752SDavid Xu 		 * suspend, just go and die.
108bc414752SDavid Xu 		 */
109bc414752SDavid Xu 		if (curthread->state == PS_DEAD)
110bc414752SDavid Xu 			break;
111bc414752SDavid Xu 		curthread->flags |= THR_FLAGS_SUSPENDED;
112bc414752SDavid Xu 		THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
113a091d823SDavid Xu 		_thr_umtx_wait(&curthread->cycle, cycle, NULL);
114bc414752SDavid Xu 		THR_UMTX_LOCK(curthread, &(curthread)->lock);
115a091d823SDavid Xu 		curthread->flags &= ~THR_FLAGS_SUSPENDED;
116a091d823SDavid Xu 	}
117bc414752SDavid Xu 	THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
118bc414752SDavid Xu 	curthread->critical_count--;
119bc414752SDavid Xu 
120bc414752SDavid Xu 	/*
121bc414752SDavid Xu 	 * Unblocks SIGCANCEL, it is possible a new SIGCANCEL is ready and
122bc414752SDavid Xu 	 * a new signal frame will nest us, this seems a problem because
123bc414752SDavid Xu 	 * stack will grow and overflow, but because kernel will automatically
124bc414752SDavid Xu 	 * mask the SIGCANCEL when delivering the signal, so we at most only
125bc414752SDavid Xu 	 * have one nesting signal frame, this should be fine.
126bc414752SDavid Xu 	 */
127a091d823SDavid Xu 	_thr_signal_unblock(curthread);
128d448272dSDavid Xu 	errno = err;
129a091d823SDavid Xu }
130a091d823SDavid Xu 
131a091d823SDavid Xu void
132a091d823SDavid Xu _thr_signal_init(void)
133a091d823SDavid Xu {
134a091d823SDavid Xu 	struct sigaction act;
135a091d823SDavid Xu 
136a091d823SDavid Xu 	/* Install cancel handler. */
137a091d823SDavid Xu 	SIGEMPTYSET(act.sa_mask);
138a091d823SDavid Xu 	act.sa_flags = SA_SIGINFO | SA_RESTART;
139a091d823SDavid Xu 	act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler;
140a091d823SDavid Xu 	__sys_sigaction(SIGCANCEL, &act, NULL);
141a091d823SDavid Xu }
142a091d823SDavid Xu 
143a091d823SDavid Xu void
144a091d823SDavid Xu _thr_signal_deinit(void)
145a091d823SDavid Xu {
146a091d823SDavid Xu }
147a091d823SDavid Xu 
148a091d823SDavid Xu __weak_reference(_sigaction, sigaction);
149a091d823SDavid Xu 
150a091d823SDavid Xu int
151a091d823SDavid Xu _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
152a091d823SDavid Xu {
153a091d823SDavid Xu 	/* Check if the signal number is out of range: */
154a091d823SDavid Xu 	if (sig < 1 || sig > _SIG_MAXSIG || sig == SIGCANCEL) {
155a091d823SDavid Xu 		/* Return an invalid argument: */
156a091d823SDavid Xu 		errno = EINVAL;
157a091d823SDavid Xu 		return (-1);
158a091d823SDavid Xu 	}
159a091d823SDavid Xu 
160a091d823SDavid Xu 	return __sys_sigaction(sig, act, oact);
161a091d823SDavid Xu }
162a091d823SDavid Xu 
163a091d823SDavid Xu __weak_reference(_sigprocmask, sigprocmask);
164a091d823SDavid Xu 
165a091d823SDavid Xu int
166a091d823SDavid Xu _sigprocmask(int how, const sigset_t *set, sigset_t *oset)
167a091d823SDavid Xu {
168a091d823SDavid Xu 	const sigset_t *p = set;
169a091d823SDavid Xu 	sigset_t newset;
170a091d823SDavid Xu 
171a091d823SDavid Xu 	if (how != SIG_UNBLOCK) {
172a091d823SDavid Xu 		if (set != NULL) {
173a091d823SDavid Xu 			newset = *set;
174a091d823SDavid Xu 			SIGDELSET(newset, SIGCANCEL);
175a091d823SDavid Xu 			p = &newset;
176a091d823SDavid Xu 		}
177a091d823SDavid Xu 	}
178a091d823SDavid Xu 	return (__sys_sigprocmask(how, p, oset));
179a091d823SDavid Xu }
180a091d823SDavid Xu 
181bb535300SJeff Roberson __weak_reference(_pthread_sigmask, pthread_sigmask);
182bb535300SJeff Roberson 
183bb535300SJeff Roberson int
184bb535300SJeff Roberson _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
185bb535300SJeff Roberson {
186a091d823SDavid Xu 	if (_sigprocmask(how, set, oset))
187a091d823SDavid Xu 		return (errno);
188a091d823SDavid Xu 	return (0);
189bb535300SJeff Roberson }
190bb535300SJeff Roberson 
191a091d823SDavid Xu __weak_reference(_sigsuspend, sigsuspend);
192bb535300SJeff Roberson 
193bb535300SJeff Roberson int
194a091d823SDavid Xu _sigsuspend(const sigset_t * set)
195bb535300SJeff Roberson {
196a091d823SDavid Xu 	struct pthread *curthread = _get_curthread();
197a091d823SDavid Xu 	sigset_t newset;
198a091d823SDavid Xu 	const sigset_t *pset;
199a091d823SDavid Xu 	int oldcancel;
200a091d823SDavid Xu 	int ret;
201bb535300SJeff Roberson 
202a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
203a091d823SDavid Xu 		newset = *set;
204a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
205a091d823SDavid Xu 		pset = &newset;
206a091d823SDavid Xu 	} else
207a091d823SDavid Xu 		pset = set;
2080ad70ba9SMike Makonnen 
209a091d823SDavid Xu 	oldcancel = _thr_cancel_enter(curthread);
210a091d823SDavid Xu 	ret = __sys_sigsuspend(pset);
211a091d823SDavid Xu 	_thr_cancel_leave(curthread, oldcancel);
2120ad70ba9SMike Makonnen 
213a091d823SDavid Xu 	return (ret);
214a091d823SDavid Xu }
215a091d823SDavid Xu 
216a091d823SDavid Xu __weak_reference(__sigwait, sigwait);
217a091d823SDavid Xu __weak_reference(__sigtimedwait, sigtimedwait);
218a091d823SDavid Xu __weak_reference(__sigwaitinfo, sigwaitinfo);
219a091d823SDavid Xu 
220a091d823SDavid Xu int
221a091d823SDavid Xu __sigtimedwait(const sigset_t *set, siginfo_t *info,
222a091d823SDavid Xu 	const struct timespec * timeout)
223a091d823SDavid Xu {
224a091d823SDavid Xu 	struct pthread	*curthread = _get_curthread();
225a091d823SDavid Xu 	sigset_t newset;
226a091d823SDavid Xu 	const sigset_t *pset;
227a091d823SDavid Xu 	int oldcancel;
228a091d823SDavid Xu 	int ret;
229a091d823SDavid Xu 
230a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
231a091d823SDavid Xu 		newset = *set;
232a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
233a091d823SDavid Xu 		pset = &newset;
234a091d823SDavid Xu 	} else
235a091d823SDavid Xu 		pset = set;
236a091d823SDavid Xu 	oldcancel = _thr_cancel_enter(curthread);
237a091d823SDavid Xu 	ret = __sys_sigtimedwait(pset, info, timeout);
238a091d823SDavid Xu 	_thr_cancel_leave(curthread, oldcancel);
239a091d823SDavid Xu 	return (ret);
240a091d823SDavid Xu }
241a091d823SDavid Xu 
242a091d823SDavid Xu int
243a091d823SDavid Xu __sigwaitinfo(const sigset_t *set, siginfo_t *info)
244a091d823SDavid Xu {
245a091d823SDavid Xu 	struct pthread	*curthread = _get_curthread();
246a091d823SDavid Xu 	sigset_t newset;
247a091d823SDavid Xu 	const sigset_t *pset;
248a091d823SDavid Xu 	int oldcancel;
249a091d823SDavid Xu 	int ret;
250a091d823SDavid Xu 
251a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
252a091d823SDavid Xu 		newset = *set;
253a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
254a091d823SDavid Xu 		pset = &newset;
255a091d823SDavid Xu 	} else
256a091d823SDavid Xu 		pset = set;
257a091d823SDavid Xu 
258a091d823SDavid Xu 	oldcancel = _thr_cancel_enter(curthread);
259a091d823SDavid Xu 	ret = __sys_sigwaitinfo(pset, info);
260a091d823SDavid Xu 	_thr_cancel_leave(curthread, oldcancel);
261a091d823SDavid Xu 	return (ret);
262a091d823SDavid Xu }
263a091d823SDavid Xu 
264a091d823SDavid Xu int
265a091d823SDavid Xu __sigwait(const sigset_t *set, int *sig)
266a091d823SDavid Xu {
267a091d823SDavid Xu 	struct pthread	*curthread = _get_curthread();
268a091d823SDavid Xu 	sigset_t newset;
269a091d823SDavid Xu 	const sigset_t *pset;
270a091d823SDavid Xu 	int oldcancel;
271a091d823SDavid Xu 	int ret;
272a091d823SDavid Xu 
273a091d823SDavid Xu 	if (SIGISMEMBER(*set, SIGCANCEL)) {
274a091d823SDavid Xu 		newset = *set;
275a091d823SDavid Xu 		SIGDELSET(newset, SIGCANCEL);
276a091d823SDavid Xu 		pset = &newset;
277a091d823SDavid Xu 	} else
278a091d823SDavid Xu 		pset = set;
279a091d823SDavid Xu 
280a091d823SDavid Xu 	oldcancel = _thr_cancel_enter(curthread);
281a091d823SDavid Xu 	ret = __sys_sigwait(pset, sig);
282a091d823SDavid Xu 	_thr_cancel_leave(curthread, oldcancel);
283a091d823SDavid Xu 	return (ret);
284bb535300SJeff Roberson }
285