thr_sig.c revision 157138
1/*
2 * Copyright (c) 2005, David Xu <davidxu@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice unmodified, this list of conditions, and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD: head/lib/libthr/thread/thr_sig.c 157138 2006-03-26 01:57:03Z davidxu $
27 */
28
29#include <sys/param.h>
30#include <sys/types.h>
31#include <sys/signalvar.h>
32#include <signal.h>
33#include <errno.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <string.h>
37#include <pthread.h>
38
39#include "thr_private.h"
40
41/* #define DEBUG_SIGNAL */
42#ifdef DEBUG_SIGNAL
43#define DBG_MSG		stdout_debug
44#else
45#define DBG_MSG(x...)
46#endif
47
48static void
49sigcancel_handler(int sig, siginfo_t *info, ucontext_t *ucp)
50{
51	struct pthread *curthread = _get_curthread();
52
53	_thr_ast(curthread);
54}
55
56void
57_thr_ast(struct pthread *curthread)
58{
59	if (!THR_IN_CRITICAL(curthread)) {
60		if (__predict_false(
61		    SHOULD_ASYNC_CANCEL(curthread->cancelflags)))
62			_pthread_testcancel();
63		if (__predict_false((curthread->flags &
64		    (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED))
65			== THR_FLAGS_NEED_SUSPEND))
66			_thr_suspend_check(curthread);
67	}
68}
69
70void
71_thr_suspend_check(struct pthread *curthread)
72{
73	umtx_t cycle;
74	int err;
75
76	err = errno;
77	/*
78	 * Blocks SIGCANCEL which other threads must send.
79	 */
80	_thr_signal_block(curthread);
81
82	/*
83	 * Increase critical_count, here we don't use THR_LOCK/UNLOCK
84	 * because we are leaf code, we don't want to recursively call
85	 * ourself.
86	 */
87	curthread->critical_count++;
88	THR_UMTX_LOCK(curthread, &(curthread)->lock);
89	while ((curthread->flags & (THR_FLAGS_NEED_SUSPEND |
90		THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND) {
91		curthread->cycle++;
92		cycle = curthread->cycle;
93
94		/* Wake the thread suspending us. */
95		_thr_umtx_wake(&curthread->cycle, INT_MAX);
96
97		/*
98		 * if we are from pthread_exit, we don't want to
99		 * suspend, just go and die.
100		 */
101		if (curthread->state == PS_DEAD)
102			break;
103		curthread->flags |= THR_FLAGS_SUSPENDED;
104		THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
105		_thr_umtx_wait(&curthread->cycle, cycle, NULL);
106		THR_UMTX_LOCK(curthread, &(curthread)->lock);
107		curthread->flags &= ~THR_FLAGS_SUSPENDED;
108	}
109	THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
110	curthread->critical_count--;
111
112	/*
113	 * Unblocks SIGCANCEL, it is possible a new SIGCANCEL is ready and
114	 * a new signal frame will nest us, this seems a problem because
115	 * stack will grow and overflow, but because kernel will automatically
116	 * mask the SIGCANCEL when delivering the signal, so we at most only
117	 * have one nesting signal frame, this should be fine.
118	 */
119	_thr_signal_unblock(curthread);
120	errno = err;
121}
122
123void
124_thr_signal_init(void)
125{
126	struct sigaction act;
127
128	/* Install cancel handler. */
129	SIGEMPTYSET(act.sa_mask);
130	act.sa_flags = SA_SIGINFO | SA_RESTART;
131	act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler;
132	__sys_sigaction(SIGCANCEL, &act, NULL);
133}
134
135void
136_thr_signal_deinit(void)
137{
138}
139
140__weak_reference(_sigaction, sigaction);
141
142int
143_sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
144{
145	/* Check if the signal number is out of range: */
146	if (sig < 1 || sig > _SIG_MAXSIG || sig == SIGCANCEL) {
147		/* Return an invalid argument: */
148		errno = EINVAL;
149		return (-1);
150	}
151
152	return __sys_sigaction(sig, act, oact);
153}
154
155__weak_reference(_sigprocmask, sigprocmask);
156
157int
158_sigprocmask(int how, const sigset_t *set, sigset_t *oset)
159{
160	const sigset_t *p = set;
161	sigset_t newset;
162
163	if (how != SIG_UNBLOCK) {
164		if (set != NULL) {
165			newset = *set;
166			SIGDELSET(newset, SIGCANCEL);
167			p = &newset;
168		}
169	}
170	return (__sys_sigprocmask(how, p, oset));
171}
172
173__weak_reference(_pthread_sigmask, pthread_sigmask);
174
175int
176_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
177{
178	if (_sigprocmask(how, set, oset))
179		return (errno);
180	return (0);
181}
182
183__weak_reference(_sigsuspend, sigsuspend);
184
185int
186_sigsuspend(const sigset_t * set)
187{
188	struct pthread *curthread = _get_curthread();
189	sigset_t newset;
190	const sigset_t *pset;
191	int oldcancel;
192	int ret;
193
194	if (SIGISMEMBER(*set, SIGCANCEL)) {
195		newset = *set;
196		SIGDELSET(newset, SIGCANCEL);
197		pset = &newset;
198	} else
199		pset = set;
200
201	oldcancel = _thr_cancel_enter(curthread);
202	ret = __sys_sigsuspend(pset);
203	_thr_cancel_leave(curthread, oldcancel);
204
205	return (ret);
206}
207
208__weak_reference(__sigwait, sigwait);
209__weak_reference(__sigtimedwait, sigtimedwait);
210__weak_reference(__sigwaitinfo, sigwaitinfo);
211
212int
213__sigtimedwait(const sigset_t *set, siginfo_t *info,
214	const struct timespec * timeout)
215{
216	struct pthread	*curthread = _get_curthread();
217	sigset_t newset;
218	const sigset_t *pset;
219	int oldcancel;
220	int ret;
221
222	if (SIGISMEMBER(*set, SIGCANCEL)) {
223		newset = *set;
224		SIGDELSET(newset, SIGCANCEL);
225		pset = &newset;
226	} else
227		pset = set;
228	oldcancel = _thr_cancel_enter(curthread);
229	ret = __sys_sigtimedwait(pset, info, timeout);
230	_thr_cancel_leave(curthread, oldcancel);
231	return (ret);
232}
233
234int
235__sigwaitinfo(const sigset_t *set, siginfo_t *info)
236{
237	struct pthread	*curthread = _get_curthread();
238	sigset_t newset;
239	const sigset_t *pset;
240	int oldcancel;
241	int ret;
242
243	if (SIGISMEMBER(*set, SIGCANCEL)) {
244		newset = *set;
245		SIGDELSET(newset, SIGCANCEL);
246		pset = &newset;
247	} else
248		pset = set;
249
250	oldcancel = _thr_cancel_enter(curthread);
251	ret = __sys_sigwaitinfo(pset, info);
252	_thr_cancel_leave(curthread, oldcancel);
253	return (ret);
254}
255
256int
257__sigwait(const sigset_t *set, int *sig)
258{
259	struct pthread	*curthread = _get_curthread();
260	sigset_t newset;
261	const sigset_t *pset;
262	int oldcancel;
263	int ret;
264
265	if (SIGISMEMBER(*set, SIGCANCEL)) {
266		newset = *set;
267		SIGDELSET(newset, SIGCANCEL);
268		pset = &newset;
269	} else
270		pset = set;
271
272	oldcancel = _thr_cancel_enter(curthread);
273	ret = __sys_sigwait(pset, sig);
274	_thr_cancel_leave(curthread, oldcancel);
275	return (ret);
276}
277