Skip to content

services: tell services when an isolate closes - #204

Merged
ymote merged 1 commit into
mainfrom
feat/isolate-closed-hook
Oct 10, 2026
Merged

ymote merged 1 commit into
mainfrom
feat/isolate-closed-hook

Conversation

@ymote

@ymote ymote commented Oct 10, 2026

Copy link
Copy Markdown
Collaborator

Why

Replier::isolate_key() is an address (ScriptHeap::heap_key = Rc::as_ptr(&root_objects)). Once an isolate is gone, a new one can get the same key. A service that keeps state per isolate therefore can't tell a reopened app from the one that closed.

OctoSense's pdf service (OctoSense-org/OctoSense#472) keeps open documents per isolate. Without a closing signal, a reopened PDF Tools can inherit its predecessor's documents. Those count toward its cap of 8, so after a few close/reopen cycles it can't open a PDF until the shell restarts.

Every host already reports a closing isolate through services::cancel_heap: App Hub's card shutdown (cardapp.rs), and the shell's glance, script-app and Wasm hosts. This PR lets services hear it there.

What

  • services::on_isolate_closed(listener: fn(usize)) registers a listener.
  • cancel_heap calls each listener with the closed isolate's heap key:
    • after the isolate's pending requests and queued answers are cleared;
    • with no lock of this module held (the list is copied out first);
    • on the thread closing the isolate (the UI thread);
    • before any new isolate can reuse the key.
  • Listeners live in their own Mutex, not under SERVICES, because dispatch holds that lock while a service runs.
  • Registering the same function twice registers it once (std::ptr::fn_addr_eq).
  • Replier::isolate_key's doc, the module doc and docs/CODE-WALKTHROUGH.md (step 5 of the request path) point at the hook.

Tests

Run the way native-tools.yml does, on our Linux build host: plain sibling checkouts prepared and verified by Octoscript-Makepad's tools/runtime.py at this tree's pins (makepad d653bc44, Octoscript 2e37d9e6, Octoscript-Makepad 6cf2d90a).

  • cargo test --locked --release -p octosense-appstore --no-default-features --lib: 52 passed, including the two new tests:
    • a_closing_isolate_reaches_each_listener_once: the listener gets the key, and a second registration doesn't double-call;
    • a_closed_isolates_listener_runs_after_its_queues_are_cleared: nothing of the isolate is left when the listener runs, and no lock is held.
  • cargo test --locked --release -p octosense-app-hub-app --no-default-features --lib: 48 passed.

macOS and Windows weren't run locally; this PR's CI covers them.

🤖 Generated with Claude Code

Replier::isolate_key is an address: once an isolate is gone, a new one
can get the same key, so a service that keeps state per isolate cannot
tell a reopened app from the one that closed. OctoSense's pdf service
(OctoSense #472) keeps open documents per isolate; a reopened PDF Tools
could inherit its predecessor's documents, which count toward its cap.

on_isolate_closed registers a listener that cancel_heap calls with the
closed isolate's heap key, after the isolate's pending requests and
queued answers are cleared and with no lock of this module held, on the
thread closing it, before a new isolate can reuse the key. Listeners
live in their own mutex, not under SERVICES (dispatch holds that lock
while a service runs). Registering the same function twice registers it
once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ymote
ymote merged commit 80a64b4 into main Oct 10, 2026
3 checks passed
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