xref: /dragonfly/lib/libthread_xu/thread/thr_sig.c (revision b990a6be)
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, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  */
26 
27 #include "namespace.h"
28 #include <sys/signalvar.h>
29 #include <machine/tls.h>
30 #include <signal.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <pthread.h>
34 #include "un-namespace.h"
35 
36 #include "thr_private.h"
37 
38 #if defined(_PTHREADS_DEBUGGING) || defined(_PTHREADS_DEBUGGING2)
39 #include <sys/file.h>
40 #include <stdio.h>
41 #include <string.h>
42 #endif
43 
44 /* #define DEBUG_SIGNAL */
45 #ifdef DEBUG_SIGNAL
46 #define DBG_MSG		stdout_debug
47 #else
48 #define DBG_MSG(x...)
49 #endif
50 
51 int	__sigwait(const sigset_t *set, int *sig);
52 int	__sigwaitinfo(const sigset_t *set, siginfo_t *info);
53 int	__sigtimedwait(const sigset_t *set, siginfo_t *info,
54 		const struct timespec * timeout);
55 
56 #if defined(_PTHREADS_DEBUGGING) || defined(_PTHREADS_DEBUGGING2)
57 static void _thr_debug_sig(int signo);
58 #endif
59 
60 static void
61 sigcancel_handler(int sig __unused, siginfo_t *info __unused,
62 	ucontext_t *ucp __unused)
63 {
64 	struct pthread *curthread = tls_get_curthread();
65 
66 	if (curthread->cancelflags & THR_CANCEL_AT_POINT)
67 		_pthread_testcancel();
68 	_thr_ast(curthread);
69 }
70 
71 void
72 _thr_ast(struct pthread *curthread)
73 {
74 	if (!THR_IN_CRITICAL(curthread)) {
75 		if (__predict_false((curthread->flags &
76 		    (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED))
77 			== THR_FLAGS_NEED_SUSPEND))
78 			_thr_suspend_check(curthread);
79 	}
80 }
81 
82 void
83 _thr_suspend_check(struct pthread *curthread)
84 {
85 	umtx_t cycle;
86 
87 	/*
88 	 * Blocks SIGCANCEL which other threads must send.
89 	 */
90 	_thr_signal_block(curthread);
91 
92 	/*
93 	 * Increase critical_count, here we don't use THR_LOCK/UNLOCK
94 	 * because we are leaf code, we don't want to recursively call
95 	 * ourself.
96 	 */
97 	curthread->critical_count++;
98 	THR_UMTX_LOCK(curthread, &(curthread)->lock);
99 	while ((curthread->flags & (THR_FLAGS_NEED_SUSPEND |
100 		THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND) {
101 		curthread->cycle++;
102 		cycle = curthread->cycle;
103 
104 		/* Wake the thread suspending us. */
105 		_thr_umtx_wake(&curthread->cycle, 0);
106 
107 		/*
108 		 * if we are from pthread_exit, we don't want to
109 		 * suspend, just go and die.
110 		 */
111 		if (curthread->state == PS_DEAD)
112 			break;
113 		curthread->flags |= THR_FLAGS_SUSPENDED;
114 		THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
115 		_thr_umtx_wait(&curthread->cycle, cycle, NULL, 0);
116 		THR_UMTX_LOCK(curthread, &(curthread)->lock);
117 		curthread->flags &= ~THR_FLAGS_SUSPENDED;
118 	}
119 	THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
120 	curthread->critical_count--;
121 
122 	_thr_signal_unblock(curthread);
123 }
124 
125 void
126 _thr_signal_init(void)
127 {
128 	struct sigaction act;
129 
130 	/* Install cancel handler. */
131 	SIGEMPTYSET(act.sa_mask);
132 	act.sa_flags = SA_SIGINFO | SA_RESTART;
133 	act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler;
134 	__sys_sigaction(SIGCANCEL, &act, NULL);
135 
136 #if defined(_PTHREADS_DEBUGGING) || defined(_PTHREADS_DEBUGGING2)
137 	/*
138 	 * If enabled, rwlock, mutex, and condition variable operations
139 	 * are recorded in a text buffer and signal 63 dumps the buffer
140 	 * to /tmp/cond${pid}.log.
141 	 */
142 	act.sa_flags = SA_RESTART;
143 	act.sa_handler = _thr_debug_sig;
144 	__sys_sigaction(63, &act, NULL);
145 #endif
146 }
147 
148 void
149 _thr_signal_deinit(void)
150 {
151 }
152 
153 int
154 _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
155 {
156 	/* Check if the signal number is out of range: */
157 	if (sig < 1 || sig > _SIG_MAXSIG || sig == SIGCANCEL) {
158 		/* Return an invalid argument: */
159 		errno = EINVAL;
160 		return (-1);
161 	}
162 
163 	return __sys_sigaction(sig, act, oact);
164 }
165 
166 __strong_reference(_sigaction, sigaction);
167 
168 int
169 _sigprocmask(int how, const sigset_t *set, sigset_t *oset)
170 {
171 	const sigset_t *p = set;
172 	sigset_t newset;
173 
174 	if (how != SIG_UNBLOCK) {
175 		if (set != NULL) {
176 			newset = *set;
177 			SIGDELSET(newset, SIGCANCEL);
178 			p = &newset;
179 		}
180 	}
181 	return (__sys_sigprocmask(how, p, oset));
182 }
183 
184 __strong_reference(_sigprocmask, sigprocmask);
185 
186 int
187 _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
188 {
189 	if (_sigprocmask(how, set, oset))
190 		return (errno);
191 	return (0);
192 }
193 
194 __strong_reference(_pthread_sigmask, pthread_sigmask);
195 
196 int
197 _sigsuspend(const sigset_t * set)
198 {
199 	struct pthread *curthread = tls_get_curthread();
200 	sigset_t newset;
201 	const sigset_t *pset;
202 	int oldcancel;
203 	int ret;
204 
205 	if (SIGISMEMBER(*set, SIGCANCEL)) {
206 		newset = *set;
207 		SIGDELSET(newset, SIGCANCEL);
208 		pset = &newset;
209 	} else
210 		pset = set;
211 
212 	oldcancel = _thr_cancel_enter(curthread);
213 	ret = __sys_sigsuspend(pset);
214 	_thr_cancel_leave(curthread, oldcancel);
215 
216 	return (ret);
217 }
218 
219 __strong_reference(_sigsuspend, sigsuspend);
220 
221 int
222 __sigtimedwait(const sigset_t *set, siginfo_t *info,
223 	const struct timespec * timeout)
224 {
225 	struct pthread	*curthread = tls_get_curthread();
226 	sigset_t newset;
227 	const sigset_t *pset;
228 	int oldcancel;
229 	int ret;
230 
231 	if (SIGISMEMBER(*set, SIGCANCEL)) {
232 		newset = *set;
233 		SIGDELSET(newset, SIGCANCEL);
234 		pset = &newset;
235 	} else
236 		pset = set;
237 	oldcancel = _thr_cancel_enter(curthread);
238 	ret = __sys_sigtimedwait(pset, info, timeout);
239 	_thr_cancel_leave(curthread, oldcancel);
240 	return (ret);
241 }
242 
243 __strong_reference(__sigtimedwait, sigtimedwait);
244 
245 int
246 __sigwaitinfo(const sigset_t *set, siginfo_t *info)
247 {
248 	struct pthread	*curthread = tls_get_curthread();
249 	sigset_t newset;
250 	const sigset_t *pset;
251 	int oldcancel;
252 	int ret;
253 
254 	if (SIGISMEMBER(*set, SIGCANCEL)) {
255 		newset = *set;
256 		SIGDELSET(newset, SIGCANCEL);
257 		pset = &newset;
258 	} else
259 		pset = set;
260 
261 	oldcancel = _thr_cancel_enter(curthread);
262 	ret = __sys_sigwaitinfo(pset, info);
263 	_thr_cancel_leave(curthread, oldcancel);
264 	return (ret);
265 }
266 
267 __strong_reference(__sigwaitinfo, sigwaitinfo);
268 
269 int
270 __sigwait(const sigset_t *set, int *sig)
271 {
272 	int s;
273 
274 	s = __sigwaitinfo(set, NULL);
275 	if (s > 0) {
276 		*sig = s;
277 		return (0);
278 	}
279 	return (errno);
280 }
281 
282 __strong_reference(__sigwait, sigwait);
283 
284 #if defined(_PTHREADS_DEBUGGING) || defined(_PTHREADS_DEBUGGING2)
285 
286 #define LOGBUF_SIZE	(4 * 1024 * 1024)
287 #define LOGBUF_MASK	(LOGBUF_SIZE - 1)
288 
289 char LogBuf[LOGBUF_SIZE];
290 unsigned long LogWIndex;
291 
292 void
293 _thr_log(const char *buf, size_t bytes)
294 {
295 	struct pthread *curthread;
296 	unsigned long i;
297 	char prefix[32];
298 	size_t plen;
299 
300 	curthread = tls_get_curthread();
301 	if (curthread) {
302 		plen = snprintf(prefix, sizeof(prefix), "%d.%d: ",
303 				(int)__sys_getpid(),
304 				curthread->tid);
305 	} else {
306 		plen = snprintf(prefix, sizeof(prefix), "unknown: ");
307 	}
308 
309 	if (bytes == 0)
310 		bytes = strlen(buf);
311 	i = atomic_fetchadd_long(&LogWIndex, plen + bytes);
312 	i = i & LOGBUF_MASK;
313 	if (plen <= (size_t)(LOGBUF_SIZE - i)) {
314 		bcopy(prefix, LogBuf + i, plen);
315 	} else {
316 		bcopy(prefix, LogBuf + i, LOGBUF_SIZE - i);
317 		plen -= LOGBUF_SIZE - i;
318 		bcopy(prefix, LogBuf, plen);
319 	}
320 
321 	i += plen;
322 	i = i & LOGBUF_MASK;
323 	if (bytes <= (size_t)(LOGBUF_SIZE - i)) {
324 		bcopy(buf, LogBuf + i, bytes);
325 	} else {
326 		bcopy(buf, LogBuf + i, LOGBUF_SIZE - i);
327 		bytes -= LOGBUF_SIZE - i;
328 		bcopy(buf, LogBuf, bytes);
329 	}
330 }
331 
332 static void
333 _thr_debug_sig(int signo __unused)
334 {
335         char buf[256];
336 	int fd;
337 	unsigned long i;
338 
339 	snprintf(buf, sizeof(buf), "/tmp/cond%d.log", (int)__sys_getpid());
340 	fd = open(buf, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0666);
341 	if (fd >= 0) {
342 		i = LogWIndex;
343 		if (i < LOGBUF_SIZE) {
344 			write(fd, LogBuf, i);
345 		} else {
346 			i &= LOGBUF_MASK;
347 			write(fd, LogBuf + i, LOGBUF_SIZE - i);
348 			write(fd, LogBuf, i);
349 		}
350 		close(fd);
351 	}
352 }
353 
354 #endif
355