GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — refactor(runtime): a Runtime owns one isolate and publishes it from Init by edusperoni · Pull Request #513 · NativeScript/ios

Conversation

@edusperoni

@edusperoni edusperoni commented Oct 13, 2026 •
edited by coderabbitai Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator

Supersedes #512, which found the race this fixes. Thanks @adrian-niculescu.

Problem

Setting up a runtime was assembled by hand at each call site:

  • Main (NativeScript.mm): new Runtime(), CreateIsolate(), Locker, Init(isolate).
  • Worker (Worker.mm): the same, plus WatchHeapLimit before Init(isolate, true) and SetWorkerId after it.

That split caused several problems:

  • Startup race. CreateIsolate added the isolate to the live registry (isolates_) before Init wrote its Caches slot. Runtime::NotifyMemoryPressure (feat(runtime): report native memory held by wrappers to V8, forward memory pressure #507) runs on a dispatch queue and reads that slot for every registered isolate, so a memory-pressure event during startup could read it while it was being written. On a miss it also built a stand-in Caches off-thread. fix(runtime): publish an isolate's Caches under the registry lock #512 fixed this by writing the slot under the registry lock; this PR removes the window instead.
  • Wrong worker flag. SetWorkerId ran after Init, so Runtime::IsWorker() returned false during a worker's Init. That includes worker-scoped class naming in ClassBuilder and the main-isolate-only checks in ns:runtime.
  • Leaked isolates. CreateIsolate could be called more than once on the same Runtime. And a Runtime deleted before Init left its isolate registered and never disposed, because isolate_ was only set at the end of Init.

Change

  • Runtime(const RuntimeOptions&) creates the isolate on the calling thread, which becomes its home thread. The isolate limits and the worker id are passed in up front, so IsWorker() is correct from the start.
  • The caller still takes the Locker and can work with the isolate before it's ready; the worker arms WatchHeapLimit there. It then calls Init().
  • Init() adds the isolate to the registry only after writing its Caches slot. Teardown already removes it from the registry before clearing the slots. So any thread that finds an isolate in the registry may read its slots while holding the registry lock. This rule is documented on isolates_, and the memory-pressure handler relies on it unchanged.
  • A Runtime deleted before Init disposes its isolate, which no other thread has seen yet.
  • CreateIsolate and SetWorkerId are removed. Runtime.h is a project header, not part of the public framework headers.

Not covered

A reader that finds the isolate in the registry, releases the lock, and only then reads the slot can still race teardown's Caches::Remove. The pre-Locker IsolateWrapper::IsValid() in ArgConverter::MethodCallback does this. That's a separate fix: read the slot only under the isolate's Locker.

Tests

No new test: the startup overlap depends on timing (see #512 for a sleep-based repro). The full suite under ASan on a dedicated simulator gives 1835 specs, 0 failures, and no sanitizer reports.

Summary by CodeRabbit

  • Refactor
    • Runtime setup now applies resource limits and worker identification from the outset. Initialization and cleanup follow a consistent lifecycle for both standard and worker runtimes.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

adrian-niculescu reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 62eb19cd-6ee9-40c3-b0d7-d687688c2f83
📥 Commits

Reviewing files that changed from the base of the PR and between 66ac3ce and ca7dda6.

📒 Files selected for processing: 4
  • NativeScript/NativeScript.mm
  • NativeScript/runtime/Runtime.h
  • NativeScript/runtime/Runtime.mm
  • NativeScript/runtime/Worker.mm

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.

📝 Walkthrough

Runtime now creates and configures isolates in the constructor using RuntimeOptions. Init() initializes caches and registers the isolate. Application and worker setup retrieve the isolate from Runtime and call Init() without arguments.

Changes: Runtime Isolate Lifecycle

Layer / File(s) Summary
Configure and create the isolate
NativeScript/runtime/Runtime.h, NativeScript/runtime/Runtime.mm
RuntimeOptions carries isolate limits and a worker ID. The constructor creates and configures the isolate. The destructor directly disposes of the isolate if initialization did not complete.
Initialize and publish the isolate
NativeScript/runtime/Runtime.h, NativeScript/runtime/Runtime.mm, NativeScript/NativeScript.mm, NativeScript/runtime/Worker.mm
Init() initializes caches, registers the isolate, and marks the runtime initialized. Application and worker setup retrieve the isolate from Runtime and call Init() without arguments. Worker setup passes its resource limits and ID to the constructor.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Suggested reviewers: nathanwalker

Merge Risk: ⚪ Minimal · up to ca7dd

The inspected lifecycle ordering addresses the memory-pressure startup race. No issue remains that should block merging after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 1 files. (3 skipped: 3 … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly describes the main runtime change: Runtime creates and owns one isolate, then publishes it during Init.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 1 files. (3 skipped: 3 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
  • X
  • Mastodon
  • Reddit
  • LinkedIn

A rabbit checks the runtime’s start,
With limits tucked beside its heart.
The isolate wakes, then joins the line,
Init() sets the caches in time.
Worker IDs arrive in tow,
And off the little runtimes go.

Comment @coderabbitai help to get the list of available commands.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@edusperoni
refactor(runtime): a Runtime owns one isolate and publishes it from Init
ca7dda6
Setting up a runtime took CreateIsolate, Init and, for workers, SetWorkerId,
assembled by hand at each call site. The isolate joined the live registry in
CreateIsolate, before Init wrote its Caches slot, so a thread that found it
there (the memory-pressure handler, #512) could read that slot while it was
being written. CreateIsolate could also be called twice on one Runtime, and a
Runtime deleted before Init left its isolate registered and never disposed.

- Runtime(const RuntimeOptions&) creates the isolate, homed on the calling
  thread, and takes the limits and worker id up front, so IsWorker() is
  correct during a worker's Init. The caller still locks the isolate and can
  set it up (WatchHeapLimit) before calling Init().
- Init() publishes the isolate to the registry only after writing its Caches
  slot. With the teardown already unregistering before clearing it, a thread
  that finds an isolate in the registry may read its slots under the lock.
- A Runtime deleted before Init disposes its unpublished isolate.
- CreateIsolate and SetWorkerId are gone.

Reported in #512.
@edusperoni
edusperoni force-pushed the fix/memory-pressure-registry branch from 66ac3ce to ca7dda6 Compare October 13, 2026 20:53
@edusperoni edusperoni changed the title fix(runtime): keep isolate ids in the runtime registry refactor(runtime): a Runtime owns one isolate and publishes it from Init Oct 13, 2026
@adrian-niculescu

adrian-niculescu commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Glad to help

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@adrian-niculescu adrian-niculescu mentioned this pull request Oct 13, 2026
fix(runtime): publish an isolate's Caches under the registry lock #512
Closed
@NathanWalker
NathanWalker merged commit 3a6ff66 into main Oct 30, 2026
10 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@NathanWalker
NathanWalker deleted the fix/memory-pressure-registry branch October 30, 2026 19:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@edusperoni @adrian-niculescu @NathanWalker
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.