BRL-CAD
interrupt.c
Go to the documentation of this file.
1 /* I N T E R R U P T . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2009-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 
21 /* FIXME: Should use sigaction(2) instead of BSD signal semantics for
22  * conformance, portability, and safety. */
23 #if defined(C99_POSIX_USE_BSD)
24 /* defining _BSD_SOURCE should ensure BSD signal semantics as well
25  * as sig_t for glibc on Linux according to 'man signal(2)'
26  */
27 #if !defined(_BSD_SOURCE)
28 #define _BSD_SOURCE
29 #endif
30 #endif
31 
32 #include "common.h"
33 
34 #include <signal.h>
35 
36 #include "bu/file.h"
37 #include "bu/log.h"
38 
39 /* wrap for hack above */
40 #if !defined(C99_POSIX_USE_BSD)
41 /* orig code: */
42 #ifndef HAVE_SIG_T
43 typedef void (*sig_t)(int);
44 #endif
45 #endif
46 
47 /* hard-coded maximum signal number we can defer due to array we're
48  * using for quick O(1) access in a single container for all signals.
49  */
50 #define _BU_MAX_SIGNUM 128
51 
52 /* keeps track of whether signal processing is put on hold */
53 volatile sig_atomic_t interrupt_defer_signal[_BU_MAX_SIGNUM] = {
54  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
56  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
57  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
58  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
61  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
62 };
63 
64 /* keeps track of whether a signal was received while on hold */
65 volatile sig_atomic_t interrupt_signal_pending[_BU_MAX_SIGNUM] = {
66  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
67  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
68  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
69  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
70  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
71  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
72  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
73  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
74 };
75 
76 /* keeps track of the installed signal handler that is suspended */
78  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
79  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
80  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
81  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
82  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
83  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
84  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
85  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
86 };
87 
88 
89 /* temporary signal handler to detect when a signal that has been
90  * suspended is raised.
91  */
92 static void
93 interrupt_suspend_signal_handler(int signum)
94 {
95  if (interrupt_defer_signal[signum])
96  interrupt_signal_pending[signum]++;
97 }
98 
99 
100 /**
101  * Defer signal processing for critical sections.
102  *
103  * Signal processing for a given 'signum' signal is put on hold until
104  * interrupt_restore_signal() is called. If a signal is received while
105  * suspended, it will be raised when/if the signal is restored.
106  *
107  * Returns non-zero on error (with perror set if signal() failure).
108  * Returns 1 if already suspended.
109  * Returns 2 if signal failure.
110  *
111  * This comment should be moved to bu/file.h if this HIDDEN function is
112  * publicly exposed.
113  */
114 HIDDEN int
116 {
117  BU_ASSERT(signum < _BU_MAX_SIGNUM && "signal number out of range");
118 
119  if (interrupt_signal_func[signum] == interrupt_suspend_signal_handler) {
120  return 1;
121  }
122 
123 
124 #if !defined(BRLCAD_USE_SIGACTION)
125  interrupt_signal_func[signum] = signal(signum, interrupt_suspend_signal_handler);
126 #else
127  interrupt_signal_func[signum] = signal(signum, interrupt_suspend_signal_handler);
128 #endif
129 
130  if (interrupt_signal_func[signum] == SIG_ERR) {
131  interrupt_signal_func[signum] = (sig_t)0;
132  return 2;
133  }
134  interrupt_signal_pending[signum] = 0;
135  interrupt_defer_signal[signum]++;
136 
137  return 0;
138 }
139 
140 
141 /**
142  * Restore signal processing for a given suspended signal.
143  *
144  * If a signal was raised since interrupt_suspend_signal() was called, the
145  * previously installed signal handler will be immediately called
146  * albeit only once even if multiple signals were received.
147  *
148  * Returns non-zero on error (with perror set if signal() failure).
149  * Returns 1 if unexpected suspend state.
150  * Returns 2 if signal failure.
151  *
152  * This comment should be moved to bu/file.h if this HIDDEN function is
153  * publicly exposed.
154  */
155 HIDDEN int
157 {
158  BU_ASSERT(signum < _BU_MAX_SIGNUM && "signal number out of range");
159 
160  /* must be before the test to avoid a race condition */
161  interrupt_defer_signal[signum]--;
162 
163  if (interrupt_defer_signal[signum] == 0 && interrupt_signal_pending[signum] != 0) {
164  sig_t ret;
165 
166  if (interrupt_signal_func[signum] != interrupt_suspend_signal_handler) {
167  /* unexpected state, how did we get here? */
168  return 1;
169  }
170 
171 #if !defined(BRLCAD_USE_SIGACTION)
172  ret = signal(signum, interrupt_signal_func[signum]);
173 #else
174  ret = signal(signum, interrupt_signal_func[signum]);
175 #endif
176 
177  interrupt_signal_func[signum] = (sig_t)0;
178  interrupt_signal_pending[signum] = 0;
179 
180  if (ret == SIG_ERR) {
181  return 2;
182  }
183  raise(signum);
184  }
185 
186  return 0;
187 }
188 
189 
190 int
192 {
193  int ret = 0;
194 
195 #ifdef SIGINT
196  ret += interrupt_suspend_signal(SIGINT);
197 #endif
198 #ifdef SIGHUP
199  ret += interrupt_suspend_signal(SIGHUP);
200 #endif
201 #ifdef SIGQUIT
202  ret += interrupt_suspend_signal(SIGQUIT);
203 #endif
204 #ifdef SIGTSTP
205  ret += interrupt_suspend_signal(SIGTSTP);
206 #endif
207 
208  /* should do something sensible on Windows here */
209 
210  if (ret > 0)
211  return 1;
212  return 0;
213 }
214 
215 
216 int
218 {
219  int ret = 0;
220 
221 #ifdef SIGINT
222  ret += interrupt_restore_signal(SIGINT);
223 #endif
224 #ifdef SIGHUP
225  ret += interrupt_restore_signal(SIGHUP);
226 #endif
227 #ifdef SIGQUIT
228  ret += interrupt_restore_signal(SIGQUIT);
229 #endif
230 #ifdef SIGTSTP
231  ret += interrupt_restore_signal(SIGTSTP);
232 #endif
233 
234  /* should do something sensible on Windows here */
235 
236  if (ret > 0)
237  return 1;
238  return 0;
239 }
240 
241 
242 /*
243  * Local Variables:
244  * tab-width: 8
245  * mode: C
246  * indent-tabs-mode: t
247  * c-file-style: "stroustrup"
248  * End:
249  * ex: shiftwidth=4 tabstop=8
250  */
HIDDEN int interrupt_restore_signal(int signum)
Definition: interrupt.c:156
int bu_restore_interrupts()
Definition: interrupt.c:217
void(* sig_t)(int)
Definition: interrupt.c:43
Header file for the BRL-CAD common definitions.
#define BU_ASSERT(_equation)
Definition: defines.h:216
#define HIDDEN
Definition: common.h:86
HIDDEN int interrupt_suspend_signal(int signum)
Definition: interrupt.c:115
#define _BU_MAX_SIGNUM
Definition: interrupt.c:50
volatile sig_atomic_t interrupt_signal_pending[_BU_MAX_SIGNUM]
Definition: interrupt.c:65
volatile sig_t interrupt_signal_func[_BU_MAX_SIGNUM]
Definition: interrupt.c:77
volatile sig_atomic_t interrupt_defer_signal[_BU_MAX_SIGNUM]
Definition: interrupt.c:53
int bu_suspend_interrupts()
Definition: interrupt.c:191