Cloud welcome: stop the carousel pager reading AVPlayer time in the view body

Merged
AU
11d ago
+155-6

Fixes #22708

Root cause

CloudWelcomeMediaCarousel draws its pager fill inside a TimelineView(.animation). On every frame the body called Playback.fraction, and that read AVPlayerItem.currentTime(). The call takes an AVFoundation lock (FigPlayerAsyncCopyBatchedNotificationSource). While the player is busy, for example when AVPlayerLooper swaps items or a decoder is being set up, the main thread blocked inside the view body. Sentry tracks this as CMUXTERM-MACOS-41S3: about 2.2k events from 293 devices on 0.65.x.

Fix

  • Playback now keeps a cached Playhead sample: seconds, duration, rate and when it was sampled. A periodic time observer on the main queue refreshes it every 0.25 s. AVFoundation also calls that observer on every jump, start and stop (seek, loop wrap, pause).
  • fraction(at:) works out the current position from the cached sample and the frame's date. It clamps the result to 0...1, so the TimelineView body never calls into AVPlayer.
  • The observer token is removed when the movie detaches, when another movie attaches, or when the model is released.
  • Before the first sample arrives (duration still NaN), or when there is no movie, the pager falls back to the wall clock as it did before.

Tests

  • CloudWelcomeTests.playbackFractionDoesNotQueryPlayer: about 2 s of frames (120 calls). It asserts zero currentItem/currentTime() reads on a counting AVPlayer subclass, and that progress rises steadily.
  • CloudWelcomeTests.cachedPlayheadLifecycle: the fill holds while stopped, clamps at the end, ignores a NaN duration, and clears on detach.
  • Published head d6d0b529ed0eed56901cfb59f6e0732a7a6166aa: CI compiled the macOS app/test products and executed all 28 Cloud welcome tests successfully. Local Swift syntax and test wiring passed.

Observed / Not run

The counting-player regression covers Playback.fraction(at:), not a rendered TimelineView. The native suite passed on the published head. A live AVFoundation mutex-hang reproduction and UI timing profile were not run.

Defaults chosen

  • This reuses the commit first written for the hang-integration branch (https://github.com/manaflow-ai/cmux/pull/22901). That PR is stale and conflicting, so this ships the welcome carousel fix on its own against main.
  • 0.25 s sample interval: wall-clock extrapolation between samples keeps the fill smooth at 60 fps.
  • There is no separate red commit. The regression test uses the new cached-playhead API, so on the old code it fails to compile rather than failing on behavior.

Changelog

Fixed: Avoid blocking AVPlayer time reads while rendering Cloud welcome video progress.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

Summary by CodeRabbit

  • Improvements
    • Movie progress in the welcome carousel now advances in line with playback speed and remains within the media’s duration.
    • Progress is shown only when valid playback information is available, including after a player is attached or detached.