xref: /freebsd-src/lib/libthr/thread/thr_sig.c (revision 17ed18fef3ee30b72f8f88ef7b43d082ae8d8bf1)
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$
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 
48 static void
49 sigcancel_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 
61 void
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 
86 void
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 
98 void
99 _thr_signal_deinit(void)
100 {
101 }
102 
103 __weak_reference(_sigaction, sigaction);
104 
105 int
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 
120 int
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 
138 int
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 
148 int
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 
175 int
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 
197 int
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 
219 int
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