Skip to content

Commit

Permalink
Change loop exit QTimer to precise
Browse files Browse the repository at this point in the history
The default QTimer is coarse, so can fire within +-5% of the time specified.
We _need_ to fire after the delay specified so that we exit _after_ schedule_stop_aborting is scheduled.
The QTimer thus needs to be a PreciseTimer.
For small values of stop_on_error_timeout I found the QTimer _still_ fired up to 5ms too early, so added 10ms of delay offset.
singleShot signature is inconsistent between PySide and PyQt, so we store a timer object, similar to ipython#990
  • Loading branch information
jdranczewski committed Feb 7, 2024
1 parent ae69c9c commit bcafdc2
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 2 deletions.
11 changes: 10 additions & 1 deletion ipykernel/eventloops.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,16 @@ def process_stream_events():
# be set from the kernel level
def _schedule_exit(delay):
"""schedule fall back to main loop in [delay] seconds"""
QtCore.QTimer.singleShot(int(1000 * delay), exit_loop)
# The signatures of QtCore.QTimer.singleShot are inconsistent between PySide and PyQt
# if setting the TimerType, so we create a timer explicitly and store it
# to avoid a memory leak.
# PreciseTimer is needed so we exit after _at least_ the specified delay, not within 5% of it
if not hasattr(kernel, "_qt_timer"):
kernel._qt_timer = QtCore.QTimer(kernel.app)
kernel._qt_timer.setSingleShot(True)
kernel._qt_timer.setTimerType(enum_helper("QtCore.Qt.TimerType").PreciseTimer)
kernel._qt_timer.timeout.connect(exit_loop)
kernel._qt_timer.start(int(1000 * delay))

loop_qt._schedule_exit = _schedule_exit

Expand Down
2 changes: 1 addition & 1 deletion ipykernel/kernelbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -1211,7 +1211,7 @@ async def stop_aborting():
# after stop_on_error_timeout, returning to the main io_loop and letting
# the call_later fire.
if self.eventloop is not None and hasattr(self.eventloop, "_schedule_exit"):
self.eventloop._schedule_exit(self.stop_on_error_timeout)
self.eventloop._schedule_exit(self.stop_on_error_timeout + 0.01)
else:
schedule_stop_aborting()

Expand Down

0 comments on commit bcafdc2

Please sign in to comment.