I am on Catalina and AltTab 2.3.2 and every now and then AltTab hangs when I try to use it. I am not talking about milliseconds like other issues, but several seconds where my machine becomes unusable and hitting alt-tab doesn't do anything.
I am new to Mac(coming from Linux) and I don't think I have done much customisation yet that might cause it. I also have no idea how to debug this. I am happy to help if you tell me how.
I'm having the same issue, sometimes it doesn't pop-up at all and the
default Mac command + tab UI shows. (I chose that key for mapping).
Happy to help debug in anyway I can.
On Thu, Jan 16, 2020 at 1:33 PM Sebastian Blask notifications@github.com
wrote:
I am on Catalina and AltTab 2.3.2 and every now and then AltTab hangs when
I try to use it. I am not talking about milliseconds like other issues, but
several seconds where my machine becomes unusable and hitting alt-tab
doesn't do anything.I am new to Mac(coming from Linux) and I don't think I have done much
customisation yet that might cause it. I also have no idea how to debug
this. I am happy to help if you tell me how.—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/lwouis/alt-tab-macos/issues/117?email_source=notifications&email_token=AB4CUJUA24CC5XWUJ5UW6CLQ6DHC3A5CNFSM4KH27HR2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4IGYUIAQ,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AB4CUJRBFH7ZMXXFHVYMAKDQ6DHC3ANCNFSM4KH27HRQ
.
Hi @sblask! Thanks for taking time to share this issue! To help investigate this, we need to determine when this happens - the context.
For instance, there is already #93 that exhibits potentially similar symptoms. During animations or app quit/launch, if you trigger alt-tab, the app will ask the OS about which windows are open. This is a "blocking" call, so if the OS takes a while to come back with the answer, the app is frozen since we do it on the main thread which also updates the UI.
The good news here is that I reworked the GCD/multithreading situation in the v3 PR I'm working on currently #114. This PR closes many tickets (see the description) and is a major rewrite of the app. You may want to build that branch (building this project is just git clone then xcodebuild; very easy, no setup, you get a .app in a folder after that; see the azure-pipelines.yml file to see how it's done during CI for instance).
Now that issue #93 may not be what you experienced; it may be a different issue. Could you try to describe the context in which it happens? Is the OS doing something else while you trigger alt-tab when it happens or is everything stable for a while, you're working on a document for example, and after 10min you trigger alt-tab and it crashes? That second scenario would mean it's a different issue.
To recap:
@nathang21 Hi! You mentioned it's the same issue. However if you say that the default mac app-switcher appears, I think it means alt-tab crashed, and it no longer intercepting the shortcut, thus why the default app-switcher is showing.
In @sblask issue, he didn't mention anything about the app crashing. Maybe it does though. I need him to share more details. I would suggest also you try the new #114 branch. It may be better for you than the current build. Basically we are doing beta-testing now. If in a few days/weeks nobody complains, I'm going to merge the PR and it will release v3.
As a long-term strategy, I would like in the future to add some kind of crash-analytics to the app. It's a sensitive topic in the OSS community, so I'm imagining something like: the app crashes, a popup appears "send a crash report as a github issue / ignore". There is the issue of private or identifying info being in the report though, so it's a tricky topic. If you have any suggestion on the matter of course I would love to hear, or even a PR ;)
There wasn't anything visible going on when the problem occurred, i just got random hangs. Just tried the branch, seems like it fixes the issue.
I will tag this ticket in the v3 branch commit, so that once v3 is released, it will automatically close this ticket.
Of course, this issue may still be happening, or a similar but slightly different issue may happen. In that case, feel free to re-open this ticket, or make a new one.
These tickets are very valuable to me to be honest! I'm always worried the app has issues on other people's computers that don't happen on mine, but they are not taking time to open a ticket to get it fixed. Thank you for contributing to making this app more robust!
I found a way to freeze alt-tab. It's not 100% consistent but usually within 10 tries it happens.
I start alt-tab (from the v3 branch). Once it's finished launching, I launch WindowSwitcher. While WindowSwitcher is launching, I trigger alt-tab (sometimes I have to trigger a few times). At this point, sometimes, alt-tab UI will show then completely freeze. The app is unresponsive on the main UI and on the menubar icon. After a few seconds (I waited more than 10s once), it will be back on track. I profiled this phenomenon with Instruments and I see the following:

