drm/linux: Improve put_user()
[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 #endif
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 int     __sigwait(const sigset_t *set, int *sig);
51 int     __sigwaitinfo(const sigset_t *set, siginfo_t *info);
52 int     __sigtimedwait(const sigset_t *set, siginfo_t *info,
53                 const struct timespec * timeout);
54
55 #if defined(_PTHREADS_DEBUGGING) || defined(_PTHREADS_DEBUGGING2)
56 static void _thr_debug_sig(int signo);
57 #endif
58
59 static void
60 sigcancel_handler(int sig __unused, siginfo_t *info __unused,
61         ucontext_t *ucp __unused)
62 {
63         struct pthread *curthread = tls_get_curthread();
64
65         if (curthread->cancelflags & THR_CANCEL_AT_POINT)
66                 _pthread_testcancel();
67         _thr_ast(curthread);
68 }
69
70 void
71 _thr_ast(struct pthread *curthread)
72 {
73         if (!THR_IN_CRITICAL(curthread)) {
74                 if (__predict_false((curthread->flags &
75                     (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED))
76                         == THR_FLAGS_NEED_SUSPEND))
77                         _thr_suspend_check(curthread);
78         }
79 }
80
81 void
82 _thr_suspend_check(struct pthread *curthread)
83 {
84         umtx_t cycle;
85
86         /*
87          * Blocks SIGCANCEL which other threads must send.
88          */
89         _thr_signal_block(curthread);
90
91         /*
92          * Increase critical_count, here we don't use THR_LOCK/UNLOCK
93          * because we are leaf code, we don't want to recursively call
94          * ourself.
95          */
96         curthread->critical_count++;
97         THR_UMTX_LOCK(curthread, &(curthread)->lock);
98         while ((curthread->flags & (THR_FLAGS_NEED_SUSPEND |
99                 THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND) {
100                 curthread->cycle++;
101                 cycle = curthread->cycle;
102
103                 /* Wake the thread suspending us. */
104                 _thr_umtx_wake(&curthread->cycle, 0);
105
106                 /*
107                  * if we are from pthread_exit, we don't want to
108                  * suspend, just go and die.
109                  */
110                 if (curthread->state == PS_DEAD)
111                         break;
112                 curthread->flags |= THR_FLAGS_SUSPENDED;
113                 THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
114                 _thr_umtx_wait(&curthread->cycle, cycle, NULL, 0);
115                 THR_UMTX_LOCK(curthread, &(curthread)->lock);
116                 curthread->flags &= ~THR_FLAGS_SUSPENDED;
117         }
118         THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
119         curthread->critical_count--;
120
121         _thr_signal_unblock(curthread);
122 }
123
124 void
125 _thr_signal_init(void)
126 {
127         struct sigaction act;
128
129         /* Install cancel handler. */
130         SIGEMPTYSET(act.sa_mask);
131         act.sa_flags = SA_SIGINFO | SA_RESTART;
132         act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler;
133         __sys_sigaction(SIGCANCEL, &act, NULL);
134
135 #if defined(_PTHREADS_DEBUGGING) || defined(_PTHREADS_DEBUGGING2)
136         /*
137          * If enabled, rwlock, mutex, and condition variable operations
138          * are recorded in a text buffer and signal 63 dumps the buffer
139          * to /tmp/cond${pid}.log.
140          */
141         act.sa_flags = SA_RESTART;
142         act.sa_handler = _thr_debug_sig;
143         __sys_sigaction(63, &act, NULL);
144 #endif
145 }
146
147 void
148 _thr_signal_deinit(void)
149 {
150 }
151
152 int
153 _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
154 {
155         /* Check if the signal number is out of range: */
156         if (sig < 1 || sig > _SIG_MAXSIG || sig == SIGCANCEL) {
157                 /* Return an invalid argument: */
158                 errno = EINVAL;
159                 return (-1);
160         }
161
162         return __sys_sigaction(sig, act, oact);
163 }
164
165 __strong_reference(_sigaction, sigaction);
166
167 int
168 _sigprocmask(int how, const sigset_t *set, sigset_t *oset)
169 {
170         const sigset_t *p = set;
171         sigset_t newset;
172
173         if (how != SIG_UNBLOCK) {
174                 if (set != NULL) {
175                         newset = *set;
176                         SIGDELSET(newset, SIGCANCEL);
177                         p = &newset;
178                 }
179         }
180         return (__sys_sigprocmask(how, p, oset));
181 }
182
183 __strong_reference(_sigprocmask, sigprocmask);
184
185 int
186 _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
187 {
188         if (_sigprocmask(how, set, oset))
189                 return (errno);
190         return (0);
191 }
192
193 __strong_reference(_pthread_sigmask, pthread_sigmask);
194
195 int
196 _sigsuspend(const sigset_t * set)
197 {
198         struct pthread *curthread = tls_get_curthread();
199         sigset_t newset;
200         const sigset_t *pset;
201         int oldcancel;
202         int ret;
203
204         if (SIGISMEMBER(*set, SIGCANCEL)) {
205                 newset = *set;
206                 SIGDELSET(newset, SIGCANCEL);
207                 pset = &newset;
208         } else
209                 pset = set;
210
211         oldcancel = _thr_cancel_enter(curthread);
212         ret = __sys_sigsuspend(pset);
213         _thr_cancel_leave(curthread, oldcancel);
214
215         return (ret);
216 }
217
218 __strong_reference(_sigsuspend, sigsuspend);
219
220 int
221 __sigtimedwait(const sigset_t *set, siginfo_t *info,
222         const struct timespec * timeout)
223 {
224         struct pthread  *curthread = tls_get_curthread();
225         sigset_t newset;
226         const sigset_t *pset;
227         int oldcancel;
228         int ret;
229
230         if (SIGISMEMBER(*set, SIGCANCEL)) {
231                 newset = *set;
232                 SIGDELSET(newset, SIGCANCEL);
233                 pset = &newset;
234         } else
235                 pset = set;
236         oldcancel = _thr_cancel_enter(curthread);
237         ret = __sys_sigtimedwait(pset, info, timeout);
238         _thr_cancel_leave(curthread, oldcancel);
239         return (ret);
240 }
241
242 __strong_reference(__sigtimedwait, sigtimedwait);
243
244 int
245 __sigwaitinfo(const sigset_t *set, siginfo_t *info)
246 {
247         struct pthread  *curthread = tls_get_curthread();
248         sigset_t newset;
249         const sigset_t *pset;
250         int oldcancel;
251         int ret;
252
253         if (SIGISMEMBER(*set, SIGCANCEL)) {
254                 newset = *set;
255                 SIGDELSET(newset, SIGCANCEL);
256                 pset = &newset;
257         } else
258                 pset = set;
259
260         oldcancel = _thr_cancel_enter(curthread);
261         ret = __sys_sigwaitinfo(pset, info);
262         _thr_cancel_leave(curthread, oldcancel);
263         return (ret);
264 }
265
266 __strong_reference(__sigwaitinfo, sigwaitinfo);
267
268 int
269 __sigwait(const sigset_t *set, int *sig)
270 {
271         int s;
272
273         s = __sigwaitinfo(set, NULL);
274         if (s > 0) {
275                 *sig = s;
276                 return (0);
277         }
278         return (errno);
279 }
280
281 __strong_reference(__sigwait, sigwait);
282
283 #if defined(_PTHREADS_DEBUGGING) || defined(_PTHREADS_DEBUGGING2)
284
285 #define LOGBUF_SIZE     (4 * 1024 * 1024)
286 #define LOGBUF_MASK     (LOGBUF_SIZE - 1)
287
288 char LogBuf[LOGBUF_SIZE];
289 unsigned long LogWIndex;
290
291 void
292 _thr_log(const char *buf, size_t bytes)
293 {
294         struct pthread *curthread;
295         unsigned long i;
296         char prefix[32];
297         size_t plen;
298
299         curthread = tls_get_curthread();
300         if (curthread) {
301                 plen = snprintf(prefix, sizeof(prefix), "%d.%d: ",
302                                 (int)__sys_getpid(),
303                                 curthread->tid);
304         } else {
305                 plen = snprintf(prefix, sizeof(prefix), "unknown: ");
306         }
307
308         if (bytes == 0)
309                 bytes = strlen(buf);
310         i = atomic_fetchadd_long(&LogWIndex, plen + bytes);
311         i = i & LOGBUF_MASK;
312         if (plen <= (size_t)(LOGBUF_SIZE - i)) {
313                 bcopy(prefix, LogBuf + i, plen);
314         } else {
315                 bcopy(prefix, LogBuf + i, LOGBUF_SIZE - i);
316                 plen -= LOGBUF_SIZE - i;
317                 bcopy(prefix, LogBuf, plen);
318         }
319
320         i += plen;
321         i = i & LOGBUF_MASK;
322         if (bytes <= (size_t)(LOGBUF_SIZE - i)) {
323                 bcopy(buf, LogBuf + i, bytes);
324         } else {
325                 bcopy(buf, LogBuf + i, LOGBUF_SIZE - i);
326                 bytes -= LOGBUF_SIZE - i;
327                 bcopy(buf, LogBuf, bytes);
328         }
329 }
330
331 static void
332 _thr_debug_sig(int signo __unused)
333 {
334         char buf[256];
335         int fd;
336         unsigned long i;
337
338         snprintf(buf, sizeof(buf), "/tmp/cond%d.log", (int)__sys_getpid());
339         fd = open(buf, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0666);
340         if (fd >= 0) {
341                 i = LogWIndex;
342                 if (i < LOGBUF_SIZE) {
343                         write(fd, LogBuf, i);
344                 } else {
345                         i &= LOGBUF_MASK;
346                         write(fd, LogBuf + i, LOGBUF_SIZE - i);
347                         write(fd, LogBuf, i);
348                 }
349                 close(fd);
350         }
351 }
352
353 #endif