fix: serialize NSPasteboard access to stop the ⌥C / clipboard-poll AppHangs#34
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Routes every
NSPasteboardaccess through a single serial queue so clipboard reads no longer block the main thread — fixing the 1.5.0 AppHang timeouts (Sentry) while keeping the no-concurrent-readers guarantee.Why
On 1.5.0 a synchronous pasteboard read on the main thread could block for seconds on
pboardd— fetching a Universal Clipboard / Handoff item over the network, or a large or promise-backed item — freezing the ⌥C hot path and the clipboard-history poll. The previous "everything on main" approach avoided a separate crash (two concurrent readers crashing in__NSFastEnumerationMutationHandler) but is exactly what left the main thread exposed to those hangs.How
PasteboardQueue: a process-wide serialDispatchQueuethat funnels every pasteboard content read/write. Reads areasync(off-main, so a slow daemon never hangs the runloop), writes are fire-and-forget but still serialized, andflush()drains pending writes on quit so a copy made right before ⌘Q isn't lost.changeCountin a single hop) and resolves off the lane — OCR and disk loads run after the read returns. A write landing mid-detection can no longer make the window act on a mix of two clipboard states, and Vision OCR doesn't occupy the shared lane.changeCountprobes stay on the caller's thread (the property is cached and doesn't iterate the items array).Testing
Notes