Skip to content

fix(ios): prevent EXC_BREAKPOINT crash in HybridVideoPlayer.release()#4851

Open
erank-pixel wants to merge 1 commit intoTheWidlarzGroup:masterfrom
erank-pixel:fix/ios-release-crash-thread-safety
Open

fix(ios): prevent EXC_BREAKPOINT crash in HybridVideoPlayer.release()#4851
erank-pixel wants to merge 1 commit intoTheWidlarzGroup:masterfrom
erank-pixel:fix/ios-release-crash-thread-safety

Conversation

@erank-pixel
Copy link
Contributor

@erank-pixel erank-pixel commented Mar 6, 2026

Summary

Fixes a crash (EXC_BREAKPOINT) in HybridVideoPlayer.release() observed on iOS 26 when the method is called from the JS thread via the Nitro bridge during Hermes microtask processing.

Crashlytics stack trace:

Crashed: com.facebook.react.runtime.JavaScript
EXC_BREAKPOINT 0x0000000102ab08b4

0  AppReel  HybridVideoPlayer.release() + 37032
2  protocol witness for HybridVideoPlayerSpec_protocol.release()
3  HybridVideoPlayerSpec_cxx.swift:375 — HybridVideoPlayerSpec_cxx.release()
4  Result.hpp:140 — HybridVideoPlayerSpecSwift::release()
5  jsi.h:1507 — HybridFunction::createHybridFunction(...)
...
15 hermes — drainMicrotasks

Two issues are fixed:

  • Double-release: release() is called twice — once from JS cleanup (useVideoPlayer unmount → __destroy()player.release()) and again from Swift deinit when the Nitro GC later collects the hybrid object. The second invocation accesses already-cleared state (event emitter, observers). An isReleased flag now prevents re-entry. deinit no longer calls release() — it performs only minimal observer/player cleanup directly.

  • Thread safety: release() runs on the JS thread (via Nitro/JSI bridge), but VideoPlayerObserver registers KVO observers and a periodic time observer that fire callbacks on the main thread. Concurrent access from both threads corrupts internal state. AVPlayer operations and observer invalidation in release() (and deinit) are now dispatched to the main thread via DispatchQueue.main.sync when not already there.

Environment

  • iOS 26 (beta)
  • react-native-video 7.0.0-beta.6
  • Hermes engine
  • 2 crash events affecting 2 users in production

Fixes a crash (EXC_BREAKPOINT) in `HybridVideoPlayer.release()` observed
on iOS 26 when the method is called from the JS thread via the Nitro bridge.

Two issues are addressed:

1. **Double-release guard**: `release()` was called twice — once explicitly
   from JS cleanup (`useVideoPlayer` unmount) and again from Swift `deinit`
   when the Nitro GC collected the hybrid object. The second invocation
   accessed already-cleared state. An `isReleased` flag now prevents
   re-entry. The `deinit` no longer calls `release()` and instead performs
   minimal observer/player cleanup directly.

2. **Main-thread dispatch for AVPlayer teardown**: `release()` runs on the
   JS thread (via Nitro/JSI), but AVPlayer KVO observers and periodic time
   observers fire callbacks on the main thread. This data race corrupts
   internal state. AVPlayer and observer invalidation are now dispatched
   to the main thread via `DispatchQueue.main.sync` when not already there.

Made-with: Cursor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To Triage

Development

Successfully merging this pull request may close these issues.

1 participant