Share one localhost WS listener across native product executions - #600
Open
decrypto21 wants to merge 1 commit into
Open
Share one localhost WS listener across native product executions#600decrypto21 wants to merge 1 commit into
decrypto21 wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What
Every product execution (App, Widget, Chat/Worker) connects through one shared localhost WebSocket listener instead of its own, each with an independent token.
NativeTrUApiHostRuntime, started lazily on the first execution'sstart_ws_bridgecall and kept alive for the runtime's lifetime.t=query pairs, so timing can't reveal which token, if any, matched.ProductRuntimetoo, so host-core subscriptions and chat state are torn down the same as on a graceful close.start_ws_bridge/stop_ws_bridgekeep their existing signatures, so iOS/Android need no source changes. Confirmed by regenerating the UniFFI Swift bindings, where only doc-comment text and the API checksum moved.NativeTrUApiCore, the combined single-host/single-execution wrapper the issue's acceptance criteria call the legacy API, was already removed from this repo in refactor(native): remove legacy single-execution core #508, before this PR started. OnlyNativeTrUApiHostRuntime/NativeProductExecutionremain, and opening exactly one execution from one host runtime through them keeps working end-to-end unchanged:native.rs's existingpending_permission_decision_does_not_stall_bridgeandstart_ws_bridge_twice_returns_already_runningtests cover a real connect, a real request/response round-trip, and a clean stop through exactly that path.Why
Every execution binding its own listener means its own port, accept loop, and connection cap: unnecessary overhead that matters more as a host runs multiple product executions concurrently (App, Widget, Chat side by side, not just one at a time). One shared listener removes the duplication while keeping every execution's traffic, queues, and backpressure as isolated as they were before.
How
A connection's token match happens inside the WebSocket handshake, before its task is spawned or registered against its execution, so in principle a token could be revoked in the gap between a sibling's handshake resolving and its registration landing. Both sides serialize on the same per-execution lock:
revokemarks the execution revoked and aborts whatever is already registered under one lock acquisition, and registration checks that same flag before adding itself under the same lock. Whichever happens first is what the other observes, so a connection admitted in that window is never left running with no owner.Handshakes run in independent tasks rather than serialized through the accept loop, so the connection-count caps are reserved with a compare-and-swap loop instead of a plain read-then-increment. Only the first execution to register against an idle bridge can choose
bind_port; a later caller's request is logged (truapi.ws_bridge.bind_port_ignored) rather than silently dropped if it differs from the port already running.Downstream compatibility
Checked against a real consumer,
polkadot-ios-community, which already opens two executions (SPA/App and Chat) from oneTrUAPIHostRuntime, the exact multi-execution shape this change targets. ItsstartWsBridge/stopWsBridgecall sites are untouched, so it keeps working unchanged once it picks up a release that includes this PR:Verification
cargo build --workspacecargo +nightly fmt --checkcargo clippy --workspace --all-targets --all-features -- -D warningscargo test --workspacecargo build -p truapi-server --target wasm32-unknown-unknown --no-default-featuresmake uniffi && ./ios/truapi-host/scripts/sync-bindings.sh --check