|
27 | 27 | from os import sysconf, times |
28 | 28 | from contextlib import contextmanager |
29 | 29 | from cysignals.alarm import alarm, cancel_alarm, AlarmInterrupt |
| 30 | +from cysignals.signals import python_check_interrupt |
30 | 31 |
|
31 | 32 |
|
32 | 33 | def count_noun(number, noun, plural=None, pad_number=False, pad_noun=False): |
@@ -808,6 +809,102 @@ def ensure_interruptible_after(seconds: float, max_wait_after_interrupt: float = |
808 | 809 |
|
809 | 810 | TESTS:: |
810 | 811 |
|
| 812 | + :: |
| 813 | +
|
| 814 | + sage: cython(r''' |
| 815 | + ....: from posix.unistd cimport usleep, useconds_t |
| 816 | + ....: from posix.time cimport clock_gettime, CLOCK_REALTIME, timespec |
| 817 | + ....: from cysignals.signals cimport sig_check |
| 818 | + ....: from time import time as walltime |
| 819 | + ....: from time import sleep |
| 820 | + ....: |
| 821 | + ....: cpdef sleep_(double t): usleep(<useconds_t>(t * 1e6)) # cython makes this interruptible |
| 822 | + ....: |
| 823 | + ....: cpdef void interruptible_sleep(double seconds): |
| 824 | + ....: cdef timespec start_time, target_time |
| 825 | + ....: start_walltime = walltime() |
| 826 | + ....: clock_gettime(CLOCK_REALTIME, &start_time) |
| 827 | + ....: |
| 828 | + ....: cdef int floor_seconds = <int>seconds |
| 829 | + ....: target_time.tv_sec = start_time.tv_sec + floor_seconds |
| 830 | + ....: target_time.tv_nsec = start_time.tv_nsec + <int>((seconds - floor_seconds) * 1e9) |
| 831 | + ....: if target_time.tv_nsec >= 1000000000: |
| 832 | + ....: target_time.tv_nsec -= 1000000000 |
| 833 | + ....: target_time.tv_sec += 1 |
| 834 | + ....: |
| 835 | + ....: while True: |
| 836 | + ....: sig_check() |
| 837 | + ....: clock_gettime(CLOCK_REALTIME, &start_time) |
| 838 | + ....: if start_time.tv_sec > target_time.tv_sec or (start_time.tv_sec == target_time.tv_sec and start_time.tv_nsec >= target_time.tv_nsec): |
| 839 | + ....: break |
| 840 | + ....: |
| 841 | + ....: cpdef test1(object ensure_interruptible_after): |
| 842 | + ....: with ensure_interruptible_after(2) as data: sleep(1) |
| 843 | + ....: |
| 844 | + ....: cpdef test2(object ensure_interruptible_after): |
| 845 | + ....: with ensure_interruptible_after(2) as data: sleep_(1) |
| 846 | + ....: |
| 847 | + ....: cpdef test3(object ensure_interruptible_after): |
| 848 | + ....: with ensure_interruptible_after(2) as data: interruptible_sleep(1) |
| 849 | + ....: |
| 850 | + ....: cpdef test4(object ensure_interruptible_after): |
| 851 | + ....: with ensure_interruptible_after(1) as data: sleep(3) |
| 852 | + ....: |
| 853 | + ....: cpdef test5(object ensure_interruptible_after): |
| 854 | + ....: with ensure_interruptible_after(1) as data: sleep_(3) |
| 855 | + ....: |
| 856 | + ....: cpdef test6(object ensure_interruptible_after): |
| 857 | + ....: with ensure_interruptible_after(1) as data: interruptible_sleep(3) |
| 858 | + ....: ''') |
| 859 | + sage: with ensure_interruptible_after(2) as data: sleep(1r) |
| 860 | + Traceback (most recent call last): |
| 861 | + ... |
| 862 | + RuntimeError: Function terminates early after 1.00... < 2.0000 seconds |
| 863 | + sage: with ensure_interruptible_after(2) as data: sleep_(1r) |
| 864 | + Traceback (most recent call last): |
| 865 | + ... |
| 866 | + RuntimeError: Function terminates early after 1.00... < 2.0000 seconds |
| 867 | + sage: with ensure_interruptible_after(2) as data: uninterruptible_sleep(1r) |
| 868 | + Traceback (most recent call last): |
| 869 | + ... |
| 870 | + RuntimeError: Function terminates early after 1.00... < 2.0000 seconds |
| 871 | + sage: with ensure_interruptible_after(2) as data: interruptible_sleep(1r) |
| 872 | + Traceback (most recent call last): |
| 873 | + ... |
| 874 | + RuntimeError: Function terminates early after 1.00... < 2.0000 seconds |
| 875 | + sage: with ensure_interruptible_after(1) as data: sleep(3r) |
| 876 | + sage: with ensure_interruptible_after(1) as data: sleep_(3r) |
| 877 | + sage: with ensure_interruptible_after(1) as data: uninterruptible_sleep(3r) |
| 878 | + Traceback (most recent call last): |
| 879 | + ... |
| 880 | + RuntimeError: Function is not interruptible within 1.0000 seconds, only after 3.00... seconds |
| 881 | + sage: with ensure_interruptible_after(1) as data: interruptible_sleep(3r) |
| 882 | + sage: test1(ensure_interruptible_after) |
| 883 | + Traceback (most recent call last): |
| 884 | + ... |
| 885 | + RuntimeError: Function terminates early after 1.00... < 2.0000 seconds |
| 886 | + sage: test2(ensure_interruptible_after) |
| 887 | + Traceback (most recent call last): |
| 888 | + ... |
| 889 | + RuntimeError: Function terminates early after 1.00... < 2.0000 seconds |
| 890 | + sage: test3(ensure_interruptible_after) |
| 891 | + Traceback (most recent call last): |
| 892 | + ... |
| 893 | + RuntimeError: Function terminates early after 1.00... < 2.0000 seconds |
| 894 | + sage: test4(ensure_interruptible_after) |
| 895 | + sage: test5(ensure_interruptible_after) |
| 896 | + Traceback (most recent call last): |
| 897 | + ... |
| 898 | + cysignals.signals.AlarmInterrupt |
| 899 | + sage: test6(ensure_interruptible_after) |
| 900 | + Traceback (most recent call last): |
| 901 | + ... |
| 902 | + cysignals.signals.AlarmInterrupt: |
| 903 | +
|
| 904 | + :: |
| 905 | +
|
| 906 | + :: |
| 907 | +
|
811 | 908 | sage: # we use 1r instead of 1 to avoid unexplained slowdown |
812 | 909 | sage: with ensure_interruptible_after(2) as data: sleep(1r) |
813 | 910 | Traceback (most recent call last): |
@@ -849,6 +946,7 @@ def ensure_interruptible_after(seconds: float, max_wait_after_interrupt: float = |
849 | 946 |
|
850 | 947 | try: |
851 | 948 | yield data |
| 949 | + python_check_interrupt(None, None) # equivalent to sig_check() (this is implementation detail!) |
852 | 950 | except AlarmInterrupt: |
853 | 951 | alarm_raised = True |
854 | 952 | finally: |
|
0 commit comments