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