-
-
Notifications
You must be signed in to change notification settings - Fork 8.5k
Timer consistency across backends #29062
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
233fc2a
94b8ae4
13289af
c1c8e7a
5f0359e
1408a72
e33ad7b
7d72960
d06ddda
f9dbbc7
09f22ce
ff67029
6842ed2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1754,6 +1754,15 @@ - (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_start(Timer* self, PyObject* args) | ||
| { | ||
|
|
@@ -1772,20 +1781,21 @@ - (void)flagsChanged:(NSEvent *)event | |
| goto exit; | ||
| } | ||
|
|
||
| // Stop the current timer if it is already running | ||
| Timer__timer_stop_impl(self); | ||
| // 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"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a rare crash.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: I ended up implementing an Objective-C class that mimics the behavior of a QTimer - changing the interval of a running timer will restart, I wrote it with compatibility with this PR in mind so it should (hopefully) not conflict when this PR lands someday. |
||
| 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 | ||
| [[NSRunLoop mainRunLoop] addTimer: self->timer forMode: NSRunLoopCommonModes]; | ||
|
|
||
| exit: | ||
| Py_XDECREF(py_interval); | ||
|
|
@@ -1798,19 +1808,22 @@ - (void)flagsChanged:(NSEvent *)event | |
| } | ||
| } | ||
|
|
||
| static void | ||
| Timer__timer_stop_impl(Timer* self) | ||
| static PyObject* | ||
| Timer__timer_stop(Timer* self) | ||
| { | ||
| if (self->timer) { | ||
| [self->timer invalidate]; | ||
| self->timer = NULL; | ||
| } | ||
| Timer__timer_stop_impl(self); | ||
| Py_RETURN_NONE; | ||
| } | ||
|
|
||
| static PyObject* | ||
| Timer__timer_stop(Timer* self) | ||
| Timer__timer_update(Timer* self) | ||
| { | ||
| Timer__timer_stop_impl(self); | ||
| // stop and invalidate a timer if it is already running and then create a new one | ||
| // where the start() method retrieves the updated interval internally | ||
| if (self->timer) { | ||
| Timer__timer_stop_impl(self); | ||
| gil_call_method((PyObject*)self, "_timer_start"); | ||
| } | ||
| Py_RETURN_NONE; | ||
| } | ||
|
|
||
|
|
@@ -1840,6 +1853,12 @@ - (void)flagsChanged:(NSEvent *)event | |
| {"_timer_stop", | ||
| (PyCFunction)Timer__timer_stop, | ||
| METH_NOARGS}, | ||
| {"_timer_set_interval", | ||
| (PyCFunction)Timer__timer_update, | ||
| METH_NOARGS}, | ||
| {"_timer_set_single_shot", | ||
| (PyCFunction)Timer__timer_update, | ||
| METH_NOARGS}, | ||
| {} // sentinel | ||
| }, | ||
| }; | ||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I realize that this is an old PR, I'm going through all of the old issues and pull requests tagged with as Apple/macOS.
+scheduledTimerWithTimeInterval:…does the following:CFRunLoopGetCurrent()will create a run loop for the current thread if one does not already exist. I'm not sure if this is the behavior that you want if this is called on a background thread created from Python.That said, in the case of a background thread, the previous behavior of calling
-addTimer:forMode:on the main thread is also technically wrong - NSRunLoop is marked as a thread-unsafe class. It works, but only becauseCFRunLoopAddTimer()is currently implemented to take out a pthread mutex.I think the right behavior is to
dispatch_async()over to the main queue and then add the timer from there.