@@ -762,28 +762,27 @@ def ensure_interruptible_after(seconds: float, max_wait_after_interrupt: float =
762
762
EXAMPLES::
763
763
764
764
sage: from sage.doctest.util import ensure_interruptible_after
765
- sage: with ensure_interruptible_after(1) as data: sleep(3r )
765
+ sage: with ensure_interruptible_after(1) as data: sleep(3 )
766
766
767
767
``as data`` is optional, but if it is used, it will contain a few useful values::
768
768
769
- sage: data # abs tol 0.01
769
+ sage: data # abs tol 1
770
770
{'alarm_raised': True, 'elapsed': 1.0}
771
771
772
772
``max_wait_after_interrupt`` can be passed if the function may take longer than usual to be interrupted::
773
773
774
+ sage: # needs sage.misc.cython
774
775
sage: cython(r'''
775
- ....: from posix.time cimport clock_gettime, CLOCK_REALTIME, timespec
776
+ ....: from posix.time cimport clock_gettime, CLOCK_REALTIME, timespec, time_t
776
777
....: from cysignals.signals cimport sig_check
777
- ....: from time import time as walltime
778
778
....:
779
779
....: cpdef void uninterruptible_sleep(double seconds):
780
780
....: cdef timespec start_time, target_time
781
- ....: start_walltime = walltime()
782
781
....: clock_gettime(CLOCK_REALTIME, &start_time)
783
782
....:
784
- ....: cdef int floor_seconds = <int >seconds
783
+ ....: cdef time_t floor_seconds = <time_t >seconds
785
784
....: target_time.tv_sec = start_time.tv_sec + floor_seconds
786
- ....: target_time.tv_nsec = start_time.tv_nsec + <int >((seconds - floor_seconds) * 1e9)
785
+ ....: target_time.tv_nsec = start_time.tv_nsec + <long >((seconds - floor_seconds) * 1e9)
787
786
....: if target_time.tv_nsec >= 1000000000:
788
787
....: target_time.tv_nsec -= 1000000000
789
788
....: target_time.tv_sec += 1
@@ -808,23 +807,44 @@ def ensure_interruptible_after(seconds: float, max_wait_after_interrupt: float =
808
807
809
808
TESTS::
810
809
811
- sage: # we use 1r instead of 1 to avoid unexplained slowdown
812
- sage: with ensure_interruptible_after(2) as data: sleep(1r)
810
+ sage: with ensure_interruptible_after(2) as data: sleep(1)
813
811
Traceback (most recent call last):
814
812
...
815
- RuntimeError: Function terminates early after 1.00. .. < 2.0000 seconds
816
- sage: data # abs tol 0.01
813
+ RuntimeError: Function terminates early after 1... < 2.0000 seconds
814
+ sage: data # abs tol 1
817
815
{'alarm_raised': False, 'elapsed': 1.0}
818
- sage: with ensure_interruptible_after(1) as data: raise ValueError
819
- Traceback (most recent call last):
820
- ...
821
- ValueError
822
- sage: data # abs tol 0.01
823
- {'alarm_raised': False, 'elapsed': 0.0}
824
816
825
- ::
817
+ The test above requires a large tolerance, because both ``time.sleep`` and
818
+ ``from posix.unistd cimport usleep`` may have slowdown on the order of 0.1s on Mac,
819
+ likely because the system is idle and GitHub CI switches the program out,
820
+ and context switch back takes time. So we use busy wait instead::
826
821
827
822
sage: # needs sage.misc.cython
823
+ sage: cython(r'''
824
+ ....: from posix.time cimport clock_gettime, CLOCK_REALTIME, timespec, time_t
825
+ ....: from cysignals.signals cimport sig_check
826
+ ....:
827
+ ....: cpdef void interruptible_sleep(double seconds):
828
+ ....: cdef timespec start_time, target_time
829
+ ....: clock_gettime(CLOCK_REALTIME, &start_time)
830
+ ....:
831
+ ....: cdef time_t floor_seconds = <time_t>seconds
832
+ ....: target_time.tv_sec = start_time.tv_sec + floor_seconds
833
+ ....: target_time.tv_nsec = start_time.tv_nsec + <long>((seconds - floor_seconds) * 1e9)
834
+ ....: if target_time.tv_nsec >= 1000000000:
835
+ ....: target_time.tv_nsec -= 1000000000
836
+ ....: target_time.tv_sec += 1
837
+ ....:
838
+ ....: while True:
839
+ ....: sig_check()
840
+ ....: clock_gettime(CLOCK_REALTIME, &start_time)
841
+ ....: 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):
842
+ ....: break
843
+ ....: ''')
844
+ sage: with ensure_interruptible_after(2) as data: interruptible_sleep(1)
845
+ Traceback (most recent call last):
846
+ ...
847
+ RuntimeError: Function terminates early after 1.00... < 2.0000 seconds
828
848
sage: with ensure_interruptible_after(1) as data: uninterruptible_sleep(2)
829
849
Traceback (most recent call last):
830
850
...
@@ -837,6 +857,15 @@ def ensure_interruptible_after(seconds: float, max_wait_after_interrupt: float =
837
857
RuntimeError: Function is not interruptible within 1.0000 seconds, only after 2.00... seconds
838
858
sage: data # abs tol 0.01
839
859
{'alarm_raised': True, 'elapsed': 2.0}
860
+
861
+ ::
862
+
863
+ sage: with ensure_interruptible_after(1) as data: raise ValueError
864
+ Traceback (most recent call last):
865
+ ...
866
+ ValueError
867
+ sage: data # abs tol 0.01
868
+ {'alarm_raised': False, 'elapsed': 0.0}
840
869
"""
841
870
seconds = float (seconds )
842
871
max_wait_after_interrupt = float (max_wait_after_interrupt )
0 commit comments