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