Skip to content

fix: fall back to non-pmr BoundedPriorityQueue on runtimes without std::pmr#1086

Open
Leppard wants to merge 1 commit into
software-mansion:mainfrom
Leppard:fix/bounded-priority-queue-pmr-fallback
Open

fix: fall back to non-pmr BoundedPriorityQueue on runtimes without std::pmr#1086
Leppard wants to merge 1 commit into
software-mansion:mainfrom
Leppard:fix/bounded-priority-queue-pmr-fallback

Conversation

@Leppard
Copy link
Copy Markdown

@Leppard Leppard commented Jun 4, 2026

Summary

BoundedPriorityQueue is backed by std::pmr (multiset + monotonic_buffer_resource + unsynchronized_pool_resource). On Apple platforms those runtime symbols only exist in the system libc++ from iOS 17.0 / macOS 14.0 / tvOS 17.0 / watchOS 10.0 (_LIBCPP_AVAILABILITY_HAS_PMR_LIBCPP_INTRODUCED_IN_LLVM_16).

libc++ currently leaves the pmr availability markup empty (see the // TODO: Enable std::pmr markup once llvm/llvm-project#40340 ... note in <__configuration/availability.h>), so using std::pmr below those versions compiles without any diagnostic but aborts at dyld load time:

Symbol not found: std::__1::pmr::memory_resource::~memory_resource()

That crashes every app at launch on iOS < 17 — see #1071 (also confirmed there on production App Store builds and real devices).

Approach

Following @mdydek's preference in #1071 (keep std::pmr, add an STL fallback for older runtimes):

  • Gate the std::pmr implementation behind a new AUDIOAPI_HAS_PMR macro, auto-detected from libc++'s own _LIBCPP_AVAILABILITY_HAS_PMR so it tracks each platform's threshold automatically (Android keeps pmr). It can be overridden by defining AUDIOAPI_HAS_PMR before including the header.
  • When pmr is unavailable, back the same std::multiset with an in-object, non-pmr pool allocator (FixedBlockPool + PoolAllocator) that preserves the original design intent: a fixed buffer, node recycling via an intrusive free list, and no heap allocation — so the audio-thread / real-time behavior is kept on the fallback too, not regressed to a heap-backed std::multiset.

The container type and the entire public API are unchanged, so iOS 17+ / Android keep the existing std::pmr fast path untouched — only sub‑iOS‑17 (and equivalent) builds compile the fallback.

Tests

Adds common/cpp/test/src/utils/BoundedPriorityQueueTest.cpp. It #defines AUDIOAPI_HAS_PMR 0 so the fallback path is exercised even on CI hosts whose libc++ has pmr (the default path is already compiled into the rest of the suite). Covers:

  • ascending order + capacity bounds (full → rejected),
  • pop returns the smallest element first,
  • node recycling under 100k insert/erase cycles (proves the free list reclaims; no pool exhaustion),
  • heterogeneous lowerBound / upperBound (transparent comparator),
  • extract → mutate key → reinsert with hint, and range erase.

Verified locally: builds clean for arm64-apple-ios15.1 with zero pmr symbols in the resulting object (nm), the default (pmr) branch still compiles on a pmr-capable host, and all 4 gtest cases pass. (ASan/UBSan/TSan variants left to CI — the local macOS 26 ASan runtime hangs at startup even for a trivial program.)

Notes

  • No podspec change needed — the ios_min_version = '14.0' claim becomes correct again with the fallback in place.
  • Happy to adjust the macro name/placement, or split the fallback allocator into its own header, if you'd prefer.

Fixes #1071

…d::pmr

BoundedPriorityQueue uses std::pmr (multiset + monotonic_buffer_resource +
unsynchronized_pool_resource). On Apple platforms those runtime symbols ship
in the system libc++ only from iOS 17.0 / macOS 14.0 / tvOS 17.0 / watchOS 10.0
(_LIBCPP_AVAILABILITY_HAS_PMR). Because libc++ currently leaves the pmr
availability markup empty (llvm/llvm-project#40340), using std::pmr below those
versions compiles cleanly but aborts at dyld load time:

    Symbol not found: std::__1::pmr::memory_resource::~memory_resource()

so every app crashes at launch on iOS < 17 (reported in software-mansion#1071).

Gate the std::pmr implementation behind AUDIOAPI_HAS_PMR (auto-detected from
_LIBCPP_AVAILABILITY_HAS_PMR, overridable). When pmr is unavailable, back the
same std::multiset with an in-object, non-pmr pool allocator that preserves the
original design intent: a fixed buffer, node recycling via an intrusive free
list, and no heap allocation. The container and its public API are unchanged,
so iOS 17+/Android keep the std::pmr fast path untouched.

Adds BoundedPriorityQueueTest (forces the fallback path) covering ordering,
capacity bounds, node recycling under 100k insert/erase cycles, heterogeneous
lowerBound/upperBound, and extract/mutate/reinsert.

Fixes software-mansion#1071

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

iOS 16 simulator/runtime crash caused by std::pmr usage in BoundedPriorityQueue.hpp

1 participant