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