Commit 20207ad
Guard deferred Fabric surface start against concurrent unregister
Summary:
`-[RCTFabricSurface start]` defers `SurfaceHandler::start()` across two async hops after its synchronous `Registered` check. If a concurrent `RCTInstance` teardown unregisters the surface in that window, the deferred `start()` dereferences a now-null `link_.uiManager` — debug aborts on the `react_native_assert`, release null-derefs. It is a TOCTOU: `-[RCTInstance invalidate]` tears down asynchronously on the JS thread with no completion signal, so an app-layer barrier cannot order against it — the start path itself must tolerate a concurrently-unregistered surface.
Fix: re-check `getStatus() == Registered` inside the deferred block before calling `start()`. Behavior-preserving on the success path; only the already-broken unregistered case changes from crash to a cancelled start.
Scope: iOS only. A companion cross-platform guard in `SurfaceHandler::start()` (return + `LOG(ERROR)` instead of the dev-fatal/release-compiled-out assert) would close the residual window between the re-check and `start()` atomically; left out to keep this iOS-only — happy to add if reviewers prefer. Flagging for code-owner review since this is core surface-start code.
Repro: an in-process React host teardown + rebuild that unregisters a surface while its deferred start is still queued.
Changelog: [iOS][Fixed] - Cancel a deferred Fabric surface start when the surface was unregistered by a concurrent instance teardown, instead of dereferencing a null UIManager
Differential Revision: D1094778681 parent 34ccf4f commit 20207ad
1 file changed
Lines changed: 6 additions & 0 deletions
Lines changed: 6 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
108 | 114 | | |
109 | 115 | | |
110 | 116 | | |
| |||
0 commit comments