Fix bug #9221 with memory leak in bidi display.
[emacs.git] / lib / sigprocmask.c
blob6780a37b14f0f8f77793a50719417db82693157b
1 /* POSIX compatible signal blocking.
2 Copyright (C) 2006-2011 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2006.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 #include <config.h>
20 /* Specification. */
21 #include <signal.h>
23 #include <errno.h>
24 #include <stdint.h>
25 #include <stdlib.h>
27 /* We assume that a platform without POSIX signal blocking functions
28 also does not have the POSIX sigaction() function, only the
29 signal() function. We also assume signal() has SysV semantics,
30 where any handler is uninstalled prior to being invoked. This is
31 true for Woe32 platforms. */
33 /* We use raw signal(), but also provide a wrapper rpl_signal() so
34 that applications can query or change a blocked signal. */
35 #undef signal
37 /* Provide invalid signal numbers as fallbacks if the uncatchable
38 signals are not defined. */
39 #ifndef SIGKILL
40 # define SIGKILL (-1)
41 #endif
42 #ifndef SIGSTOP
43 # define SIGSTOP (-1)
44 #endif
46 /* On native Windows, as of 2008, the signal SIGABRT_COMPAT is an alias
47 for the signal SIGABRT. Only one signal handler is stored for both
48 SIGABRT and SIGABRT_COMPAT. SIGABRT_COMPAT is not a signal of its own. */
49 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
50 # undef SIGABRT_COMPAT
51 # define SIGABRT_COMPAT 6
52 #endif
53 #ifdef SIGABRT_COMPAT
54 # define SIGABRT_COMPAT_MASK (1U << SIGABRT_COMPAT)
55 #else
56 # define SIGABRT_COMPAT_MASK 0
57 #endif
59 typedef void (*handler_t) (int);
61 /* Handling of gnulib defined signals. */
63 #if GNULIB_defined_SIGPIPE
64 static handler_t SIGPIPE_handler = SIG_DFL;
65 #endif
67 #if GNULIB_defined_SIGPIPE
68 static handler_t
69 ext_signal (int sig, handler_t handler)
71 switch (sig)
73 case SIGPIPE:
75 handler_t old_handler = SIGPIPE_handler;
76 SIGPIPE_handler = handler;
77 return old_handler;
79 default: /* System defined signal */
80 return signal (sig, handler);
83 # define signal ext_signal
84 #endif
86 int
87 sigismember (const sigset_t *set, int sig)
89 if (sig >= 0 && sig < NSIG)
91 #ifdef SIGABRT_COMPAT
92 if (sig == SIGABRT_COMPAT)
93 sig = SIGABRT;
94 #endif
96 return (*set >> sig) & 1;
98 else
99 return 0;
103 sigemptyset (sigset_t *set)
105 *set = 0;
106 return 0;
110 sigaddset (sigset_t *set, int sig)
112 if (sig >= 0 && sig < NSIG)
114 #ifdef SIGABRT_COMPAT
115 if (sig == SIGABRT_COMPAT)
116 sig = SIGABRT;
117 #endif
119 *set |= 1U << sig;
120 return 0;
122 else
124 errno = EINVAL;
125 return -1;
130 sigdelset (sigset_t *set, int sig)
132 if (sig >= 0 && sig < NSIG)
134 #ifdef SIGABRT_COMPAT
135 if (sig == SIGABRT_COMPAT)
136 sig = SIGABRT;
137 #endif
139 *set &= ~(1U << sig);
140 return 0;
142 else
144 errno = EINVAL;
145 return -1;
151 sigfillset (sigset_t *set)
153 *set = ((2U << (NSIG - 1)) - 1) & ~ SIGABRT_COMPAT_MASK;
154 return 0;
157 /* Set of currently blocked signals. */
158 static volatile sigset_t blocked_set /* = 0 */;
160 /* Set of currently blocked and pending signals. */
161 static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
163 /* Signal handler that is installed for blocked signals. */
164 static void
165 blocked_handler (int sig)
167 /* Reinstall the handler, in case the signal occurs multiple times
168 while blocked. There is an inherent race where an asynchronous
169 signal in between when the kernel uninstalled the handler and
170 when we reinstall it will trigger the default handler; oh
171 well. */
172 signal (sig, blocked_handler);
173 if (sig >= 0 && sig < NSIG)
174 pending_array[sig] = 1;
178 sigpending (sigset_t *set)
180 sigset_t pending = 0;
181 int sig;
183 for (sig = 0; sig < NSIG; sig++)
184 if (pending_array[sig])
185 pending |= 1U << sig;
186 *set = pending;
187 return 0;
190 /* The previous signal handlers.
191 Only the array elements corresponding to blocked signals are relevant. */
192 static volatile handler_t old_handlers[NSIG];
195 sigprocmask (int operation, const sigset_t *set, sigset_t *old_set)
197 if (old_set != NULL)
198 *old_set = blocked_set;
200 if (set != NULL)
202 sigset_t new_blocked_set;
203 sigset_t to_unblock;
204 sigset_t to_block;
206 switch (operation)
208 case SIG_BLOCK:
209 new_blocked_set = blocked_set | *set;
210 break;
211 case SIG_SETMASK:
212 new_blocked_set = *set;
213 break;
214 case SIG_UNBLOCK:
215 new_blocked_set = blocked_set & ~*set;
216 break;
217 default:
218 errno = EINVAL;
219 return -1;
221 to_unblock = blocked_set & ~new_blocked_set;
222 to_block = new_blocked_set & ~blocked_set;
224 if (to_block != 0)
226 int sig;
228 for (sig = 0; sig < NSIG; sig++)
229 if ((to_block >> sig) & 1)
231 pending_array[sig] = 0;
232 if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR)
233 blocked_set |= 1U << sig;
237 if (to_unblock != 0)
239 sig_atomic_t received[NSIG];
240 int sig;
242 for (sig = 0; sig < NSIG; sig++)
243 if ((to_unblock >> sig) & 1)
245 if (signal (sig, old_handlers[sig]) != blocked_handler)
246 /* The application changed a signal handler while the signal
247 was blocked, bypassing our rpl_signal replacement.
248 We don't support this. */
249 abort ();
250 received[sig] = pending_array[sig];
251 blocked_set &= ~(1U << sig);
252 pending_array[sig] = 0;
254 else
255 received[sig] = 0;
257 for (sig = 0; sig < NSIG; sig++)
258 if (received[sig])
259 raise (sig);
262 return 0;
265 /* Install the handler FUNC for signal SIG, and return the previous
266 handler. */
267 handler_t
268 rpl_signal (int sig, handler_t handler)
270 /* We must provide a wrapper, so that a user can query what handler
271 they installed even if that signal is currently blocked. */
272 if (sig >= 0 && sig < NSIG && sig != SIGKILL && sig != SIGSTOP
273 && handler != SIG_ERR)
275 #ifdef SIGABRT_COMPAT
276 if (sig == SIGABRT_COMPAT)
277 sig = SIGABRT;
278 #endif
280 if (blocked_set & (1U << sig))
282 /* POSIX states that sigprocmask and signal are both
283 async-signal-safe. This is not true of our
284 implementation - there is a slight data race where an
285 asynchronous interrupt on signal A can occur after we
286 install blocked_handler but before we have updated
287 old_handlers for signal B, such that handler A can see
288 stale information if it calls signal(B). Oh well -
289 signal handlers really shouldn't try to manipulate the
290 installed handlers of unrelated signals. */
291 handler_t result = old_handlers[sig];
292 old_handlers[sig] = handler;
293 return result;
295 else
296 return signal (sig, handler);
298 else
300 errno = EINVAL;
301 return SIG_ERR;
305 #if GNULIB_defined_SIGPIPE
306 /* Raise the signal SIG. */
308 rpl_raise (int sig)
309 # undef raise
311 switch (sig)
313 case SIGPIPE:
314 if (blocked_set & (1U << sig))
315 pending_array[sig] = 1;
316 else
318 handler_t handler = SIGPIPE_handler;
319 if (handler == SIG_DFL)
320 exit (128 + SIGPIPE);
321 else if (handler != SIG_IGN)
322 (*handler) (sig);
324 return 0;
325 default: /* System defined signal */
326 return raise (sig);
329 #endif