Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/c_lib/include/interrupt.h
4024 views
1
/*
2
Interrupt and signal handling for Sage.
3
4
For documentation about how to use these, see the Developer's Guide.
5
6
7
AUTHORS:
8
9
- William Stein, Martin Albrecht (2006): initial version
10
11
- Jeroen Demeyer (2010-10-03): almost complete rewrite (#9678)
12
13
*/
14
15
/*****************************************************************************
16
* Copyright (C) 2006 William Stein <[email protected]>
17
* 2006 Martin Albrecht <[email protected]>
18
* 2010 Jeroen Demeyer <[email protected]>
19
*
20
* Distributed under the terms of the GNU General Public License (GPL)
21
* as published by the Free Software Foundation; either version 2 of
22
* the License, or (at your option) any later version.
23
* http://www.gnu.org/licenses/
24
****************************************************************************/
25
26
/* Whether or not to enable interrupt debugging (0: disable, 1: enable) */
27
#define ENABLE_DEBUG_INTERRUPT 0
28
29
30
#ifndef C_LIB_INCLUDE_INTERRUPT_H
31
#define C_LIB_INCLUDE_INTERRUPT_H
32
#include <Python.h>
33
#include <setjmp.h>
34
#include <signal.h>
35
36
#ifdef __cplusplus
37
extern "C" {
38
#endif
39
40
41
/* Declare likely() and unlikely() as in Cython */
42
#ifdef __GNUC__
43
/* Test for GCC > 2.95 */
44
#if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95))
45
#define HAVE_BUILTIN_EXPECT 1
46
#endif
47
#endif
48
49
#if HAVE_BUILTIN_EXPECT
50
#define likely(x) __builtin_expect(!!(x), 1)
51
#define unlikely(x) __builtin_expect(!!(x), 0)
52
#else
53
#define likely(x) (x)
54
#define unlikely(x) (x)
55
#endif
56
57
58
59
/* Print a C backtrace if supported by libc */
60
void print_backtrace(void);
61
62
/* Print a message s and kill ourselves with signal sig */
63
void sigdie(int sig, const char* s);
64
65
66
/*
67
* The signal handlers for Sage, one for SIGINT and one for other
68
* signals.
69
* Inside sig_on() (i.e. when _signals.sig_on_count is positive), this
70
* raises an exception and jumps back to sig_on().
71
* Outside of sig_on(), sage_interrupt_handler() sets Python's
72
* interrupt flag using PyErr_SetInterrupt(); sage_signal_handler()
73
* terminates Sage.
74
*/
75
void sage_interrupt_handler(int sig); /* SIGINT */
76
void sage_signal_handler(int sig); /* Other signals */
77
78
/* Wrapper to call sage_interrupt_handler() "by hand". */
79
void call_sage_interrupt_handler(int sig);
80
81
/*
82
* Setup the signal handlers for SIGINT, SIGILL, SIGABRT, SIGFPE
83
* SIGBUS, SIGSEGV. It is safe to call this more than once.
84
*
85
* We do not handle SIGALRM since there is code to deal with
86
* alarms in sage/misc/misc.py
87
*/
88
void setup_sage_signal_handler(void);
89
90
91
/**********************************************************************
92
* SAGE_SIGNALS_T STRUCTURE *
93
**********************************************************************/
94
95
/* All the state of the signal handler is in this struct. */
96
struct sage_signals_t
97
{
98
/* Reference counter for sig_on().
99
* If this is strictly positive, we are inside a sig_on(). */
100
volatile sig_atomic_t sig_on_count;
101
102
/* If this is nonzero, check for interrupts using PyErr_Occured()
103
* during sig_on() and sig_unblock(). This value is increased
104
* whenever an interrupt happens outside of sig_on() or inside
105
* sig_block(). */
106
volatile sig_atomic_t interrupt_received;
107
108
/* Are we currently handling a signal inside sage_signal_handler()?
109
* This is set to 1 on entry in sage_signal_handler (not in
110
* sage_interrupt_handler) and 0 in _sig_on_postjmp. This is
111
* needed to check for signals raised within the signal handler. */
112
volatile sig_atomic_t inside_signal_handler;
113
114
/* Non-zero if we currently are in a function which blocks SIGINT,
115
* zero normally. See sig_block(), sig_unblock(). */
116
volatile sig_atomic_t block_sigint;
117
118
/* A jump buffer holding where to siglongjmp() after a signal has
119
* been received. This is set by sig_on(). */
120
sigjmp_buf env;
121
122
/* An optional string may be passed to the signal handler which
123
* will be used as the text for the exception. This can be set
124
* using sig_str() instead of sig_on() or it can be changed by
125
* set_sage_signal_handler_message() declared below.
126
*/
127
const char* s;
128
};
129
130
/*
131
* The actual object (there is a unique copy of this throughout Sage).
132
*/
133
extern struct sage_signals_t _signals;
134
135
136
/**********************************************************************
137
* IMPLEMENTATION OF SIG_ON/SIG_OFF *
138
**********************************************************************/
139
140
/*
141
* Implementation of sig_on(). Applications should not use this
142
* directly, use sig_on() or sig_str() instead.
143
*
144
* _sig_on_(message) is a macro which pretends to be a function.
145
* Since this is declared as "cdef except 0", Cython will know that an
146
* exception occured if the value of _sig_on_() is 0 (false).
147
*
148
* INPUT:
149
*
150
* - message -- a string to be displayed as error message when the code
151
* between sig_on() and sig_off() fails and raises an exception.
152
*
153
* OUTPUT: zero if an exception occured, non-zero otherwise.
154
*
155
* The function sigsetjmp() in the _sig_on_() macro can return:
156
* - zero: this happens in the actual sig_on() call. sigsetjmp() sets
157
* up the address for the Sage signal handler to jump to. The
158
* program continues normally.
159
* - a signal number (e.g. 2 for SIGINT), assumed to be strictly
160
* positive: the Sage signal handler handled a signal. Since
161
* _sig_on_() will return 0 in this case, the Exception (raised by
162
* sage_signal_handler) will be detected by Cython.
163
* - a negative number: this is assumed to come from sig_retry(). In
164
* this case, the program continues as if nothing happened between
165
* sig_on() and sig_retry().
166
*
167
* We cannot simply put sigsetjmp() in a function, because when that
168
* function returns, we would lose the stack frame to siglongjmp() to.
169
* That's why we need this hackish macro. We use the fact that || is
170
* a short-circuiting operator (the second argument is only evaluated
171
* if the first returns 0).
172
*/
173
#define _sig_on_(message) ( unlikely(_sig_on_prejmp(message, __FILE__, __LINE__)) || _sig_on_postjmp(sigsetjmp(_signals.env,0)) )
174
175
/* This will be called during _sig_on_postjmp() when a SIGINT was
176
* received *before* the call to sig_on().
177
* Return 0 if there was an interrupt, 1 otherwise. */
178
int _sig_on_interrupt_received(void);
179
180
/*
181
* Set message, return 0 if we need to sigsetjmp(), return 1 otherwise.
182
*/
183
static inline int _sig_on_prejmp(const char* message, const char* file, int line)
184
{
185
_signals.s = message;
186
#if ENABLE_DEBUG_INTERRUPT
187
fprintf(stderr, "sig_on (counter = %i) at %s:%i\n", _signals.sig_on_count+1, file, line);
188
fflush(stderr);
189
#endif
190
if (_signals.sig_on_count > 0)
191
{
192
_signals.sig_on_count++;
193
return 1;
194
}
195
196
/* At this point, _signals.sig_on_count == 0 */
197
return 0;
198
}
199
200
201
/* Cleanup after siglongjmp() (reset signal mask to the default, set
202
* sig_on_count to zero) */
203
void _sig_on_recover(void);
204
205
/*
206
* Process the return value of sigsetjmp().
207
* Return 0 if there was an exception, 1 otherwise.
208
*/
209
static inline int _sig_on_postjmp(int jmpret)
210
{
211
if (unlikely(jmpret > 0))
212
{
213
/* An exception occured */
214
_sig_on_recover();
215
return 0;
216
}
217
218
/* When we are here, it's either the original sig_on() call or we
219
* got here after sig_retry(). */
220
_signals.sig_on_count = 1;
221
222
/* Check whether we received an interrupt before this point.
223
* _signals.interrupt_received can only be set by the interrupt
224
* handler if _signals.sig_on_count is zero. Because of that and
225
* because _signals.sig_on_count and _signals.interrupt_received are
226
* volatile, we can safely evaluate _signals.interrupt_received here
227
* without race conditions. */
228
if (unlikely(_signals.interrupt_received))
229
return _sig_on_interrupt_received();
230
231
return 1;
232
}
233
234
/* Give a warning that sig_off() was called without sig_on() */
235
void _sig_off_warning(const char* file, int line);
236
237
/*
238
* Implementation of sig_off(). Applications should not use this
239
* directly, use sig_off() instead.
240
*/
241
static inline void _sig_off_(const char* file, int line)
242
{
243
#if ENABLE_DEBUG_INTERRUPT
244
fprintf(stderr, "sig_off (counter = %i) at %s:%i\n", _signals.sig_on_count, file, line);
245
fflush(stderr);
246
#endif
247
if (unlikely(_signals.sig_on_count <= 0))
248
{
249
_sig_off_warning(file, line);
250
}
251
else
252
{
253
--_signals.sig_on_count;
254
}
255
}
256
257
258
259
/**********************************************************************
260
* USER MACROS/FUNCTIONS *
261
**********************************************************************/
262
263
/* The actual macros which should be used in a program. */
264
#define sig_on() _sig_on_(NULL)
265
#define sig_str(message) _sig_on_(message)
266
#define sig_off() _sig_off_(__FILE__, __LINE__)
267
268
/* These deprecated macros provide backwards compatibility with
269
* sage-4.6 and earlier */
270
#define _sig_on {if (!_sig_on_(NULL)) return 0;}
271
#define _sig_str(s) {if (!_sig_on_(s)) return 0;}
272
#define _sig_off {_sig_off_(__FILE__, __LINE__);}
273
274
275
/* sig_check() should be functionally equivalent to sig_on(); sig_off();
276
* but much faster. Essentially, it checks whether we missed any
277
* interrupts.
278
*
279
* OUTPUT: zero if an interrupt occured, non-zero otherwise.
280
*/
281
static inline int sig_check()
282
{
283
if (unlikely(_signals.interrupt_received) && _signals.sig_on_count == 0)
284
return _sig_on_interrupt_received();
285
286
return 1;
287
}
288
289
/* Macros behaving exactly like sig_on, sig_str and sig_check
290
* but which are *not* declared cdef except 0. This is useful if some
291
* Cython code wants to do its own exception handling. */
292
#define sig_on_no_except() sig_on()
293
#define sig_str_no_except(message) sig_str(message)
294
#define sig_check_no_except() sig_check()
295
296
297
/*
298
* Temporarily block interrupts from happening inside sig_on(). This
299
* is meant to wrap malloc() for example. sig_unblock() checks whether
300
* an interrupt happened in the mean time. If yes, the interrupt is
301
* re-raised.
302
*
303
* NOTES:
304
* - This only works inside sig_on()/sig_off(). Outside of sig_on(),
305
* interrupts behave as usual. This is because we can't propagate
306
* Python exceptions from low-level C code.
307
* - Other signals still go through, because we can't really ignore
308
* SIGSEGV for example.
309
* - For efficiency reasons, currently these may NOT be nested.
310
* Nesting could be implemented like src/headers/pariinl.h in PARI.
311
*/
312
static inline void sig_block()
313
{
314
_signals.block_sigint = 1;
315
}
316
317
static inline void sig_unblock()
318
{
319
_signals.block_sigint = 0;
320
321
if (unlikely(_signals.interrupt_received) && _signals.sig_on_count > 0)
322
kill(getpid(), SIGINT); /* Re-raise the interrupt */
323
}
324
325
326
/*
327
* Call this before raising an exception to set _signals.s. The string
328
* s will be used as the text for the exception. Note that s is not
329
* copied, we just store the pointer.
330
*/
331
void set_sage_signal_handler_message(const char* s);
332
333
334
/*
335
* Retry a failed computation starting from sig_on(). This is useful
336
* for PARI: if PARI complains that it doesn't have enough memory, we
337
* allocate a larger stack and retry the computation.
338
*/
339
static inline void sig_retry()
340
{
341
/* If we're outside of sig_on(), we can't jump, so we can only bail
342
* out */
343
if (unlikely(_signals.sig_on_count <= 0))
344
{
345
fprintf(stderr, "sig_retry() without sig_on()\n");
346
abort();
347
}
348
siglongjmp(_signals.env, -1);
349
}
350
351
352
/*
353
* This function does nothing, but it is declared cdef except *, so it
354
* can be used to make Cython check whether there is a pending exception
355
* (PyErr_Occurred() is non-NULL).
356
* To Cython, it will look like cython_check_exception() actually
357
* raised the exception.
358
*/
359
static inline void cython_check_exception() {return;}
360
361
362
#ifdef __cplusplus
363
} /* extern "C" */
364
#endif
365
#endif /* C_LIB_INCLUDE_INTERRUPT_H */
366
367