xref: /dragonfly/lib/libthread_xu/thread/thr_sig.c (revision 56f51086)
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 /* #define DEBUG_SIGNAL */
39 #ifdef DEBUG_SIGNAL
40 #define DBG_MSG		stdout_debug
41 #else
42 #define DBG_MSG(x...)
43 #endif
44 
45 int	__sigwait(const sigset_t *set, int *sig);
46 int	__sigwaitinfo(const sigset_t *set, siginfo_t *info);
47 int	__sigtimedwait(const sigset_t *set, siginfo_t *info,
48 		const struct timespec * timeout);
49 
50 static void
51 sigcancel_handler(int sig __unused, siginfo_t *info __unused,
52 	ucontext_t *ucp __unused)
53 {
54 	struct pthread *curthread = tls_get_curthread();
55 
56 	if (curthread->cancelflags & THR_CANCEL_AT_POINT)
57 		_pthread_testcancel();
58 	_thr_ast(curthread);
59 }
60 
61 void
62 _thr_ast(struct pthread *curthread)
63 {
64 	if (!THR_IN_CRITICAL(curthread)) {
65 		if (__predict_false((curthread->flags &
66 		    (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED))
67 			== THR_FLAGS_NEED_SUSPEND))
68 			_thr_suspend_check(curthread);
69 	}
70 }
71 
72 void
73 _thr_suspend_check(struct pthread *curthread)
74 {
75 	umtx_t cycle;
76 
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, 0);
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 	_thr_signal_unblock(curthread);
113 }
114 
115 void
116 _thr_signal_init(void)
117 {
118 	struct sigaction act;
119 
120 	/* Install cancel handler. */
121 	SIGEMPTYSET(act.sa_mask);
122 	act.sa_flags = SA_SIGINFO | SA_RESTART;
123 	act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler;
124 	__sys_sigaction(SIGCANCEL, &act, NULL);
125 }
126 
127 void
128 _thr_signal_deinit(void)
129 {
130 }
131 
132 int
133 _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
134 {
135 	/* Check if the signal number is out of range: */
136 	if (sig < 1 || sig > _SIG_MAXSIG || sig == SIGCANCEL) {
137 		/* Return an invalid argument: */
138 		errno = EINVAL;
139 		return (-1);
140 	}
141 
142 	return __sys_sigaction(sig, act, oact);
143 }
144 
145 __strong_reference(_sigaction, sigaction);
146 
147 int
148 _sigprocmask(int how, const sigset_t *set, sigset_t *oset)
149 {
150 	const sigset_t *p = set;
151 	sigset_t newset;
152 
153 	if (how != SIG_UNBLOCK) {
154 		if (set != NULL) {
155 			newset = *set;
156 			SIGDELSET(newset, SIGCANCEL);
157 			p = &newset;
158 		}
159 	}
160 	return (__sys_sigprocmask(how, p, oset));
161 }
162 
163 __strong_reference(_sigprocmask, sigprocmask);
164 
165 int
166 _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
167 {
168 	if (_sigprocmask(how, set, oset))
169 		return (errno);
170 	return (0);
171 }
172 
173 __strong_reference(_pthread_sigmask, pthread_sigmask);
174 
175 int
176 _sigsuspend(const sigset_t * set)
177 {
178 	struct pthread *curthread = tls_get_curthread();
179 	sigset_t newset;
180 	const sigset_t *pset;
181 	int oldcancel;
182 	int ret;
183 
184 	if (SIGISMEMBER(*set, SIGCANCEL)) {
185 		newset = *set;
186 		SIGDELSET(newset, SIGCANCEL);
187 		pset = &newset;
188 	} else
189 		pset = set;
190 
191 	oldcancel = _thr_cancel_enter(curthread);
192 	ret = __sys_sigsuspend(pset);
193 	_thr_cancel_leave(curthread, oldcancel);
194 
195 	return (ret);
196 }
197 
198 __strong_reference(_sigsuspend, sigsuspend);
199 
200 int
201 __sigtimedwait(const sigset_t *set, siginfo_t *info,
202 	const struct timespec * timeout)
203 {
204 	struct pthread	*curthread = tls_get_curthread();
205 	sigset_t newset;
206 	const sigset_t *pset;
207 	int oldcancel;
208 	int ret;
209 
210 	if (SIGISMEMBER(*set, SIGCANCEL)) {
211 		newset = *set;
212 		SIGDELSET(newset, SIGCANCEL);
213 		pset = &newset;
214 	} else
215 		pset = set;
216 	oldcancel = _thr_cancel_enter(curthread);
217 	ret = __sys_sigtimedwait(pset, info, timeout);
218 	_thr_cancel_leave(curthread, oldcancel);
219 	return (ret);
220 }
221 
222 __strong_reference(__sigtimedwait, sigtimedwait);
223 
224 int
225 __sigwaitinfo(const sigset_t *set, siginfo_t *info)
226 {
227 	struct pthread	*curthread = tls_get_curthread();
228 	sigset_t newset;
229 	const sigset_t *pset;
230 	int oldcancel;
231 	int ret;
232 
233 	if (SIGISMEMBER(*set, SIGCANCEL)) {
234 		newset = *set;
235 		SIGDELSET(newset, SIGCANCEL);
236 		pset = &newset;
237 	} else
238 		pset = set;
239 
240 	oldcancel = _thr_cancel_enter(curthread);
241 	ret = __sys_sigwaitinfo(pset, info);
242 	_thr_cancel_leave(curthread, oldcancel);
243 	return (ret);
244 }
245 
246 __strong_reference(__sigwaitinfo, sigwaitinfo);
247 
248 int
249 __sigwait(const sigset_t *set, int *sig)
250 {
251 	int s;
252 
253 	s = __sigwaitinfo(set, NULL);
254 	if (s > 0) {
255 		*sig = s;
256 		return (0);
257 	}
258 	return (errno);
259 }
260 
261 __strong_reference(__sigwait, sigwait);
262