Repository navigation
Move UNUserNotificationCenter remove calls off main thread - #820
Conversation
removeDeliveredNotifications(withIdentifiers:) and removePendingNotificationRequests(withIdentifiers:) perform synchronous XPC to usernoted. When usernoted is slow or overwhelmed, this blocks the main thread indefinitely, freezing the entire UI (confirmed via sample showing __NSXPCCONNECTION_IS_WAITING_FOR_A_SYNCHRONOUS_REPLY__ for the full sample duration on both cmux and cmux NIGHTLY). Add extension methods on UNUserNotificationCenter that dispatch these calls to a background queue. All 13 call sites in TerminalNotificationStore are fire-and-forget (void, no data flows back), so moving them off-main is safe. The @published property mutations and dock badge updates remain on @mainactor.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review infoConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAn extension adds two Changes
Sequence Diagram(s)sequenceDiagram
participant Store as TerminalNotificationStore
participant Center as UNUserNotificationCenter
participant Queue as DispatchQueue (background)
Store->>Center: removeDeliveredNotificationsOffMain(ids)
Center->>Queue: enqueue removal block
Queue->>Center: removeDeliveredNotifications(withIdentifiers: ids)
Queue->>Center: removePendingNotificationRequests(withIdentifiers: ids)
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
Greptile SummaryAddressed critical UI freeze caused by synchronous XPC calls to Changes:
Analysis: The changes align with the threading policy in AGENTS.md which recommends moving blocking operations off the main thread. Confidence Score: 5/5
Important Files Changed
Last reviewed commit: 1d02cc4 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Sources/TerminalNotificationStore.swift`:
- Around line 11-23: The two methods
removeDeliveredNotificationsOffMain(withIdentifiers:) and
removePendingNotificationRequestsOffMain(withIdentifiers:) use
DispatchQueue.global(qos:.utility) which can exhaust threads; replace that with
a dedicated bounded queue (e.g., a private static or instance DispatchQueue like
offMainNotificationQueue = DispatchQueue(label: "com.<app>.notificationOffMain",
qos: .utility) or a DispatchQueue with a target serial/limited-concurrency
configuration) and dispatch the XPC calls onto that queue instead; update both
functions to use that queue so the synchronous removeDeliveredNotifications(...)
and removePendingNotificationRequests(...) calls run off-main but with capped
concurrency.
Replaces DispatchQueue.global(qos: .utility) with a private static serial queue. If usernoted stalls, concurrent dispatches to the global pool could exhaust threads. A dedicated queue caps concurrency at 1.
…ai#820) * Move UNUserNotificationCenter remove calls off main thread removeDeliveredNotifications(withIdentifiers:) and removePendingNotificationRequests(withIdentifiers:) perform synchronous XPC to usernoted. When usernoted is slow or overwhelmed, this blocks the main thread indefinitely, freezing the entire UI (confirmed via sample showing __NSXPCCONNECTION_IS_WAITING_FOR_A_SYNCHRONOUS_REPLY__ for the full sample duration on both cmux and cmux NIGHTLY). Add extension methods on UNUserNotificationCenter that dispatch these calls to a background queue. All 13 call sites in TerminalNotificationStore are fire-and-forget (void, no data flows back), so moving them off-main is safe. The @published property mutations and dock badge updates remain on @mainactor. * Use dedicated serial queue for notification removal dispatch Replaces DispatchQueue.global(qos: .utility) with a private static serial queue. If usernoted stalls, concurrent dispatches to the global pool could exhaust threads. A dedicated queue caps concurrency at 1.
|
Owned by gate-rules on cosmic-gray · session |
Summary
removeDeliveredNotifications(withIdentifiers:)andremovePendingNotificationRequests(withIdentifiers:)perform synchronous XPC tousernotedunder the hood. Whenusernotedis slow or overwhelmed, this blocks the main thread indefinitely, freezing the entire UI.Confirmed via
sampleon both cmux (PID 56257) and cmux NIGHTLY (PID 53475): the main thread was stuck in__NSXPCCONNECTION_IS_WAITING_FOR_A_SYNCHRONOUS_REPLY__for the full 3-second sample duration. NIGHTLY had accumulated 846MB physical footprint because nothing could be freed while the main thread was blocked.The fix adds two extension methods on
UNUserNotificationCenterthat dispatch the blocking calls toDispatchQueue.global(qos: .utility). All 13 call sites inTerminalNotificationStoreare fire-and-forget (void return, no data flows back to the caller), so this is safe. The@Publishedproperty mutations and dock badge updates remain on@MainActor.Apple provides no async variants of these two methods, so off-main dispatch is the only option.
Testing
xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination 'platform=macOS' buildsamplethat the blocking XPC calls were the root causeusernotedto immediately unfreeze both hung processesRelated
usernotedXPC stall blocking main thread inTerminalNotificationStore.addNotification()andTerminalNotificationStore.markRead(forTabId:surfaceId:)Summary by cubic
Moves notification removal off the main thread to prevent UI freezes when usernoted stalls. Updates TerminalNotificationStore to use background-dispatched helpers.
Written for commit 58faf6d. Summary will update on new commits.
Summary by CodeRabbit