xref: /freebsd/lib/libthr/thread/thr_sig.c (revision 87569f75a91f298c52a71823c04d41cf53c88889)
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 	_thr_ast(curthread);
56 }
57 
58 void
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 
69 void
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 
119 void
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 
131 void
132 _thr_signal_deinit(void)
133 {
134 }
135 
136 __weak_reference(_sigaction, sigaction);
137 
138 int
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 
153 int
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 
171 int
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 
181 int
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 
208 int
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 
230 int
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 
252 int
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