What I think it happening is the following (just my guess):
The fundamental issue I see here is that there is nothing we can do in alt-tab to avoid this. Ok we can put these OS calls on another thread, so the UI doesn't freeze, but then what do we show the user? An empty black background with a spinner and some text "Waiting for screenshots from the OS..."? I'm not sure this is actually a better UX.
I see this issue as the OS lacking compute resources. The same way it sometimes randomly slows on some operation like I'm launching a Terminal and for some reason it takes 5s to launch this time, because the OS is busy with some background task invisible to me, the user. I would say it's part of the experience of a multi-tasking OS.
That being said, if you have any idea on what could be done here to improve, or maybe correct my theory about what's happening, I really welcome your input here!
I can confirm the v3 branch is much more stable - I've not had it hang in days.
The CPU use is high though, even when not in use.
@GrahamW thank you for testing the v3 branch!
Do you have more specifics regarding the high CPU usage? What kind of numbers do you see? Is it constant, or sometimes it "activates"?
Seems fairly constant, hovering around 7 to 8% when idle, climbs >10% with aggressive switching.
This is on a MBP 13" 2017 (2.5 GHz Dual-Core Intel Core i7)
@GrahamW I'm quite surprised. I just checked on my machine using Activity Monitor and iStats Pro. Here is what I see:
Overall, it seems great to me. Could you maybe try this latest build? Maybe there is an issue in the build you're running that I fixed at some point?
If the issue is still there, could you please share some more data like which tool you use to observe this issue, some screenshot or video maybe. Thanks!
Thanks for looking into this. This is by far the best window switcher I've found.
I built from 1e0894d and it was about the same, so I took a quick look with xcode instrument and it looked like the subscribeWithRetry call in AIUXElement.swift. I changed the timeout to 100ms and the CPU use dropped to ~0.7% with no noticable imapct on respnsiveness.
diff --git a/alt-tab-macos/api-wrappers/AXUIElement.swift b/alt-tab-macos/api-wrappers/AXUIElement.swift
index 814fade..99a413a 100644
--- a/alt-tab-macos/api-wrappers/AXUIElement.swift
+++ b/alt-tab-macos/api-wrappers/AXUIElement.swift
@@ -52,7 +52,7 @@ extension AXUIElement {
func subscribeWithRetry(_ axObserver: AXObserver, _ notification: String, _ pointer: UnsafeMutableRawPointer?) {
let result = AXObserverAddNotification(axObserver, self, notification as CFString, pointer)
if result != .success && result != .notificationUnsupported && result != .notificationAlreadyRegistered {
- DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(10), execute: {
+ DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100), execute: {
self.subscribeWithRetry(axObserver, notification, pointer)
})
}
I have noticed the same behavior on my MacBook (Pro 13 2019). The CPU started at <0.5% but after a while I was at >15% in idle with 5 windows open. I'll try the changes from @GrahamW and report back if I find something.
Update: It seems like some windows can't be subscribed wich results in an infinite loop. Only retrying a few times should fix it. Have tried the following which seems to work. but I think the values can still be tweaked a bit
diff --git a/alt-tab-macos/api-wrappers/AXUIElement.swift b/alt-tab-macos/api-wrappers/AXUIElement.swift
index 0421c59..7774232 100644
--- a/alt-tab-macos/api-wrappers/AXUIElement.swift
+++ b/alt-tab-macos/api-wrappers/AXUIElement.swift
@@ -53,12 +53,14 @@ extension AXUIElement {
return attribute(kAXSubroleAttribute, String.self)
}
- func subscribeWithRetry(_ axObserver: AXObserver, _ notification: String, _ pointer: UnsafeMutableRawPointer?) {
- let result = AXObserverAddNotification(axObserver, self, notification as CFString, pointer)
- if result != .success && result != .notificationUnsupported && result != .notificationAlreadyRegistered {
- DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(10), execute: {
- self.subscribeWithRetry(axObserver, notification, pointer)
- })
+ func subscribeWithRetry(_ axObserver: AXObserver, _ notification: String, _ pointer: UnsafeMutableRawPointer?, _ retry: Int = 0) {
+ if retry < 5 {
+ let result = AXObserverAddNotification(axObserver, self, notification as CFString, pointer)
+ if result != .success && result != .notificationUnsupported && result != .notificationAlreadyRegistered {
+ DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100), execute: {
+ self.subscribeWithRetry(axObserver, notification, pointer, retry + 1)
+ })
+ }
}
}
Update 2: Unrelated to the issue above, I noticed, when rapidly spawning windows introduces a delay for AltTab. The Panel only opens when the last window has finished its animation and is opened.
it looked like the subscribeWithRetry call in AIUXElement.swift. I changed the timeout to 100ms and the CPU use dropped to ~0.7% with no noticable imapct on respnsiveness.
Update: It seems like some windows can't be subscribed wich results in an infinite loop. Only retrying a few times should fix it. Have tried the following which seems to work. but I think the values can still be tweaked a bit
This is retry mechanism was added after @koekeishiya explained to me. See how it's done in yabai.
I think the current implem has shortcomings. For instance, it can infinitely loop. We could limit the retries, but then we risk having a window/app that's slow to start that will be ignored and not displayed in the list (maybe impacting big software like Photoshop, AutoCAD, etc). Another option would be to retry slower. The issue there is that if we are subscribing to an app, and it spawns a window just before we retry, that window will not be listed.
I'm not sure how to approach this issue. Also even if we increase the retry timer to reduce resources consumption, I can imagine someone having lots of background processes that are hanging and having 10s or 100s of these having constant retries. I was tempted to filter applications to listen to. For instance, I could listen to only user-facing apps, not background processes. But then menubar apps and other background processes would have their windows ignored. See the current filtering here with some comments I left also. You can imagine a chrome background process being in charge of spawning fullscreen videos, or even apple using some background process to spawn some window when you use OS integration features like taking a call from your mac. I didn't want to miss on content, so I filtered very little. The tradeoff is that there are more background processes with sometimes broken state being watched.
For windows, not retrying means that we could have a window that's displayed even though it was closed already. We never get the window closed notification, so we never remove the window from the list.
If anyone has a vision on how to handle this better, please share! 👍
Update 2: Unrelated to the issue above, I noticed, when rapidly spawning windows introduces a delay for AltTab. The Panel only opens when the last window has finished its animation and is opened.
I think that's because when you hit the shortcut while the windows are spawning, the WindowServer is going to be slow to respond with the thumbnails. We still ask for all windows thumbnails when the shortcut is hit. There is no notification when a window has its contents changed, so we have no way to pre-fetch these. We ask the WindowServer on-the-spot for all thumbnails on trigger, so if the window server is busy (and I think it is, when you're spawning windows), it blocks the app. A workaround would be to cache the previous thumbnails, and instantly display, then asynchronously update the thumbnails. I dislike the fact that it means the user is presented with incorrect information. Also how do we show the user when a thumbnail is recent or old? A spinner? I'm not convinced that it can make sense but I stay open to this discussion as it is interesting
It seems like some windows can't be subscribed wich results in an infinite loop. Only retrying a few times should fix it. Have tried the following which seems to work. but I think the values can still be tweaked a bit
Good find... I'm running with that, but slightly tweaked to a back-off of the timer on repeat:
.milliseconds(Int(pow(Double(10), Double(retry))) )
@GrahamW actually could you tell me which result the AX call is giving you? There are like 12 different types of results and i only check 3. Also which app/window is involved? Maybe by better understanding the scenario we can code a better logic than a retry
@lwouis It's a bit tricky to reproduce the error. I so far got it on time with the usable debug outputs but will try to get a few more samples.
This on time it seems that AltTab tried to subscribe to a Window wich application already quit. At least the PID was not in the Activity monitor. I'll try to get more information and report back here :)
@lwouis ... it seems to be cannotComplete - I triggered it by opening The GIMP, but it also happens when I start the app first time (but I've already got loads of windows open.) If I get chance I'll try to pin down a couple more offending apps.
I triggered it by opening The GIMP
I'm able to reproduce it with Gimp! Thank you!
Karabiner is another large app that only checks the same statuses that we do. It's really puzzling what we are supposed to do in this scenario. Here is what happens:
NSWorkspace.shared.addObserver(Applications.appsObserver, forKeyPath: "runningApplications", options: [.old, .new], context: nil))isFinishedLaunching property is true. It is not, as is expected from a large app (i.e. they need some time to finish launching). So we observe that propertyisFinishedLaunching switched to true. Note that at this point visually, I see the gimp splash window with the loading bar still loadingwe get the -25204 (i.e. AXError.cannotComplete) return code. There are 2 documentations for it (from Apple website, and from the comments in ApplicationServices.HIServices.AXError.swift):
we retry every 10ms, until Gimp loading splash window finishes. At this point, we are observing the Gimp app, so if I create a new window, it appears in alt-tab. However, the first main window was not observed at launch, so it does not appear. That means that it launched before we successfully subscribed to the kAXWindowCreatedNotification event. Even though we retry every 10ms. I even tried 1ms: same result.
Really puzzling where to go from there. I can imagine things we can do to mitigate the issue. For instance, when we focus windows not currently tracked by alt-tab, we could start tracking them. This would help a bit. You wouldn't see the window initially in alt-tab, but at least after you focus it, it would appear. Again, it is a mitigation, not a root cause fix :/
@GrahamW I think I found a reasonable solution:
isFinishedLaunching flag only as an indication that we should start trying to subscribe to the AX events.I think it's a nice workaround for applications like Gimp. However, my worry is that @Robinhuett mentioned:
windows can't be subscribed which results in an infinite loop
Can you guys help me reproduce this? I never witnessed infinite loops. I always see these slow app launches end eventually. A window never finishing launching is a whole other beast
Can you guys help me reproduce this? I never witnessed infinite loops.
Sure. I'll see if I can pin it down to one app causing an issue.
Note: this ticket started about v2 issue, however it diverted to v3 discussions. As v3 approaches, I think it will fix the OP issue, and I'll close this ticket. Then we can reopen new tickets if new perf issues arise with v3. In the meanwhile, I'm continuing the v3 discussion here.
Today I noticed the fans were spinning, and that alt-tab was consuming a constant 45% CPU. I sampled it, and see that it spends time in the subscribeWithRetry method. I was running a release build though, so couldn't debug further.
@GrahamW @Robinhuett did you guys find a way to reproduce/pin-point the issue by any chance? I would like to get to the bottom of it and fix it. Another approach would be a timer and basically give up after a certain time retrying, but that means missing out on potentially slow-to-launch apps or even system daemon that can spawn windows later on which we wouldn't know about then.
I added a bunch of logs, and I can now see some long retries with com.google.Keystone.Agent. I think the problem scenario is: app starts but the subscription can't be done immediately because the app is not ready yet, so it goes into a retry loop every 10ms. Before the subscription is possible, the app quits. At this point the retries are never going to success which means infinite loop = CPU going up.
I think I have put an end to the CPU leaks from AX subscription retries. I may also have removed memory leaks from some apps or windows not being released after being quit/closed 👍
@GrahamW @Robinhuett could you possibly try the latest code from the v3-poc branch and see if you still see CPU leaks or weird behavior? Here is a build for your convenience: AltTab.app.zip