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