-
Notifications
You must be signed in to change notification settings - Fork 0
Fix shutdown receiver ownership #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix shutdown receiver ownership #55
Conversation
Reviewer's GuideThis PR optimizes shutdown signaling by reducing the broadcast channel capacity to 1, refactors worker tasks to own and pattern-match on their shutdown receivers rather than borrow them, and introduces a comprehensive rstest-based test suite covering server binding, callbacks, worker management, and graceful shutdown. Sequence diagram for worker task shutdown signal handlingsequenceDiagram
participant Main as Main Server Task
participant Worker as Worker Task
participant Broadcast as broadcast::Receiver
Main->>Broadcast: send shutdown signal
Worker->>Broadcast: recv()
alt Shutdown signal received
Worker->>Worker: break loop and exit
else Channel closed or lagged
Worker->>Worker: break loop and exit
end
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThe changes update the server worker shutdown logic to pass ownership of the shutdown receiver to each worker task, eliminating the need for mutable references. A comprehensive suite of unit and integration tests for the server and worker behaviour is introduced. Additionally, a development dependency on Changes
Sequence Diagram(s)sequenceDiagram
participant Main
participant Server
participant Worker
participant ShutdownChannel
Main->>Server: run_with_shutdown()
Server->>ShutdownChannel: subscribe()
loop For each worker
Server->>Worker: spawn(worker_task, owned shutdown_rx)
end
Main->>ShutdownChannel: send shutdown signal
Worker->>ShutdownChannel: receives shutdown signal (owned receiver)
Worker->>Worker: exits loop and shuts down
Possibly related issues
Possibly related PRs
Poem
✨ Finishing Touches🧪 Generate Unit Tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @leynos - I've reviewed your changes - here's some feedback:
- Verify that creating a fresh
shutdown_tx.subscribe()
for each worker still matches your intended shutdown semantics, since it differs from sharing a single receiver. - Consider inspecting each worker’s
JoinHandle
result afterjoin_all
to catch panics or errors instead of silently dropping them.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Verify that creating a fresh `shutdown_tx.subscribe()` for each worker still matches your intended shutdown semantics, since it differs from sharing a single receiver.
- Consider inspecting each worker’s `JoinHandle` result after `join_all` to catch panics or errors instead of silently dropping them.
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Note Unit test generation is an Early Access feature. Expect some limitations and changes as we gather feedback and continue to improve it. Generating unit tests... This may take up to 20 minutes. |
Here are the copyable unit test edits: Copyable Editssrc/server.rsInsert at line 412:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gates Failed
Prevent hotspot decline
(1 hotspot with Code Duplication)
Enforce advisory code health rules
(1 file with Code Duplication)
Gates Passed
4 Quality Gates Passed
See analysis details in CodeScene
Reason for failure
Prevent hotspot decline | Violations | Code Health Impact | |
---|---|---|---|
server.rs | 1 rule in this hotspot | 10.00 → 9.39 | Suppress |
Enforce advisory code health rules | Violations | Code Health Impact | |
---|---|---|---|
server.rs | 1 advisory rule | 10.00 → 9.39 | Suppress |
Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (6)
.markdownlint-cli2.jsonc (1)
10-10
: Reconsider disabling MD040 globally
RuleMD040
ensures fenced code blocks specify a language identifier, which is valuable for readability and syntax highlighting. Disabling it for all markdown may hide missing identifiers in new content. Consider scoping this override to specific files or revisiting the need for a full global disable.Cargo.toml (1)
15-17
:rstest
addition looks good – consider pinning to a patch release.Adding
rstest
under[dev-dependencies]
is appropriate for the new test-suite.
If you want fully reproducible CI builds, consider locking the patch level as well ("0.18.2"
at the time of writing). Otherwisecargo update
may silently pull in a newer minor patch with different MSRV or behaviour.src/server.rs (4)
288-302
: Nice ownership fix – broadcast capacity can be reduced.Passing
shutdown_tx.subscribe()
into every worker resolves the previous lifetime/borrow issues – good call.
Minor: the broadcast channel is created with a capacity of16
, yet the server only ever sends a single shutdown message. You can safely lower this to1
to save a tiny amount of memory and silence clippy’slarge_capacity
lint if it’s enabled.- let (shutdown_tx, _) = broadcast::channel(16); + let (shutdown_tx, _) = broadcast::channel(1);
332-334
:shutdown_rx.recv()
result is discarded – handle lagged errors explicitly?
broadcast::Receiver::recv()
returnsResult<(), RecvError>
.
Dropping the result is usually fine here, but if the channel ever overflows (Lagged
) the worker will exit immediately without processing further connections. Consider matching on the result to ignoreLagged
but still continue waiting for a real shutdown signal:- _ = shutdown_rx.recv() => break, + res = shutdown_rx.recv() => { + if matches!(res, Ok(()) | Err(broadcast::error::RecvError::Closed)) { + break; + } + // Ignore lag and continue. + }
684-686
: Avoid underscore-prefixed parameter that is still used (Clippy).The parameter is consumed by the function’s type system even though the body ignores it, so Clippy flags
used_underscore_binding
.
Simplest fix: rename it without the leading underscore.-async fn test_multiple_worker_creation( - _factory: impl Fn() -> WireframeApp + Send + Sync + Clone + 'static, +async fn test_multiple_worker_creation( + factory: impl Fn() -> WireframeApp + Send + Sync + Clone + 'static,No other changes are required.
🧰 Tools
🪛 GitHub Actions: CI
[error] 684-684: Clippy error: used underscore-prefixed binding
_factory
. Consider removing the underscore prefix or add#[allow(clippy::used_underscore_binding)]
.
733-736
: Use an explicit&()
pattern instead of_
to silence Clippy.- .on_preamble_decode_success(|_: &()| {}) + .on_preamble_decode_success(|&()| {})This makes the intent explicit and clears the
match_over_unit
lint.🧰 Tools
🪛 GitHub Actions: CI
[error] 734-734: Clippy error: matching over
()
is more explicit. Suggestion: use()
instead of_
in.on_preamble_decode_success(|_: &()| {})
.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
.markdownlint-cli2.jsonc
(1 hunks)Cargo.toml
(1 hunks)src/server.rs
(3 hunks)
🧰 Additional context used
🪛 GitHub Actions: CI
src/server.rs
[error] 641-641: Clippy error: unneeded unit expression. Suggestion: remove the final ()
in let shutdown_future = async { () };
.
[error] 684-684: Clippy error: used underscore-prefixed binding _factory
. Consider removing the underscore prefix or add #[allow(clippy::used_underscore_binding)]
.
[error] 734-734: Clippy error: matching over ()
is more explicit. Suggestion: use ()
instead of _
in .on_preamble_decode_success(|_: &()| {})
.
🔇 Additional comments (1)
.markdownlint-cli2.jsonc (1)
9-9
: Proper JSONC syntax for additional config entries
Adding the comma after the closing brace of theMD013
block is required to introduce the next rule override. Everything looks correctly formatted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gates Failed
Prevent hotspot decline
(1 hotspot with Code Duplication)
Enforce advisory code health rules
(1 file with Code Duplication)
Gates Passed
4 Quality Gates Passed
See analysis details in CodeScene
Reason for failure
Prevent hotspot decline | Violations | Code Health Impact | |
---|---|---|---|
server.rs | 1 rule in this hotspot | 10.00 → 9.39 | Suppress |
Enforce advisory code health rules | Violations | Code Health Impact | |
---|---|---|---|
server.rs | 1 advisory rule | 10.00 → 9.39 | Suppress |
Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/server.rs (1)
660-660
: Needless()
in async block still triggers ClippyThis is the same lint that was highlighted previously.
-let shutdown_future = async { () }; +let shutdown_future = async {};🧰 Tools
🪛 GitHub Actions: CI
[error] 660-660: Clippy lint 'unused_unit': unneeded unit expression '()' found. Remove the final '()' to fix this issue.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Cargo.lock
is excluded by!**/*.lock
📒 Files selected for processing (2)
Cargo.toml
(1 hunks)src/server.rs
(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- Cargo.toml
🧰 Additional context used
🪛 GitHub Actions: CI
src/server.rs
[error] 660-660: Clippy lint 'unused_unit': unneeded unit expression '()' found. Remove the final '()' to fix this issue.
@coderabbitai review |
✅ Actions performedReview triggered.
|
@sourcery-ai review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (1)
src/server.rs (1)
305-317
: Potential double-await onjoin_all
may panic at runtime
join_all
is first polled to completion by
_ = &mut join_all => {}
inside thetokio::select!
, and then awaited again immediately afterwards in thefor res in join_all.await
loop.
TheJoinAll
future is notFused
; a second poll after it has returnedReady
can panic (seefutures::stream::FuturesUnordered
internals).A minimal fix is to await the future only once and reuse the result:
tokio::select! { () = shutdown => { let _ = shutdown_tx.send(()); - } - _ = &mut join_all => {} + // wait for workers after the shutdown signal + _ = &mut join_all => {} } -for res in join_all.await { - if let Err(e) = res { - eprintln!("worker task failed: {e}"); - } +for res in join_all.await { + if let Err(e) = res { + eprintln!("worker task failed: {e}"); + } }Or, capture the vector inside the
select!
branch and avoid a second await altogether.
Please refactor to guarantee the future is polled exactly once.
🧹 Nitpick comments (2)
src/server.rs (2)
286-288
: Capacity comment is correct, but using1
reduces observabilityA channel size of
1
works because only a single shutdown is broadcast, yet it also discards any accidental extra notifications, making debugging harder. Considerbroadcast::channel(self.workers.max(1))
to keep one message per worker without impacting memory meaningfully.
357-368
: Match arm can be collapsed for clarity
broadcast::Receiver::recv
only returnsOk(())
,Err(Closed)
orErr(Lagged(_))
.
Since every variant is a shutdown condition you can simplify:-res = shutdown_rx.recv() => { - if matches!( - res, - Ok(()) - | Err( - broadcast::error::RecvError::Closed - | broadcast::error::RecvError::Lagged(_), - ) - ) { - break; - } -}, +_ = shutdown_rx.recv() => break,This shortens the branch and avoids an unnecessary pattern match.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/server.rs
(4 hunks)
🔇 Additional comments (2)
src/server.rs (2)
334-335
: Ownership fix looks goodTaking ownership of
shutdown_rx
removes the previous borrow-checker gymnastics and aligns with howbroadcast::Receiver
is designed to be used. No further action required.
666-666
: Clippy warning resolved – empty async block is fineThe previous needless unit expression has been removed (
async {}
), satisfying Clippy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coderabbitai new github issue:
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain@leynos I'll create a GitHub issue for this potential double-await bug. 🏁 Script executed:
Length of output: 1090 Perfect! I've successfully created GitHub issue #58 for the potential double-await bug: Issue created: #58 The issue has been:
This will help track the fix for this potential runtime panic in the server shutdown logic. |
Summary
Testing
cargo clippy -- -D warnings
RUSTFLAGS="-D warnings" cargo test
https://chatgpt.com/codex/tasks/task_e_684f4b8d26a483228c8847d3d71f32c8
Summary by Sourcery
Refine shutdown handling by changing worker tasks to take ownership of their broadcast receiver, reducing channel capacity to 1, and updating recv logic to break on closed or lagged errors; add extensive tests for server creation, binding, worker configuration, preamble callbacks, shutdown behavior, extreme worker counts, multi-address binding, and debug guard; add rstest as a dev dependency.
Bug Fixes:
Enhancements:
Build:
Tests:
Summary by CodeRabbit
rstest
crate.