xref: /freebsd/lib/libthr/thread/thr_sig.c (revision 0f2bd1e89db1a2f09268edea21e0ead329e092df)
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 "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 
50 extern int	__pause(void);
51 int	___pause(void);
52 int	_raise(int);
53 int	__sigtimedwait(const sigset_t *set, siginfo_t *info,
54 	const struct timespec * timeout);
55 int	_sigtimedwait(const sigset_t *set, siginfo_t *info,
56 	const struct timespec * timeout);
57 int	__sigwaitinfo(const sigset_t *set, siginfo_t *info);
58 int	_sigwaitinfo(const sigset_t *set, siginfo_t *info);
59 int	__sigwait(const sigset_t *set, int *sig);
60 int	_sigwait(const sigset_t *set, int *sig);
61 int	__sigsuspend(const sigset_t *sigmask);
62 
63 
64 static void
65 sigcancel_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 
75 void
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 
124 void
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 
180 void
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 
192 void
193 _thr_signal_deinit(void)
194 {
195 }
196 
197 __weak_reference(___pause, pause);
198 
199 int
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 
214 int
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 
228 int
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 
243 int
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 
261 int
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 
271 static const sigset_t *
272 thr_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 
285 int
286 _sigsuspend(const sigset_t * set)
287 {
288 	sigset_t newset;
289 
290 	return (__sys_sigsuspend(thr_remove_thr_signals(set, &newset)));
291 }
292 
293 int
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 
311 int
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  */
326 int
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 
341 int
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  */
354 int
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 
367 int
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  */
380 int
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