Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork8.1k
FIX: macos: Use standard NSApp run loop in our input hook#28981
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
greglucas commentedOct 16, 2024
Running the example from#26869 on current main and waiting for The removal of the runloop timer was to fix this issue:#27515 I think that was the wrong "fix" of the issue, and the real way to fix that issue is to remove our own input hook and yield back to Python's terminal handling. In all of these examples, we should be at minimal CPU usage when sitting still. I'm still not sure how we could test/verify this? But it would be nice to add something to make sure we don't regress on this in the future. |
src/_macosx.m Outdated
| [superclose]; | ||
| --FigureWindowCount; | ||
| if (!FigureWindowCount)[NSAppstop:self]; | ||
| if (!FigureWindowCount)stop(NULL); |
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.
This leaks theNone reference returned fromstop (at least on versions before it became immortal).
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.
👍 Added a decref to the return object now. I could change this back to the original[NSApp stop: self] and add thePyOS_InputHook = NULL call here too instead if that is better? I just figured it might be nice to try and go through one stop function everywhere we need it, although it is only in two places currently.
We want to defer back to Python's native input hook handling whenwe don't have any figures to interact with, otherwise our runloopwill continue running unecessarily.Additionally, we need to add a short runloop burst in our hotloopto avoid saturating the CPU while waiting for input. (i.e. sittingon an idle figure would keep 100% cpu while waiting for an input()event without this runloop trigger).
298243c tob1822cfComparetacaswell commentedOct 20, 2024
@ksunden and I spent a while looking at this on Friday, but not sure where that landed / if he pushed anything. |
greglucas commentedOct 21, 2024
Feel free to push directly here or open a separate PR and I'm happy to review it too. |
ksunden commentedOct 21, 2024
You can see what I have been trying inhttps://github.com/ksunden/matplotlib/tree/macos-pyos-input-hook Essentially:
|
greglucas commentedOct 21, 2024
FYI, I think v3.6.3 is where some of this logic was referenced from with the double loop and breaking out of it: Lines 149 to 169 inabcbf3d
specifically, there was the call to the cfrunloop: Line 158 inabcbf3d
which is what the "sleep" you mention is from. |
| // Short circuit if no windows are active | ||
| // Rely on Python's input handling to manage CPU usage | ||
| // This queries the NSApp, rather than using our FigureWindowCount because that is decremented when events still | ||
| // need to be processed to properly close the windows. |
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.
This will also make us play nicer with any other OSX apps you launch from Python (which I am not aware of any others....)
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.
Should we use this method in theclose() method too instead of keeping track of it ourselves?
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.
There are some folks embedding this in C++/objc it would appear:#27147
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.
That is its own bit of fun, but I think this will not intersect with that issue because the only place thePyOS_InputHook is run is from the Python repl (either readline or the new one) so if they are embedding Python in another application (which means they make an interpreter object and then stuff strings into it to run Python code) so I expect the repl will never run.
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.
oooh, just after I clicked comment I connected some more dots.
Yes, we probably should be using something like this inclose and than may fix#27147 .
src/_macosx.m Outdated
| staticvoidlazy_init(void) { | ||
| // Run our own event loop while waiting for stdin on the Python side | ||
| // this is needed to keep the application responsive while waiting for input | ||
| PyOS_InputHook = wait_for_stdin; |
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.
can probably move this back down so we are not re-setting it all the time?
greglucas left a comment
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.
Nice updates@ksunden, thanks for pushing those!
I verified that this new change fixes the slow input typing lag regression I saw before as well.
src/_macosx.m Outdated
| NSEvent* event = [NSAppnextEventMatchingMask: NSEventMaskAny | ||
| untilDate: [NSDatedistantPast] | ||
| inMode:NSDefaultRunLoopMode | ||
| dequeue:YES]; |
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.
We've been aligning on: it seems like, so suggest doing the same here and below after thenotificationID indent now.
The name of this function isflushEvents() but above you addedstop_with_event() is there a common form we want to use between them?
| NSEvent* event = [NSApp nextEventMatchingMask: NSEventMaskAny | |
| untilDate: [NSDate distantPast] | |
| inMode: NSDefaultRunLoopMode | |
| dequeue: YES]; | |
| NSEvent* event = [NSApp nextEventMatchingMask: NSEventMaskAny | |
| untilDate: [NSDate distantPast] | |
| inMode: NSDefaultRunLoopMode | |
| dequeue: YES]; |
| // Short circuit if no windows are active | ||
| // Rely on Python's input handling to manage CPU usage | ||
| // This queries the NSApp, rather than using our FigureWindowCount because that is decremented when events still | ||
| // need to be processed to properly close the windows. |
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.
Should we use this method in theclose() method too instead of keeping track of it ourselves?
tacaswell left a comment
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.
But I was on a call with Kyle as we worked through this so this should probably count as less than 1 full review.
greglucas commentedOct 30, 2024
I have run through this with a few different test cases as well and it looks good to me and is a nice simplification. It has been significantly changed by@ksunden, so I would consider it his PR now :) I "approve", although I can not technically click the button on my own PR. |
tacaswell commentedOct 30, 2024
I'm going to go ahead and merge this. |
4468488 intomatplotlib:mainUh oh!
There was an error while loading.Please reload this page.
… in our input hook
…981-on-v3.9.xBackport PR#28981 on branch v3.9.x (FIX: macos: Use standard NSApp run loop in our input hook)
PR summary
We want to defer back to Python's native input hook handling when we don't have any figures to interact with, otherwise our runloop will continue running unecessarily.
Additionally, we need to add a short runloop burst in our hotloop to avoid saturating the CPU while waiting for input. (i.e. sitting on an idle figure would keep 100% cpu while waiting for an input() event without this runloop trigger).
closes#28960
PR checklist