Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/tests/interrupt.pyx
4045 views
1
"""
2
Testing signal handling.
3
4
AUTHORS:
5
6
- Jeroen Demeyer (2010-09-29): initial version (#10030)
7
8
"""
9
#*****************************************************************************
10
# Copyright (C) 2010 Jeroen Demeyer <[email protected]>
11
#
12
# Distributed under the terms of the GNU General Public License (GPL)
13
# as published by the Free Software Foundation; either version 2 of
14
# the License, or (at your option) any later version.
15
# http://www.gnu.org/licenses/
16
#*****************************************************************************
17
18
19
import signal
20
21
cdef extern from 'stdlib.h':
22
void abort()
23
24
cdef extern from 'signal.h':
25
int SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGKILL, \
26
SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGBUS
27
28
cdef extern from '../tests/c_lib.h':
29
void ms_sleep(long ms)
30
void signal_after_delay(int signum, long ms)
31
void signals_after_delay(int signum, long ms, long interval, int n)
32
33
cdef extern from *:
34
ctypedef int volatile_int "volatile int"
35
36
37
include '../ext/interrupt.pxi'
38
include '../ext/stdsage.pxi'
39
40
41
# Default delay in milliseconds before raising signals
42
cdef long DEFAULT_DELAY = 200
43
44
45
########################################################################
46
# C helper functions #
47
########################################################################
48
cdef void infinite_loop():
49
while True:
50
pass
51
52
cdef void infinite_malloc_loop():
53
cdef size_t s = 1
54
while True:
55
sage_free(sage_malloc(s))
56
s *= 2
57
if (s > 1000000): s = 1
58
59
# Dereference a NULL pointer on purpose. This signals a SIGSEGV on most
60
# systems, but on older Mac OS X and possibly other systems, this
61
# signals a SIGBUS instead. In any case, this should give some signal.
62
cdef void dereference_null_pointer():
63
cdef long* ptr = <long*>(0)
64
ptr[0] += 1
65
66
67
########################################################################
68
# Python helper functions #
69
########################################################################
70
def raise_KeyboardInterrupt():
71
"""
72
Raise a KeyboardInterrupt.
73
74
TESTS::
75
76
sage: from sage.tests.interrupt import raise_KeyboardInterrupt
77
"""
78
raise KeyboardInterrupt, "raise test"
79
80
def try_sigint(f):
81
"""
82
Calls the Python function ``f`` and catch any KeyboardInterrupts.
83
This function is needed because the doctest program stops whenever
84
it sees a KeyboardInterrupt.
85
86
EXAMPLES::
87
88
sage: from sage.tests.interrupt import *
89
sage: try_sigint(raise_KeyboardInterrupt)
90
KeyboardInterrupt: raise test
91
"""
92
try:
93
f()
94
except KeyboardInterrupt, err:
95
print "KeyboardInterrupt:", err
96
97
def interrupt_after_delay(ms_delay = 500):
98
"""
99
Send an interrupt signal (``SIGINT``) to the Sage process
100
after a delay of ``ms_delay`` milliseconds.
101
102
INPUT:
103
104
- ``ms_delay`` -- (default: 500) a nonnegative integer indicating
105
how many milliseconds to wait before raising the interrupt signal.
106
107
EXAMPLES:
108
109
This function is meant to test interrupt functionality. We
110
demonstrate here how to test that the ``factor`` function can be
111
interrupted::
112
113
sage: import sage.tests.interrupt
114
sage: try:
115
... sage.tests.interrupt.interrupt_after_delay()
116
... factor(10^1000 + 3)
117
... except KeyboardInterrupt:
118
... print "Caught KeyboardInterrupt"
119
Caught KeyboardInterrupt
120
"""
121
signal_after_delay(SIGINT, ms_delay)
122
123
124
########################################################################
125
# Test basic macros from c_lib/headers/interrupt.h #
126
########################################################################
127
def test_sig_off():
128
"""
129
TESTS::
130
131
sage: from sage.tests.interrupt import *
132
sage: test_sig_off()
133
"""
134
sig_on()
135
sig_off()
136
137
def test_sig_on(long delay = DEFAULT_DELAY):
138
"""
139
TESTS::
140
141
sage: from sage.tests.interrupt import *
142
sage: try_sigint(test_sig_on)
143
KeyboardInterrupt:
144
"""
145
signal_after_delay(SIGINT, delay)
146
sig_on()
147
infinite_loop()
148
149
def test_sig_str(long delay = DEFAULT_DELAY):
150
"""
151
TESTS::
152
153
sage: from sage.tests.interrupt import *
154
sage: test_sig_str()
155
Traceback (most recent call last):
156
...
157
RuntimeError: Everything ok!
158
"""
159
sig_str("Everything ok!")
160
signal_after_delay(SIGABRT, delay)
161
infinite_loop()
162
163
cdef c_test_sig_on_cython():
164
sig_on()
165
infinite_loop()
166
167
def test_sig_on_cython(long delay = DEFAULT_DELAY):
168
"""
169
TESTS::
170
171
sage: from sage.tests.interrupt import *
172
sage: try_sigint(test_sig_on_cython)
173
KeyboardInterrupt:
174
"""
175
signal_after_delay(SIGINT, delay)
176
c_test_sig_on_cython()
177
178
cdef int c_test_sig_on_cython_except() except 42:
179
sig_on()
180
infinite_loop()
181
182
def test_sig_on_cython_except(long delay = DEFAULT_DELAY):
183
"""
184
TESTS::
185
186
sage: from sage.tests.interrupt import *
187
sage: try_sigint(test_sig_on_cython_except)
188
KeyboardInterrupt:
189
"""
190
signal_after_delay(SIGINT, delay)
191
c_test_sig_on_cython_except()
192
193
cdef void c_test_sig_on_cython_except_all() except *:
194
sig_on()
195
infinite_loop()
196
197
def test_sig_on_cython_except_all(long delay = DEFAULT_DELAY):
198
"""
199
TESTS::
200
201
sage: from sage.tests.interrupt import *
202
sage: try_sigint(test_sig_on_cython_except_all)
203
KeyboardInterrupt:
204
"""
205
signal_after_delay(SIGINT, delay)
206
c_test_sig_on_cython_except_all()
207
208
def test_sig_check(long delay = DEFAULT_DELAY):
209
"""
210
TESTS::
211
212
sage: from sage.tests.interrupt import *
213
sage: try_sigint(test_sig_check)
214
KeyboardInterrupt:
215
"""
216
signal_after_delay(SIGINT, delay)
217
while True:
218
sig_check()
219
220
def test_sig_check_inside_sig_on(long delay = DEFAULT_DELAY):
221
"""
222
TESTS::
223
224
sage: from sage.tests.interrupt import *
225
sage: try_sigint(test_sig_check_inside_sig_on)
226
KeyboardInterrupt:
227
"""
228
signal_after_delay(SIGINT, delay)
229
sig_on()
230
while True:
231
sig_check()
232
233
def test_sig_retry():
234
"""
235
TESTS::
236
237
sage: from sage.tests.interrupt import *
238
sage: test_sig_retry()
239
10
240
"""
241
cdef volatile_int v = 0
242
243
sig_on()
244
if v < 10:
245
v = v + 1
246
sig_retry()
247
sig_off()
248
return v
249
250
def test_sig_retry_and_signal(long delay = DEFAULT_DELAY):
251
"""
252
TESTS::
253
254
sage: from sage.tests.interrupt import *
255
sage: try_sigint(test_sig_retry_and_signal)
256
KeyboardInterrupt:
257
"""
258
cdef volatile_int v = 0
259
260
sig_on()
261
if v < 10:
262
v = v + 1
263
sig_retry()
264
signal_after_delay(SIGINT, delay)
265
infinite_loop()
266
267
########################################################################
268
# Test no_except macros #
269
########################################################################
270
def test_sig_on_no_except(long delay = DEFAULT_DELAY):
271
"""
272
TESTS::
273
274
sage: from sage.tests.interrupt import *
275
sage: test_sig_on_no_except()
276
42
277
"""
278
if not sig_on_no_except():
279
# We should never get here, because this sig_on_no_except()
280
# will not catch a signal.
281
print "Unexpected zero returned from sig_on_no_except()"
282
sig_off()
283
284
signal_after_delay(SIGINT, delay)
285
if not sig_on_no_except():
286
# We get here when we caught a signal. An exception
287
# has been raised, but Cython doesn't realize it yet.
288
try:
289
# Make Cython realize that there is an exception.
290
# To Cython, it will look like the exception was raised on
291
# the following line, so the try/except should work.
292
cython_check_exception()
293
except KeyboardInterrupt:
294
return 42
295
return 0 # fail
296
infinite_loop()
297
298
def test_sig_str_no_except(long delay = DEFAULT_DELAY):
299
"""
300
TESTS::
301
302
sage: from sage.tests.interrupt import *
303
sage: test_sig_str_no_except()
304
Traceback (most recent call last):
305
...
306
RuntimeError: Everything ok!
307
"""
308
if not sig_on_no_except():
309
# We should never get here, because this sig_on_no_except()
310
# will not catch a signal.
311
print "Unexpected zero returned from sig_on_no_except()"
312
sig_off()
313
314
if not sig_str_no_except("Everything ok!"):
315
cython_check_exception()
316
return 0 # fail
317
signal_after_delay(SIGABRT, delay)
318
infinite_loop()
319
320
321
def test_sig_check_no_except(long delay = DEFAULT_DELAY):
322
"""
323
TESTS::
324
325
sage: from sage.tests.interrupt import *
326
sage: try_sigint(test_sig_check_no_except)
327
KeyboardInterrupt:
328
"""
329
signal_after_delay(SIGINT, delay)
330
while True:
331
if not sig_check_no_except():
332
cython_check_exception()
333
return 0 # fail
334
335
336
########################################################################
337
# Test deprecated macros for backwards compatibility #
338
########################################################################
339
def test_old_sig_off():
340
"""
341
TESTS::
342
343
sage: from sage.tests.interrupt import *
344
sage: test_old_sig_off()
345
"""
346
_sig_on
347
_sig_off
348
349
def test_old_sig_on(long delay = DEFAULT_DELAY):
350
"""
351
TESTS::
352
353
sage: from sage.tests.interrupt import *
354
sage: try_sigint(test_old_sig_on)
355
KeyboardInterrupt:
356
"""
357
signal_after_delay(SIGINT, delay)
358
_sig_on
359
infinite_loop()
360
361
def test_old_sig_str(long delay = DEFAULT_DELAY):
362
"""
363
TESTS::
364
365
sage: from sage.tests.interrupt import *
366
sage: test_old_sig_str()
367
Traceback (most recent call last):
368
...
369
RuntimeError: Everything ok!
370
"""
371
_sig_str("Everything ok!")
372
signal_after_delay(SIGABRT, delay)
373
infinite_loop()
374
375
376
########################################################################
377
# Test different signals #
378
########################################################################
379
def test_signal_segv(long delay = DEFAULT_DELAY):
380
"""
381
TESTS::
382
383
sage: from sage.tests.interrupt import *
384
sage: test_signal_segv()
385
Traceback (most recent call last):
386
...
387
RuntimeError: Segmentation fault
388
"""
389
sig_on()
390
signal_after_delay(SIGSEGV, delay)
391
infinite_loop()
392
393
def test_signal_fpe(long delay = DEFAULT_DELAY):
394
"""
395
TESTS::
396
397
sage: from sage.tests.interrupt import *
398
sage: test_signal_fpe()
399
Traceback (most recent call last):
400
...
401
RuntimeError: Floating point exception
402
"""
403
sig_on()
404
signal_after_delay(SIGFPE, delay)
405
infinite_loop()
406
407
def test_signal_ill(long delay = DEFAULT_DELAY):
408
"""
409
TESTS::
410
411
sage: from sage.tests.interrupt import *
412
sage: test_signal_ill()
413
Traceback (most recent call last):
414
...
415
RuntimeError: Illegal instruction
416
"""
417
sig_on()
418
signal_after_delay(SIGILL, delay)
419
infinite_loop()
420
421
def test_signal_abrt(long delay = DEFAULT_DELAY):
422
"""
423
TESTS::
424
425
sage: from sage.tests.interrupt import *
426
sage: test_signal_abrt()
427
Traceback (most recent call last):
428
...
429
RuntimeError: Aborted
430
"""
431
sig_on()
432
signal_after_delay(SIGABRT, delay)
433
infinite_loop()
434
435
def test_signal_bus(long delay = DEFAULT_DELAY):
436
"""
437
TESTS::
438
439
sage: from sage.tests.interrupt import *
440
sage: test_signal_bus()
441
Traceback (most recent call last):
442
...
443
RuntimeError: Bus error
444
"""
445
sig_on()
446
signal_after_delay(SIGBUS, delay)
447
infinite_loop()
448
449
450
########################################################################
451
# Test with "true" errors (not signals raised by hand) #
452
########################################################################
453
def test_dereference_null_pointer():
454
"""
455
TESTS:
456
457
This test should result in either a Segmentation Fault or a Bus
458
Error. ::
459
460
sage: from sage.tests.interrupt import *
461
sage: test_dereference_null_pointer()
462
Traceback (most recent call last):
463
...
464
RuntimeError: ...
465
"""
466
sig_on()
467
dereference_null_pointer()
468
469
def unguarded_dereference_null_pointer():
470
"""
471
TESTS:
472
473
We run Sage in a subprocess and dereference a NULL pointer without
474
using ``sig_on()``. This will crash Sage::
475
476
sage: from subprocess import *
477
sage: cmd = 'from sage.tests.interrupt import *; unguarded_dereference_null_pointer()'
478
sage: print '---'; print Popen(['sage', '-c', cmd], stdout=PIPE, stderr=PIPE).communicate()[1] # long time
479
-...
480
------------------------------------------------------------------------
481
Unhandled SIG...
482
This probably occurred because a *compiled* component of Sage has a bug
483
in it and is not properly wrapped with sig_on(), sig_off(). You might
484
want to run Sage under gdb with 'sage -gdb' to debug this.
485
Sage will now terminate.
486
------------------------------------------------------------------------
487
...
488
"""
489
dereference_null_pointer()
490
491
def test_abort():
492
"""
493
TESTS::
494
495
sage: from sage.tests.interrupt import *
496
sage: test_abort()
497
Traceback (most recent call last):
498
...
499
RuntimeError: Aborted
500
"""
501
sig_on()
502
abort()
503
504
def unguarded_abort():
505
"""
506
TESTS:
507
508
We run Sage in a subprocess and make it call abort()::
509
510
sage: from subprocess import *
511
sage: cmd = 'from sage.tests.interrupt import *; unguarded_abort()'
512
sage: print '---'; print Popen(['sage', '-c', cmd], stdout=PIPE, stderr=PIPE).communicate()[1] # long time
513
-...
514
------------------------------------------------------------------------
515
Unhandled SIGABRT: An abort() occurred in Sage.
516
This probably occurred because a *compiled* component of Sage has a bug
517
in it and is not properly wrapped with sig_on(), sig_off(). You might
518
want to run Sage under gdb with 'sage -gdb' to debug this.
519
Sage will now terminate.
520
------------------------------------------------------------------------
521
...
522
"""
523
abort()
524
525
def test_bad_str(long delay = DEFAULT_DELAY):
526
"""
527
TESTS:
528
529
We run Sage in a subprocess and induce an error during the signal handler::
530
531
sage: from subprocess import *
532
sage: cmd = 'from sage.tests.interrupt import *; test_bad_str()'
533
sage: print '---'; print Popen(['sage', '-c', cmd], stdout=PIPE, stderr=PIPE).communicate()[1] # long time
534
-...
535
------------------------------------------------------------------------
536
An error occured during signal handling.
537
This probably occurred because a *compiled* component of Sage has a bug
538
in it and is not properly wrapped with sig_on(), sig_off(). You might
539
want to run Sage under gdb with 'sage -gdb' to debug this.
540
Sage will now terminate.
541
------------------------------------------------------------------------
542
...
543
"""
544
cdef char* s = <char*>(16)
545
sig_str(s)
546
signal_after_delay(SIGILL, delay)
547
infinite_loop()
548
549
550
########################################################################
551
# Test various usage scenarios for sig_on()/sig_off() #
552
########################################################################
553
def test_sig_on_cython_after_delay(long delay = DEFAULT_DELAY):
554
"""
555
TESTS::
556
557
sage: from sage.tests.interrupt import *
558
sage: try_sigint(test_sig_on_cython_after_delay)
559
KeyboardInterrupt:
560
"""
561
signal_after_delay(SIGINT, delay)
562
ms_sleep(delay * 2) # We get signaled during this sleep
563
sig_on() # The signal should be detected here
564
abort() # This should not be reached
565
566
def test_sig_on_inside_try(long delay = DEFAULT_DELAY):
567
"""
568
TESTS::
569
570
sage: from sage.tests.interrupt import *
571
sage: test_sig_on_inside_try()
572
"""
573
try:
574
sig_on()
575
signal_after_delay(SIGABRT, delay)
576
infinite_loop()
577
except RuntimeError:
578
pass
579
580
def test_interrupt_bomb(int n = 100, int p = 10):
581
"""
582
Have `p` processes each sending `n` interrupts in very quick
583
succession and see what happens :-)
584
585
TESTS::
586
587
sage: from sage.tests.interrupt import *
588
sage: test_interrupt_bomb() # long time (1200 + 5*p + 10*n milliseconds)
589
Received ... interrupts
590
"""
591
cdef int i
592
593
# Spawn p processes, each sending n signals with an interval of 1 millisecond
594
cdef long base_delay = DEFAULT_DELAY + 5*p
595
for i in range(p):
596
signals_after_delay(SIGINT, base_delay, 1, n)
597
598
# Some time later (after the smoke clears up) send a SIGABRT,
599
# which will raise RuntimeError.
600
signal_after_delay(SIGABRT, base_delay + 10*n + 1000)
601
i = 0
602
while True:
603
try:
604
sig_on()
605
infinite_loop()
606
except KeyboardInterrupt:
607
i = i + 1
608
except RuntimeError:
609
break
610
print "Received %i/%i interrupts"%(i,n*p)
611
612
def test_sig_on_loop():
613
"""
614
Test sig_on() and sig_off() in a loop, this is also useful for
615
benchmarking.
616
617
TESTS::
618
619
sage: from sage.tests.interrupt import *
620
sage: test_sig_on_loop()
621
"""
622
cdef int i
623
for i in range(10000000):
624
sig_on()
625
sig_off()
626
627
# Special thanks to Robert Bradshaw for suggesting the try/finally
628
# construction. -- Jeroen Demeyer
629
def test_try_finally_signal(long delay = DEFAULT_DELAY):
630
"""
631
Test a try/finally construct for sig_on() and sig_off(), raising
632
a signal inside the ``try``.
633
634
TESTS::
635
636
sage: from sage.tests.interrupt import *
637
sage: test_try_finally_signal()
638
Traceback (most recent call last):
639
...
640
RuntimeError: Aborted
641
"""
642
sig_on()
643
try:
644
signal_after_delay(SIGABRT, delay)
645
infinite_loop()
646
finally:
647
sig_off()
648
649
def test_try_finally_raise():
650
"""
651
Test a try/finally construct for sig_on() and sig_off(), raising
652
a Python exception inside the ``try``.
653
654
TESTS::
655
656
sage: from sage.tests.interrupt import *
657
sage: test_try_finally_raise()
658
Traceback (most recent call last):
659
...
660
RuntimeError: Everything ok!
661
"""
662
sig_on()
663
try:
664
raise RuntimeError, "Everything ok!"
665
finally:
666
sig_off()
667
668
def test_try_finally_return():
669
"""
670
Test a try/finally construct for sig_on() and sig_off(), doing a
671
normal ``return`` inside the ``try``.
672
673
TESTS::
674
675
sage: from sage.tests.interrupt import *
676
sage: test_try_finally_return()
677
'Everything ok!'
678
"""
679
sig_on()
680
try:
681
return "Everything ok!"
682
finally:
683
sig_off()
684
685
686
########################################################################
687
# Test sig_block()/sig_unblock() #
688
########################################################################
689
def test_sig_block(long delay = DEFAULT_DELAY):
690
"""
691
TESTS::
692
693
sage: from sage.tests.interrupt import *
694
sage: test_sig_block()
695
42
696
"""
697
cdef volatile_int v = 0
698
699
try:
700
sig_on()
701
except KeyboardInterrupt:
702
return v
703
704
sig_block()
705
signal_after_delay(SIGINT, delay)
706
ms_sleep(delay * 2) # We get signaled during this sleep
707
v = 42
708
sig_unblock() # Here, the interrupt will be handled
709
return 1 # Never reached
710
711
def test_sig_block_outside_sig_on(long delay = DEFAULT_DELAY):
712
"""
713
TESTS::
714
715
sage: from sage.tests.interrupt import *
716
sage: test_sig_block_outside_sig_on()
717
'Success'
718
"""
719
signal_after_delay(SIGINT, delay)
720
cdef int v = 0
721
cdef int* p = &v
722
723
# sig_block()/sig_unblock() shouldn't do anything
724
# since we're outside of sig_on()
725
sig_block()
726
ms_sleep(delay * 2) # We get signaled during this sleep
727
sig_unblock()
728
729
try:
730
sig_on() # Interrupt caught here
731
except KeyboardInterrupt:
732
return "Success"
733
abort() # This should not be reached
734
735
def test_signal_during_malloc(long delay = DEFAULT_DELAY):
736
"""
737
Test a signal arriving during a sage_malloc() or sage_free() call.
738
Since these are wrapped with sig_block()/sig_unblock(), we should
739
safely be able to interrupt them.
740
741
TESTS::
742
743
sage: from sage.tests.interrupt import *
744
sage: for i in range(4): # Several times to reduce chances of false positive
745
... test_signal_during_malloc()
746
"""
747
signal_after_delay(SIGINT, delay)
748
try:
749
sig_on()
750
infinite_malloc_loop()
751
except KeyboardInterrupt:
752
pass
753
754
755