Skip to content

Restore the MediaPipe graph paths in Release without reintroducing the launch deadlock (fingerspelling / holistic landmarker) #309

Description

@straff2002

Follow-up to #304, which is closed because its symptom — Release builds deadlocking at launch — is fixed by #308. That fix removed the trigger (-force_load), not the defect, and it cost a feature.

What doesn't work

The MediaPipe graph paths are non-functional in Release builds. Without -force_load, plain archive linking dead-strips the calculator registrations, so the holistic landmarker — and therefore fingerspelling (Plan CK) — cannot run in a shipping build. FstRegisterer symbols in the app binary went 152 → 6 with the flag removed, which is exactly why it launches now and equally why the graph can't be constructed.

This loses nothing that previously worked: no Release build had ever launched on device, so these paths have never functioned in a shipping build. But it is a live gap between what the app advertises and what a TestFlight/App Store build can do.

CK P3's device smoke is blocked by the same thing. It needs -force_load in a device build to exercise the real landmarker — precisely the configuration that deadlocks.

Root cause: two abseil runtimes in one process

binary absl symbols fst:: symbols
libMediaPipeTasksCommon_device_graph.a (410 MB) ~47,859 867 registrars
MediaPipeTasksCommon.framework 2,046 (incl. 26 absl::Mutex::lock) 0
libsherpa-onnx.a 0 0

Force-loading pulls all 867 OpenFst FstRegisterer static initialisers into the app binary alongside the archive's abseil, while the embedded framework carries its own. A registrar can then bind to a different abseil instance than the one whose state was initialised, and the registry's absl::Mutex never resolves — the main thread sleeps inside dyld4::Loader::findAndRunAllInitializers until iOS kills it at the 20-second launch watchdog (0x8BADF00D). Full traces and crash-log analysis in #304.

⚠️ sherpa-onnx is not involved. An earlier comment on #304 guessed it supplied a second OpenFst copy; measurement showed zero fst::/absl symbols. Don't start there.

Most tractable route

Replace blanket -force_load with targeted -u <symbol> forcing — name only the calculator registration symbols the holistic landmarker graph actually needs, so the linker pulls those objects instead of all 867 registrars. Steps:

  1. Determine the calculator list for MediaPipe's holistic landmarker graph (from the graph definition, or observed from a Debug-with-force_load device run).
  2. Map each to its registration symbol via nm over libMediaPipeTasksCommon_device_graph.a.
  3. Iterate: device build → install → launch and probe past 20s. The failure mode is a watchdog kill, not a build error, so a wrong symbol set looks like a hang rather than a compile failure — budget a full round trip per attempt.
  4. Validate the graph actually runs afterwards (CK P3 device smoke), since a launching app proves nothing about whether the calculators registered.

Rejected alternatives:

  • Dedupe upstream — not possible; both abseil copies are inside Google's prebuilt artefacts.
  • Defer the initialisers — not possible; statically-linked archive initialisers run at dyld time by definition. Deferral would require the archive to live in a dlopened dylib, but the embedded framework references its symbols at launch.

Definition of done

  • Release device build launches (survives >20s, no 0x8BADF00D)
  • Holistic landmarker constructs and fingerspelling decodes on hardware in a Release build
  • project.base.yml carries the reasoning, so the next person doesn't reintroduce blanket -force_load

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions