fix(core): report WebSocket bind failures instead of panicking - #775
Open
cds-amal wants to merge 4 commits into
Open
fix(core): report WebSocket bind failures instead of panicking#775cds-amal wants to merge 4 commits into
cds-amal wants to merge 4 commits into
Conversation
- Occupy the WebSocket port, call the WS starter, and expect the bind error through the startup channel plus the aborted event. - Red at this commit: the handler thread panics dropping a runtime in an async context, and the starter reports only the generic channel error. The next commit makes it green.
- jsonrpc-ws-server builds a private tokio runtime and drops it when a bind fails; dropped inside an async context, that panics the handler thread before the error report runs, masking the bind error as a channel failure. Seen in CI when a test port was claimed between the preflight check and the server bind. - Hand the ws server the runtime the pubsub tasks already use (event_loop_executor), removing the private runtime and its threads. - Extract one server-thread scaffold shared by the HTTP and WebSocket starters; both become plain fns (neither awaits). Emit the aborted event before publishing the startup error, and emit shutdown even when the server exits abnormally. - Close the HTTP server before propagating a WebSocket startup error, so a failed startup does not orphan it with its port bound. - The regression test from the previous commit goes green; its call site sheds the block_on wrapper along with the API.
Contributor
Greptile SummaryThe PR restructures HTTP and WebSocket server startup around a shared thread scaffold and ensures WebSocket bind failures reach the startup caller without a Tokio runtime-drop panic.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Reviews (4): Last reviewed commit: "docs(core): clean up docs" | Re-trigger Greptile |
- aborted() blocks on a full events buffer, and the caller waiting on the startup handshake may be the thread that drains events, so emitting the event first can deadlock startup: caller waits on handler, handler waits on the drain the caller runs. - Publish the handshake error first; the aborted event follows once the drain runs. Verified with a Spin model across both orders and both buffer states. - Drain events blocking with a deadline in the regression test; a nonblocking poll races the aborted send even in the fixed order. - Name the servers-handle tuple; dropping async exposed a type_complexity lint on the old inline signature.
cds-amal
marked this pull request as draft
August 23, 2026 00:05
cds-amal
marked this pull request as ready for review
August 25, 2026 19:26
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.
#774's CI run hit a WebSocket port race, but the bind failure surfaced as a misleading runtime panic:
The port race is a separate issue. This PR fixes the failure path so a lost WebSocket port reports the bind error that caused it.
How
jsonrpc-ws-servercreates a private Tokio runtime for its event loop and drops it whenstart()fails.Our WebSocket starter called
start()insideruntime.block_on. On a bind failure, that private runtime was therefore dropped from an async context, which panics. The handler thread died before it could publish the startup error, hiding the useful failure: which port failed to bind and why.Fix
event_loop_executor), eliminating its private runtime and worker threads.Server behavior is otherwise unchanged: same servers, ports, and close handles.
How to test
The commits are ordered so both states reproduce. The regression test
ws_bind_failure_reports_the_bind_errorlands first: it occupies the WebSocket port and expects the bind error through the startup channel plus the aborted event. It is red at its own commit against the unfixed code, and the fix commit turns it green.Baseline, from the PR branch (shows the CI failure pair: the runtime-drop panic and the masked channel error):
git checkout HEAD^ cargo test -p surfpool-core --lib ws_bind_failure_reports -- --nocaptureFix (the bind error reaches the caller, no panic):
git checkout - cargo test -p surfpool-core --lib ws_bind_failure_reportstest_simnet_tickspasses end to end.NOTE: