Skip to content

Move PushNotificationIOS badge writes off the main thread - #59208

Open
fkgozali wants to merge 1 commit into
react:mainfrom
fkgozali:export-D125826470
Open

fkgozali wants to merge 1 commit into
react:mainfrom
fkgozali:export-D125826470

Conversation

@fkgozali

Copy link
Copy Markdown
Contributor

Summary:
PushNotificationIOS.setApplicationIconBadgeNumber can still hang the main thread long enough to trigger an 0x8badf00d watchdog kill, even after the switch to -[UNUserNotificationCenter setBadgeCount:withCompletionHandler:].

That API takes a completion handler but is not asynchronous to its caller. Inside -[UNUserNotificationServiceConnection setBadgeCount:forBundleIdentifier:withCompletionHandler:] it dispatch_syncs onto the UserNotifications connection queue. That queue is serial and shared by every UserNotifications call in the process. A synchronous call on another thread, such as reading UIApplication.applicationIconBadgeNumber, holds it for a full XPC round trip to usernotificationsd. RCTPushNotificationManager.methodQueue is the main queue, so a slow daemon reply on any thread becomes a main-thread hang.

In the hang reports the stacks look like this:

  • Main thread: -[RCTPushNotificationManager setApplicationIconBadgeNumber:] -> -[UNUserNotificationCenter setBadgeCount:withCompletionHandler:] -> _dispatch_sync_f_slow / __DISPATCH_WAIT_FOR_QUEUE__
  • Another thread: -[UIApplication applicationIconBadgeNumber] -> -[UNUserNotificationServiceConnection badgeNumberForBundleIdentifier:] -> _dispatch_lane_barrier_sync_invoke_and_complete -> xpc_connection_send_message_with_reply_sync

This change issues the badge write from a private serial queue. Serial keeps writes in order, so the last write wins. Utility QoS, because nothing waits on the queue. getApplicationIconBadgeNumber is unchanged: UIApplication.applicationIconBadgeNumber is main-thread-only, and there is no public off-main read API.

Changelog: [iOS][Fixed] Fix remaining main-thread watchdog hang in PushNotificationIOS.setApplicationIconBadgeNumber

Reviewed By: christophpurrer

Differential Revision: D125826470

Summary:
`PushNotificationIOS.setApplicationIconBadgeNumber` can still hang the main thread long enough to trigger an `0x8badf00d` watchdog kill, even after the switch to `-[UNUserNotificationCenter setBadgeCount:withCompletionHandler:]`.

That API takes a completion handler but is not asynchronous to its caller. Inside `-[UNUserNotificationServiceConnection setBadgeCount:forBundleIdentifier:withCompletionHandler:]` it `dispatch_sync`s onto the UserNotifications connection queue. That queue is serial and shared by every UserNotifications call in the process. A synchronous call on another thread, such as reading `UIApplication.applicationIconBadgeNumber`, holds it for a full XPC round trip to `usernotificationsd`. `RCTPushNotificationManager.methodQueue` is the main queue, so a slow daemon reply on any thread becomes a main-thread hang.

In the hang reports the stacks look like this:
- Main thread: `-[RCTPushNotificationManager setApplicationIconBadgeNumber:]` -> `-[UNUserNotificationCenter setBadgeCount:withCompletionHandler:]` -> `_dispatch_sync_f_slow` / `__DISPATCH_WAIT_FOR_QUEUE__`
- Another thread: `-[UIApplication applicationIconBadgeNumber]` -> `-[UNUserNotificationServiceConnection badgeNumberForBundleIdentifier:]` -> `_dispatch_lane_barrier_sync_invoke_and_complete` -> `xpc_connection_send_message_with_reply_sync`

This change issues the badge write from a private serial queue. Serial keeps writes in order, so the last write wins. Utility QoS, because nothing waits on the queue. `getApplicationIconBadgeNumber` is unchanged: `UIApplication.applicationIconBadgeNumber` is main-thread-only, and there is no public off-main read API.

Changelog: [iOS][Fixed] Fix remaining main-thread watchdog hang in PushNotificationIOS.setApplicationIconBadgeNumber

Reviewed By: christophpurrer

Differential Revision: D125826470
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 19, 2026
@facebook-github-tools facebook-github-tools Bot added the p: Facebook Partner: Facebook label Oct 19, 2026
@meta-codesync

meta-codesync Bot commented Oct 19, 2026

Copy link
Copy Markdown

@fkgozali has exported this pull request. If you are a Meta employee, you can view the originating Diff in D125826470.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant