]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libthr/thread/thr_sig.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / lib / libthr / 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 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         _thr_ast(curthread);
54 }
55
56 void
57 _thr_ast(struct pthread *curthread)
58 {
59         if (!THR_IN_CRITICAL(curthread)) {
60                 if (__predict_false(
61                     SHOULD_ASYNC_CANCEL(curthread->cancelflags)))
62                         _pthread_testcancel();
63                 if (__predict_false((curthread->flags &
64                     (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED))
65                         == THR_FLAGS_NEED_SUSPEND))
66                         _thr_suspend_check(curthread);
67         }
68 }
69
70 void
71 _thr_suspend_check(struct pthread *curthread)
72 {
73         umtx_t cycle;
74         int err;
75
76         err = errno;
77         /* 
78          * Blocks SIGCANCEL which other threads must send.
79          */
80         _thr_signal_block(curthread);
81
82         /*
83          * Increase critical_count, here we don't use THR_LOCK/UNLOCK
84          * because we are leaf code, we don't want to recursively call
85          * ourself.
86          */
87         curthread->critical_count++;
88         THR_UMTX_LOCK(curthread, &(curthread)->lock);
89         while ((curthread->flags & (THR_FLAGS_NEED_SUSPEND |
90                 THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND) {
91                 curthread->cycle++;
92                 cycle = curthread->cycle;
93
94                 /* Wake the thread suspending us. */
95                 _thr_umtx_wake(&curthread->cycle, INT_MAX);
96
97                 /*
98                  * if we are from pthread_exit, we don't want to
99                  * suspend, just go and die.
100                  */
101                 if (curthread->state == PS_DEAD)
102                         break;
103                 curthread->flags |= THR_FLAGS_SUSPENDED;
104                 THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
105                 _thr_umtx_wait(&curthread->cycle, cycle, NULL);
106                 THR_UMTX_LOCK(curthread, &(curthread)->lock);
107                 curthread->flags &= ~THR_FLAGS_SUSPENDED;
108         }
109         THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
110         curthread->critical_count--;
111
112         /* 
113          * Unblocks SIGCANCEL, it is possible a new SIGCANCEL is ready and
114          * a new signal frame will nest us, this seems a problem because 
115          * stack will grow and overflow, but because kernel will automatically
116          * mask the SIGCANCEL when delivering the signal, so we at most only
117          * have one nesting signal frame, this should be fine.
118          */
119         _thr_signal_unblock(curthread);
120         errno = err;
121 }
122
123 void
124 _thr_signal_init(void)
125 {
126         struct sigaction act;
127
128         /* Install cancel handler. */
129         SIGEMPTYSET(act.sa_mask);
130         act.sa_flags = SA_SIGINFO | SA_RESTART;
131         act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler;
132         __sys_sigaction(SIGCANCEL, &act, NULL);
133 }
134
135 void
136 _thr_signal_deinit(void)
137 {
138 }
139
140 __weak_reference(_sigaction, sigaction);
141
142 int
143 _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
144 {
145         /* Check if the signal number is out of range: */
146         if (sig < 1 || sig > _SIG_MAXSIG || sig == SIGCANCEL) {
147                 /* Return an invalid argument: */
148                 errno = EINVAL;
149                 return (-1);
150         }
151
152         return __sys_sigaction(sig, act, oact);
153 }
154
155 __weak_reference(_sigprocmask, sigprocmask);
156
157 int
158 _sigprocmask(int how, const sigset_t *set, sigset_t *oset)
159 {
160         const sigset_t *p = set;
161         sigset_t newset;
162
163         if (how != SIG_UNBLOCK) {
164                 if (set != NULL) {
165                         newset = *set;
166                         SIGDELSET(newset, SIGCANCEL);
167                         p = &newset;
168                 }
169         }
170         return (__sys_sigprocmask(how, p, oset));
171 }
172
173 __weak_reference(_pthread_sigmask, pthread_sigmask);
174
175 int
176 _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
177 {
178         if (_sigprocmask(how, set, oset))
179                 return (errno);
180         return (0);
181 }
182
183 __weak_reference(_sigsuspend, sigsuspend);
184
185 int
186 _sigsuspend(const sigset_t * set)
187 {
188         struct pthread *curthread = _get_curthread();
189         sigset_t newset;
190         const sigset_t *pset;
191         int oldcancel;
192         int ret;
193
194         if (SIGISMEMBER(*set, SIGCANCEL)) {
195                 newset = *set;
196                 SIGDELSET(newset, SIGCANCEL);
197                 pset = &newset;
198         } else
199                 pset = set;
200
201         oldcancel = _thr_cancel_enter(curthread);
202         ret = __sys_sigsuspend(pset);
203         _thr_cancel_leave(curthread, oldcancel);
204
205         return (ret);
206 }
207
208 __weak_reference(__sigwait, sigwait);
209 __weak_reference(__sigtimedwait, sigtimedwait);
210 __weak_reference(__sigwaitinfo, sigwaitinfo);
211
212 int
213 __sigtimedwait(const sigset_t *set, siginfo_t *info,
214         const struct timespec * timeout)
215 {
216         struct pthread  *curthread = _get_curthread();
217         sigset_t newset;
218         const sigset_t *pset;
219         int oldcancel;
220         int ret;
221
222         if (SIGISMEMBER(*set, SIGCANCEL)) {
223                 newset = *set;
224                 SIGDELSET(newset, SIGCANCEL);
225                 pset = &newset;
226         } else
227                 pset = set;
228         oldcancel = _thr_cancel_enter(curthread);
229         ret = __sys_sigtimedwait(pset, info, timeout);
230         _thr_cancel_leave(curthread, oldcancel);
231         return (ret);
232 }
233
234 int
235 __sigwaitinfo(const sigset_t *set, siginfo_t *info)
236 {
237         struct pthread  *curthread = _get_curthread();
238         sigset_t newset;
239         const sigset_t *pset;
240         int oldcancel;
241         int ret;
242
243         if (SIGISMEMBER(*set, SIGCANCEL)) {
244                 newset = *set;
245                 SIGDELSET(newset, SIGCANCEL);
246                 pset = &newset;
247         } else
248                 pset = set;
249
250         oldcancel = _thr_cancel_enter(curthread);
251         ret = __sys_sigwaitinfo(pset, info);
252         _thr_cancel_leave(curthread, oldcancel);
253         return (ret);
254 }
255
256 int
257 __sigwait(const sigset_t *set, int *sig)
258 {
259         struct pthread  *curthread = _get_curthread();
260         sigset_t newset;
261         const sigset_t *pset;
262         int oldcancel;
263         int ret;
264
265         if (SIGISMEMBER(*set, SIGCANCEL)) {
266                 newset = *set;
267                 SIGDELSET(newset, SIGCANCEL);
268                 pset = &newset;
269         } else 
270                 pset = set;
271
272         oldcancel = _thr_cancel_enter(curthread);
273         ret = __sys_sigwait(pset, sig);
274         _thr_cancel_leave(curthread, oldcancel);
275         return (ret);
276 }