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