06356d576cd43715ca8a93de250111e5d5addfb4
[dragonfly.git] / lib / libthread_xu / thread / thr_sig.c
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, INT_MAX);
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