From 9eda24f891966cdff80a22be331876deca94a7e6 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 7 Mar 2024 03:41:07 -0500 Subject: [PATCH] macosx: Clean up single-shot timers correctly The `NSTimer` docs state that a non-repeating (aka single-shot in our terms) timer is invalidated after it fires. This means that we should not do it ourselves, and in fact it appears that the pointer itself is no longer valid, so we would be passing an `invalidate` message to a random object or segfault. --- lib/matplotlib/backends/backend_macosx.py | 4 ---- src/_macosx.m | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/backends/backend_macosx.py b/lib/matplotlib/backends/backend_macosx.py index 822cd22ba6c7..7f5589677d31 100644 --- a/lib/matplotlib/backends/backend_macosx.py +++ b/lib/matplotlib/backends/backend_macosx.py @@ -67,13 +67,9 @@ def draw_idle(self): def _single_shot_timer(self, callback): """Add a single shot timer with the given callback""" - # We need to explicitly stop and remove the timer after - # firing, otherwise segfaults will occur when trying to deallocate - # the singleshot timers. def callback_func(callback, timer): callback() self._timers.remove(timer) - timer.stop() timer = self.new_timer(interval=0) timer.single_shot = True timer.add_callback(callback_func, callback, timer) diff --git a/src/_macosx.m b/src/_macosx.m index 299fed1785a8..5f2b1ab8d195 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -1741,6 +1741,11 @@ - (void)flagsChanged:(NSEvent *)event repeats: !single block: ^(NSTimer *timer) { gil_call_method((PyObject*)self, "_on_timer"); + if (single) { + // A single-shot timer will be automatically invalidated when it fires, so + // we shouldn't do it ourselves when the object is deleted. + self->timer = NULL; + } }]; // Schedule the timer on the main run loop which is needed // when updating the UI from a background thread