thr_sig.c revision 153496
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 153496 2005-12-17 09:42:45Z 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	if (curthread->flags & THR_FLAGS_NEED_SUSPEND) {
56		__sys_sigprocmask(SIG_SETMASK, &ucp->uc_sigmask, NULL);
57		_thr_suspend_check(curthread);
58	}
59}
60
61void
62_thr_suspend_check(struct pthread *curthread)
63{
64	long cycle;
65
66	/* Async suspend. */
67	_thr_signal_block(curthread);
68	THR_LOCK(curthread);
69	if ((curthread->flags & (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED))
70		== THR_FLAGS_NEED_SUSPEND) {
71		curthread->flags |= THR_FLAGS_SUSPENDED;
72		while (curthread->flags & THR_FLAGS_NEED_SUSPEND) {
73			cycle = curthread->cycle;
74			THR_UNLOCK(curthread);
75			_thr_signal_unblock(curthread);
76			_thr_umtx_wait(&curthread->cycle, cycle, NULL);
77			_thr_signal_block(curthread);
78			THR_LOCK(curthread);
79		}
80		curthread->flags &= ~THR_FLAGS_SUSPENDED;
81	}
82	THR_UNLOCK(curthread);
83	_thr_signal_unblock(curthread);
84}
85
86void
87_thr_signal_init(void)
88{
89	struct sigaction act;
90
91	/* Install cancel handler. */
92	SIGEMPTYSET(act.sa_mask);
93	act.sa_flags = SA_SIGINFO | SA_RESTART;
94	act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler;
95	__sys_sigaction(SIGCANCEL, &act, NULL);
96}
97
98void
99_thr_signal_deinit(void)
100{
101}
102
103__weak_reference(_sigaction, sigaction);
104
105int
106_sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
107{
108	/* Check if the signal number is out of range: */
109	if (sig < 1 || sig > _SIG_MAXSIG || sig == SIGCANCEL) {
110		/* Return an invalid argument: */
111		errno = EINVAL;
112		return (-1);
113	}
114
115	return __sys_sigaction(sig, act, oact);
116}
117
118__weak_reference(_sigprocmask, sigprocmask);
119
120int
121_sigprocmask(int how, const sigset_t *set, sigset_t *oset)
122{
123	const sigset_t *p = set;
124	sigset_t newset;
125
126	if (how != SIG_UNBLOCK) {
127		if (set != NULL) {
128			newset = *set;
129			SIGDELSET(newset, SIGCANCEL);
130			p = &newset;
131		}
132	}
133	return (__sys_sigprocmask(how, p, oset));
134}
135
136__weak_reference(_pthread_sigmask, pthread_sigmask);
137
138int
139_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
140{
141	if (_sigprocmask(how, set, oset))
142		return (errno);
143	return (0);
144}
145
146__weak_reference(_sigsuspend, sigsuspend);
147
148int
149_sigsuspend(const sigset_t * set)
150{
151	struct pthread *curthread = _get_curthread();
152	sigset_t newset;
153	const sigset_t *pset;
154	int oldcancel;
155	int ret;
156
157	if (SIGISMEMBER(*set, SIGCANCEL)) {
158		newset = *set;
159		SIGDELSET(newset, SIGCANCEL);
160		pset = &newset;
161	} else
162		pset = set;
163
164	oldcancel = _thr_cancel_enter(curthread);
165	ret = __sys_sigsuspend(pset);
166	_thr_cancel_leave(curthread, oldcancel);
167
168	return (ret);
169}
170
171__weak_reference(__sigwait, sigwait);
172__weak_reference(__sigtimedwait, sigtimedwait);
173__weak_reference(__sigwaitinfo, sigwaitinfo);
174
175int
176__sigtimedwait(const sigset_t *set, siginfo_t *info,
177	const struct timespec * timeout)
178{
179	struct pthread	*curthread = _get_curthread();
180	sigset_t newset;
181	const sigset_t *pset;
182	int oldcancel;
183	int ret;
184
185	if (SIGISMEMBER(*set, SIGCANCEL)) {
186		newset = *set;
187		SIGDELSET(newset, SIGCANCEL);
188		pset = &newset;
189	} else
190		pset = set;
191	oldcancel = _thr_cancel_enter(curthread);
192	ret = __sys_sigtimedwait(pset, info, timeout);
193	_thr_cancel_leave(curthread, oldcancel);
194	return (ret);
195}
196
197int
198__sigwaitinfo(const sigset_t *set, siginfo_t *info)
199{
200	struct pthread	*curthread = _get_curthread();
201	sigset_t newset;
202	const sigset_t *pset;
203	int oldcancel;
204	int ret;
205
206	if (SIGISMEMBER(*set, SIGCANCEL)) {
207		newset = *set;
208		SIGDELSET(newset, SIGCANCEL);
209		pset = &newset;
210	} else
211		pset = set;
212
213	oldcancel = _thr_cancel_enter(curthread);
214	ret = __sys_sigwaitinfo(pset, info);
215	_thr_cancel_leave(curthread, oldcancel);
216	return (ret);
217}
218
219int
220__sigwait(const sigset_t *set, int *sig)
221{
222	struct pthread	*curthread = _get_curthread();
223	sigset_t newset;
224	const sigset_t *pset;
225	int oldcancel;
226	int ret;
227
228	if (SIGISMEMBER(*set, SIGCANCEL)) {
229		newset = *set;
230		SIGDELSET(newset, SIGCANCEL);
231		pset = &newset;
232	} else
233		pset = set;
234
235	oldcancel = _thr_cancel_enter(curthread);
236	ret = __sys_sigwait(pset, sig);
237	_thr_cancel_leave(curthread, oldcancel);
238	return (ret);
239}
240