From b3c99125ad30ac39d0e0ae5e3dc4894cbbbecc80 Mon Sep 17 00:00:00 2001 From: Greg Lucas Date: Mon, 28 Oct 2024 11:13:07 -0600 Subject: [PATCH] FIX/ENH: macos: dispatch timer tasks asynchronously to the main loop Previously, the timers were dependent on the length of time it took for the timer callback to execute. This dispatches the callback to the task queue to avoid synchronously waiting on long-running callback tasks. --- src/_macosx.m | 67 ++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/src/_macosx.m b/src/_macosx.m index 09838eccaf98..e95124d13e53 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -1729,6 +1729,29 @@ - (void)flagsChanged:(NSEvent *)event (void*) self, (void*)(self->timer)); } +static void +Timer__timer_stop_impl(Timer* self) +{ + if (self->timer) { + [self->timer invalidate]; + self->timer = NULL; + } +} + +static PyObject* +Timer__timer_stop(Timer* self) +{ + Timer__timer_stop_impl(self); + Py_RETURN_NONE; +} + +static void +Timer_dealloc(Timer* self) +{ + Timer__timer_stop_impl(self); + Py_TYPE(self)->tp_free((PyObject*)self); +} + static PyObject* Timer__timer_start(Timer* self, PyObject* args) { @@ -1748,19 +1771,16 @@ - (void)flagsChanged:(NSEvent *)event } // hold a reference to the timer so we can invalidate/stop it later - self->timer = [NSTimer timerWithTimeInterval: interval - 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; - } + self->timer = [NSTimer scheduledTimerWithTimeInterval: interval + repeats: !single + block: ^(NSTimer *timer) { + dispatch_async(dispatch_get_main_queue(), ^{ + gil_call_method((PyObject*)self, "_on_timer"); + if (single) { + Timer__timer_stop_impl(self); + } + }); }]; - // Schedule the timer on the main run loop which is needed - // when updating the UI from a background thread - [[NSRunLoop mainRunLoop] addTimer: self->timer forMode: NSRunLoopCommonModes]; exit: Py_XDECREF(py_interval); @@ -1773,29 +1793,6 @@ - (void)flagsChanged:(NSEvent *)event } } -static void -Timer__timer_stop_impl(Timer* self) -{ - if (self->timer) { - [self->timer invalidate]; - self->timer = NULL; - } -} - -static PyObject* -Timer__timer_stop(Timer* self) -{ - Timer__timer_stop_impl(self); - Py_RETURN_NONE; -} - -static void -Timer_dealloc(Timer* self) -{ - Timer__timer_stop_impl(self); - Py_TYPE(self)->tp_free((PyObject*)self); -} - static PyTypeObject TimerType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "matplotlib.backends._macosx.Timer",