@@ -766,49 +766,59 @@ def ensure_interruptible_after(seconds: float, max_wait_after_interrupt: float =
766766
767767 ``as data`` is optional, but if it is used, it will contain a few useful values::
768768
769- sage: data # abs tol 0.2
769+ sage: data # abs tol 0.01
770770 {'alarm_raised': True, 'elapsed': 1.0}
771771
772772 ``max_wait_after_interrupt`` can be passed if the function may take longer than usual to be interrupted::
773773
774- sage: cython('''
775- ....: from libc .time cimport clock_t, clock, CLOCKS_PER_SEC
774+ sage: cython(r '''
775+ ....: from posix .time cimport clock_gettime, CLOCK_REALTIME, timespec
776776 ....: from cysignals.signals cimport sig_check
777+ ....: from time import time as walltime
778+ ....:
777779 ....: cpdef void uninterruptible_sleep(double seconds):
778- ....: cdef clock_t target = clock() + <clock_t>(CLOCKS_PER_SEC * seconds)
779- ....: while clock() < target:
780- ....: pass
780+ ....: cdef timespec start_time, target_time
781+ ....: start_walltime = walltime()
782+ ....: clock_gettime(CLOCK_REALTIME, &start_time)
783+ ....:
784+ ....: cdef int floor_seconds = <int>seconds
785+ ....: target_time.tv_sec = start_time.tv_sec + floor_seconds
786+ ....: target_time.tv_nsec = start_time.tv_nsec + <int>((seconds - floor_seconds) * 1e9)
787+ ....: if target_time.tv_nsec >= 1000000000:
788+ ....: target_time.tv_nsec -= 1000000000
789+ ....: target_time.tv_sec += 1
790+ ....:
791+ ....: while True:
792+ ....: clock_gettime(CLOCK_REALTIME, &start_time)
793+ ....: 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):
794+ ....: break
795+ ....:
781796 ....: cpdef void check_interrupt_only_occasionally():
782797 ....: for i in range(10):
783798 ....: uninterruptible_sleep(0.8)
784799 ....: sig_check()
785800 ....: ''')
786- sage: with ensure_interruptible_after(1) as data : # not passing max_wait_after_interrupt will raise an error
801+ sage: with ensure_interruptible_after(1): # not passing max_wait_after_interrupt will raise an error
787802 ....: check_interrupt_only_occasionally()
788803 Traceback (most recent call last):
789804 ...
790- RuntimeError: Function is not interruptible within 1.0000 seconds, only after 1... seconds
805+ RuntimeError: Function is not interruptible within 1.0000 seconds, only after 1.60. .. seconds
791806 sage: with ensure_interruptible_after(1, max_wait_after_interrupt=0.9):
792807 ....: check_interrupt_only_occasionally()
793808
794809 TESTS::
795810
796- sage: data['elapsed'] # abs tol 0.3 # 1.6 = 0.8 * 2
797- 1.6
798-
799- ::
800-
801811 sage: with ensure_interruptible_after(2) as data: sleep(1)
802812 Traceback (most recent call last):
803813 ...
804- RuntimeError: Function terminates early after 1... < 2.0000 seconds
805- sage: data # abs tol 0.2
814+ RuntimeError: Function terminates early after 1.00. .. < 2.0000 seconds
815+ sage: data # abs tol 0.01
806816 {'alarm_raised': False, 'elapsed': 1.0}
807817 sage: with ensure_interruptible_after(1) as data: raise ValueError
808818 Traceback (most recent call last):
809819 ...
810820 ValueError
811- sage: data # abs tol 0.2
821+ sage: data # abs tol 0.01
812822 {'alarm_raised': False, 'elapsed': 0.0}
813823
814824 ::
@@ -817,16 +827,20 @@ def ensure_interruptible_after(seconds: float, max_wait_after_interrupt: float =
817827 sage: with ensure_interruptible_after(1) as data: uninterruptible_sleep(2)
818828 Traceback (most recent call last):
819829 ...
820- RuntimeError: Function is not interruptible within 1.0000 seconds, only after 2... seconds
821- sage: data # abs tol 0.2
830+ RuntimeError: Function is not interruptible within 1.0000 seconds, only after 2.00. .. seconds
831+ sage: data # abs tol 0.01
822832 {'alarm_raised': True, 'elapsed': 2.0}
823833 sage: with ensure_interruptible_after(1): uninterruptible_sleep(2); raise RuntimeError
824834 Traceback (most recent call last):
825835 ...
826- RuntimeError: Function is not interruptible within 1.0000 seconds, only after 2... seconds
827- sage: data # abs tol 0.2
836+ RuntimeError: Function is not interruptible within 1.0000 seconds, only after 2.00. .. seconds
837+ sage: data # abs tol 0.01
828838 {'alarm_raised': True, 'elapsed': 2.0}
829839 """
840+ seconds = float (seconds )
841+ max_wait_after_interrupt = float (max_wait_after_interrupt )
842+ inaccuracy_tolerance = float (inaccuracy_tolerance )
843+ # use Python float to avoid unexplained slowdown with Sage objects
830844 data = {}
831845 start_time = walltime ()
832846 alarm (seconds )
@@ -837,6 +851,7 @@ def ensure_interruptible_after(seconds: float, max_wait_after_interrupt: float =
837851 except AlarmInterrupt :
838852 alarm_raised = True
839853 finally :
854+ before_cancel_alarm_elapsed = walltime () - start_time
840855 cancel_alarm ()
841856 elapsed = walltime () - start_time
842857 data ["elapsed" ] = elapsed
0 commit comments