thr_sig.c revision 176818
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 176818 2008-03-05 07:04:55Z 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
50extern int	__pause(void);
51int	___pause(void);
52int	_raise(int);
53int	__sigtimedwait(const sigset_t *set, siginfo_t *info,
54	const struct timespec * timeout);
55int	_sigtimedwait(const sigset_t *set, siginfo_t *info,
56	const struct timespec * timeout);
57int	__sigwaitinfo(const sigset_t *set, siginfo_t *info);
58int	_sigwaitinfo(const sigset_t *set, siginfo_t *info);
59int	__sigwait(const sigset_t *set, int *sig);
60int	_sigwait(const sigset_t *set, int *sig);
61int	__sigsuspend(const sigset_t *sigmask);
62
63
64static void
65sigcancel_handler(int sig __unused,
66	siginfo_t *info __unused, ucontext_t *ucp __unused)
67{
68	struct pthread *curthread = _get_curthread();
69
70	if (curthread->cancel_defer && curthread->cancel_pending)
71		thr_wake(curthread->tid);
72	curthread->in_sigcancel_handler++;
73	_thr_ast(curthread);
74	curthread->in_sigcancel_handler--;
75}
76
77void
78_thr_ast(struct pthread *curthread)
79{
80	if (!THR_IN_CRITICAL(curthread)) {
81		_thr_testcancel(curthread);
82		if (__predict_false((curthread->flags &
83		    (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED))
84			== THR_FLAGS_NEED_SUSPEND))
85			_thr_suspend_check(curthread);
86	}
87}
88
89void
90_thr_suspend_check(struct pthread *curthread)
91{
92	long cycle;
93	int err;
94
95	err = errno;
96	/*
97	 * Blocks SIGCANCEL which other threads must send.
98	 */
99	_thr_signal_block(curthread);
100
101	/*
102	 * Increase critical_count, here we don't use THR_LOCK/UNLOCK
103	 * because we are leaf code, we don't want to recursively call
104	 * ourself.
105	 */
106	curthread->critical_count++;
107	THR_UMUTEX_LOCK(curthread, &(curthread)->lock);
108	while ((curthread->flags & (THR_FLAGS_NEED_SUSPEND |
109		THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND) {
110		curthread->cycle++;
111		cycle = curthread->cycle;
112
113		/* Wake the thread suspending us. */
114		_thr_umtx_wake(&curthread->cycle, INT_MAX);
115
116		/*
117		 * if we are from pthread_exit, we don't want to
118		 * suspend, just go and die.
119		 */
120		if (curthread->state == PS_DEAD)
121			break;
122		curthread->flags |= THR_FLAGS_SUSPENDED;
123		THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock);
124		_thr_umtx_wait(&curthread->cycle, cycle, NULL);
125		THR_UMUTEX_LOCK(curthread, &(curthread)->lock);
126		curthread->flags &= ~THR_FLAGS_SUSPENDED;
127	}
128	THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock);
129	curthread->critical_count--;
130
131	/*
132	 * Unblocks SIGCANCEL, it is possible a new SIGCANCEL is ready and
133	 * a new signal frame will nest us, this seems a problem because
134	 * stack will grow and overflow, but because kernel will automatically
135	 * mask the SIGCANCEL when delivering the signal, so we at most only
136	 * have one nesting signal frame, this should be fine.
137	 */
138	_thr_signal_unblock(curthread);
139	errno = err;
140}
141
142void
143_thr_signal_init(void)
144{
145	struct sigaction act;
146
147	/* Install cancel handler. */
148	SIGEMPTYSET(act.sa_mask);
149	act.sa_flags = SA_SIGINFO | SA_RESTART;
150	act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler;
151	__sys_sigaction(SIGCANCEL, &act, NULL);
152}
153
154void
155_thr_signal_deinit(void)
156{
157}
158
159__weak_reference(___pause, pause);
160
161int
162___pause(void)
163{
164	struct pthread *curthread = _get_curthread();
165	int	ret;
166
167	_thr_cancel_enter(curthread);
168	ret = __pause();
169	_thr_cancel_leave(curthread);
170
171	return ret;
172}
173
174__weak_reference(_raise, raise);
175
176int
177_raise(int sig)
178{
179	int ret;
180
181	if (!_thr_isthreaded())
182		ret = kill(getpid(), sig);
183	else
184		ret = _thr_send_sig(_get_curthread(), sig);
185	return (ret);
186}
187
188__weak_reference(_sigaction, sigaction);
189
190int
191_sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
192{
193	/* Check if the signal number is out of range: */
194	if (sig < 1 || sig > _SIG_MAXSIG || sig == SIGCANCEL) {
195		/* Return an invalid argument: */
196		errno = EINVAL;
197		return (-1);
198	}
199
200	return __sys_sigaction(sig, act, oact);
201}
202
203__weak_reference(_sigprocmask, sigprocmask);
204
205int
206_sigprocmask(int how, const sigset_t *set, sigset_t *oset)
207{
208	const sigset_t *p = set;
209	sigset_t newset;
210
211	if (how != SIG_UNBLOCK) {
212		if (set != NULL) {
213			newset = *set;
214			SIGDELSET(newset, SIGCANCEL);
215			p = &newset;
216		}
217	}
218	return (__sys_sigprocmask(how, p, oset));
219}
220
221__weak_reference(_pthread_sigmask, pthread_sigmask);
222
223int
224_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
225{
226	if (_sigprocmask(how, set, oset))
227		return (errno);
228	return (0);
229}
230
231__weak_reference(__sigsuspend, sigsuspend);
232
233int
234_sigsuspend(const sigset_t * set)
235{
236	sigset_t newset;
237	const sigset_t *pset;
238	int ret;
239
240	if (SIGISMEMBER(*set, SIGCANCEL)) {
241		newset = *set;
242		SIGDELSET(newset, SIGCANCEL);
243		pset = &newset;
244	} else
245		pset = set;
246
247	ret = __sys_sigsuspend(pset);
248
249	return (ret);
250}
251
252int
253__sigsuspend(const sigset_t * set)
254{
255	struct pthread *curthread = _get_curthread();
256	sigset_t newset;
257	const sigset_t *pset;
258	int ret;
259
260	if (SIGISMEMBER(*set, SIGCANCEL)) {
261		newset = *set;
262		SIGDELSET(newset, SIGCANCEL);
263		pset = &newset;
264	} else
265		pset = set;
266
267	_thr_cancel_enter(curthread);
268	ret = __sys_sigsuspend(pset);
269	_thr_cancel_leave(curthread);
270
271	return (ret);
272}
273
274__weak_reference(__sigwait, sigwait);
275__weak_reference(__sigtimedwait, sigtimedwait);
276__weak_reference(__sigwaitinfo, sigwaitinfo);
277
278int
279_sigtimedwait(const sigset_t *set, siginfo_t *info,
280	const struct timespec * timeout)
281{
282	sigset_t newset;
283	const sigset_t *pset;
284	int ret;
285
286	if (SIGISMEMBER(*set, SIGCANCEL)) {
287		newset = *set;
288		SIGDELSET(newset, SIGCANCEL);
289		pset = &newset;
290	} else
291		pset = set;
292	ret = __sys_sigtimedwait(pset, info, timeout);
293	return (ret);
294}
295
296int
297__sigtimedwait(const sigset_t *set, siginfo_t *info,
298	const struct timespec * timeout)
299{
300	struct pthread	*curthread = _get_curthread();
301	sigset_t newset;
302	const sigset_t *pset;
303	int ret;
304
305	if (SIGISMEMBER(*set, SIGCANCEL)) {
306		newset = *set;
307		SIGDELSET(newset, SIGCANCEL);
308		pset = &newset;
309	} else
310		pset = set;
311	_thr_cancel_enter(curthread);
312	ret = __sys_sigtimedwait(pset, info, timeout);
313	_thr_cancel_leave(curthread);
314	return (ret);
315}
316
317int
318_sigwaitinfo(const sigset_t *set, siginfo_t *info)
319{
320	sigset_t newset;
321	const sigset_t *pset;
322	int ret;
323
324	if (SIGISMEMBER(*set, SIGCANCEL)) {
325		newset = *set;
326		SIGDELSET(newset, SIGCANCEL);
327		pset = &newset;
328	} else
329		pset = set;
330
331	ret = __sys_sigwaitinfo(pset, info);
332	return (ret);
333}
334
335int
336__sigwaitinfo(const sigset_t *set, siginfo_t *info)
337{
338	struct pthread	*curthread = _get_curthread();
339	sigset_t newset;
340	const sigset_t *pset;
341	int ret;
342
343	if (SIGISMEMBER(*set, SIGCANCEL)) {
344		newset = *set;
345		SIGDELSET(newset, SIGCANCEL);
346		pset = &newset;
347	} else
348		pset = set;
349
350	_thr_cancel_enter(curthread);
351	ret = __sys_sigwaitinfo(pset, info);
352	_thr_cancel_leave(curthread);
353	return (ret);
354}
355
356int
357_sigwait(const sigset_t *set, int *sig)
358{
359	sigset_t newset;
360	const sigset_t *pset;
361	int ret;
362
363	if (SIGISMEMBER(*set, SIGCANCEL)) {
364		newset = *set;
365		SIGDELSET(newset, SIGCANCEL);
366		pset = &newset;
367	} else
368		pset = set;
369
370	ret = __sys_sigwait(pset, sig);
371	return (ret);
372}
373
374int
375__sigwait(const sigset_t *set, int *sig)
376{
377	struct pthread	*curthread = _get_curthread();
378	sigset_t newset;
379	const sigset_t *pset;
380	int ret;
381
382	if (SIGISMEMBER(*set, SIGCANCEL)) {
383		newset = *set;
384		SIGDELSET(newset, SIGCANCEL);
385		pset = &newset;
386	} else
387		pset = set;
388
389	_thr_cancel_enter(curthread);
390	ret = __sys_sigwait(pset, sig);
391	_thr_cancel_leave(curthread);
392	return (ret);
393}
394