Skip to content

iOS: wait for Fabric to settle before triggering immediate bundle reload - #29

Draft
ofalvai wants to merge 1 commit into
masterfrom
push-vxsvtyokvzzv
Draft

iOS: wait for Fabric to settle before triggering immediate bundle reload#29
ofalvai wants to merge 1 commit into
masterfrom
push-vxsvtyokvzzv

Conversation

@ofalvai

@ofalvai ofalvai commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Why

Somewhat related to #28, but this is a different kind of race condition and crash.

The immediate update mode needs to reload the JS bundle at a point when the JS runtime and Fabric are already running. This leads to race conditions and crashes in certain cases.

Much more detailed LLM summary

The crash

EXC_BAD_ACCESS / SIGSEGV, intermittent, hit almost exclusively on CI (rarely reproduces locally). It happens right after an IMMEDIATE-mode install triggers a restart.

Thread 5 Crashed::  Dispatch queue: com.apple.root.user-interactive-qos
0   libsystem_pthread.dylib       	       0x100f692e8 pthread_mutex_lock + 8
1   libc++.1.dylib                	       0x180336574 std::__1::mutex::lock() + 12
2   React                         	       0x103287b7c facebook::react::MountingCoordinator::setMountingOverrideDelegate(std::__1::weak_ptr<facebook::react::MountingOverrideDelegate const>) const + 28
3   React                         	       0x10330cc3c -[RCTScheduler setupAnimationDriver:] + 80
4   React                         	       0x103311a64 -[RCTSurfacePresenter setupAnimationDriverWithSurfaceHandler:] + 40
5   React                         	       0x103319940 __25-[RCTFabricSurface start]_block_invoke_2 + 64
6   libdispatch.dylib             	       0x1801c07a8 _dispatch_call_block_and_release + 24
7   libdispatch.dylib             	       0x1801db4b0 _dispatch_client_callout + 12
8   libdispatch.dylib             	       0x1801f6824 <deduplicated_symbol> + 28
9   libdispatch.dylib             	       0x1801d3d58 _dispatch_root_queue_drain + 916
10  libdispatch.dylib             	       0x1801d4510 _dispatch_worker_thread2 + 252
11  libsystem_pthread.dylib       	       0x100f6abec _pthread_wqthread + 228
12  libsystem_pthread.dylib       	       0x100f69a28 start_wqthread + 8

Root cause

-[CodePush loadBundle] calls RCTTriggerReloadCommandListeners to tear down and rebuild the RCTInstance. Under the new architecture (RN 0.86), right after the JS bundle finishes evaluating, RN separately enqueues -[RCTFabricSurface start] on a background queue for that same instance. If the reload tears the instance down while that block is still in flight, it crashes inside RN's Fabric mounting layer.

The window between "bundle finished evaluating" and "Fabric surface start block finishes" is normally sub-millisecond, so it rarely gets hit. Under load — CI simulators being the textbook case — it stretches to tens of milliseconds, which is enough to make the race land often enough to be a recurring CI failure, while staying hard to reproduce on a local machine.

localPackage.install code paths using InstallMode.IMMEDIATE are the ones exposed to this: they call restartApp() from JS almost immediately after the new bundle starts running, right when this window is open.

Note: I'm aware of at least one more race condition and crash, this PR is not supposed to fix all of them, follow-up PRs are coming.

What

Wait for Fabric and rendering to settle before reloading the bundle (RCTTriggerReloadCommandListeners). This is achieved with a little state machine (I know, CodePush.m has too many state machines already...) that parks the pending update until we get the signal or a 5-second timer fires (see code comments why this fallback is needed)

Also, spotted that _restartQueue was incorrectly read: [_restartQueue valueForKey: @"@firstObject"] is not valid KVC syntax and was returning junk data (ask your Claude why, it's funny)

@ofalvai ofalvai changed the title iOS: gate CodePush-triggered reloads on a Fabric readiness signal iOS: wait for Fabric to settle before triggering immediate bundle reload Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant