Skip to content

Commit a9d6584

Browse files
mauricecarrier7tclaude
authored
fix(deflake): AccountsManager useBeta handler off poster-thread + CrossDeviceSyncE2E joins (#1321)
The two deadline-poll/hang flakes that survived #1306's crasher fix: - AccountsManager.updateAccountSetFromNotification (testUseBetaDidChange 120s hang): .TPPUseBetaDidChange is delivered SYNCHRONOUSLY by NotificationCenter on the poster's thread (main, from a Settings toggle). The handler did an accountSetsLock.sync read (performRead) that blocks until any in-flight background catalog-refresh barrier on that lock drains — under CI full-suite load that barrier can hold for the whole 120s execution allowance, hanging the poster (and, in production, stalling the Settings UI). Dispatch the account-set update off the poster's thread (.userInitiated) — it's inherently async (it may reload catalogs), so this is behavior-correct AND unblocks the poster. - CrossDeviceSyncE2ETests (test_audiobookPositionOnDeviceA ~32s): all 10 wait/fulfillment(timeout:) deadline-polls on fire-and-forget backend annotation completions converted to withCheckedContinuation joins. Bookmark types are NSObject-based (non-Sendable), so each site projects the asserted primitive facts inside the completion (off-isolation) and resumes with a Sendable projection — no non-Sendable object crosses the @mainactor boundary. Verified: 56 tests pass in isolation (51 E2E + 5 AccountsManager). **Scope:** one critical-path AccountsManager behavior fix (settings-notification handler no longer blocks the poster) + E2E test-hermeticity. The async dispatch preserves the account-set update; only the thread it runs on changes. **Not done:** the broader deadline-poll tail beyond these observed-failing tests. Co-authored-by: t <t@t.io> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 6cfef68 commit a9d6584

2 files changed

Lines changed: 209 additions & 124 deletions

File tree

Palace/Accounts/Library/AccountsManager.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2002,7 +2002,18 @@ private struct CrawlerHandoffBox: @unchecked Sendable {
20022002
}
20032003

20042004
@objc private func updateAccountSetFromNotification(_ notif: Notification) {
2005-
updateAccountSet(completion: nil)
2005+
// Run off the poster's thread. `.TPPUseBetaDidChange` is delivered
2006+
// synchronously by `NotificationCenter` on whatever thread posted it —
2007+
// typically MAIN, from a Settings toggle. `updateAccountSet` does an
2008+
// `accountSetsLock.sync` read (`performRead`) that blocks until any
2009+
// in-flight background catalog-refresh barrier on that lock drains; under
2010+
// load that barrier can take a long time, so reacting synchronously stalls
2011+
// the poster (the Settings UI — and any test that posts this notification,
2012+
// which is how it surfaced as a 120s hang). The account-set update is
2013+
// inherently async anyway (it may reload catalogs), so dispatch it.
2014+
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
2015+
self?.updateAccountSet(completion: nil)
2016+
}
20062017
}
20072018

20082019
func updateAccountSet(completion: ((Bool) -> Void)?) {

0 commit comments

Comments
 (0)