NativeScript / NativeScript/ios

isImplementedInClass leaks the losing sample instance on re-entrant or racing cache population

Open
#459 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
150
Forks
43
Avg merge
3d 10h
Merged PRs (30d)
22

Description

Problem

MethodMeta::isImplementedInClass (NativeScript/runtime/Metadata.mm) keeps a process-wide cache of [klass alloc]-created sample instances used to answer respondsToSelector: for classes whose alloc returns a different class or which forward messages. The cache is populated like this:

  1. [klass alloc] runs outside the mutex — deliberately, because alloc can trigger +initialize, which may run arbitrary code that re-enters this method (holding the lock would deadlock).
  2. The lock is then taken and sampleInstances.emplace(klass, instance) inserts.

When the emplace loses — the re-entrant call already populated the entry for the same class, or another thread raced — the freshly allocated instance is abandoned: one leaked object per lost race (Instruments shows these as e.g. a leaked UIAlertView / NSURLSessionConfiguration attributed to isImplementedInClass). The count varies run to run since it is timing-dependent.

Why the obvious fixes don't work

  • Releasing the loser is unsafe: the instance is alloc'd but never init'd, so -release runs -dealloc against zero-filled ivars of an arbitrary framework class, on whatever thread the probe ran on (it demonstrably runs on worker threads). Benign for most classes, but a -dealloc that does CFRelease/dispatch_release on a zero ivar traps, and UIKit teardown off the main thread is asserting territory.
  • Holding the lock across alloc deadlocks via the +initialize re-entry described above.
  • Parking losers in a static container merely converts the unreachable leak into intentional perpetual retention — it silences Instruments without reclaiming anything (tried and reverted in #458).

Potential solutions

  • A re-entrancy-aware locking scheme (recursive mutex, or a reader/writer arrangement) so the populate path can be made atomic with respect to re-entrant probes without deadlocking through +initialize.
  • Reuse already-alloc-ed objects: when a sample for that Class is requested (or something else calls [thatClass alloc] through the runtime), hand out / consume the cached instance instead of allocating another, so a losing instance gets used rather than abandoned.

The leak site carries a comment pointing at this issue.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in NativeScript/runtime/Metadata.mm at MethodMeta::isImplementedInClass and the comment pointing to this issue; trace sampleInstances population, mutex use, and re-entrant alloc behavior. Done means eliminating abandoned sample instances during re-entrant or racing population without deadlocking, unsafe release, or intentional perpetual retention.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, objective-c
Domain
mobile-dev
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.