Skip to content

Replace shutdown channels with CancellationToken#9

Merged
dirvine merged 7 commits intomainfrom
feat/dht-routing-and-cancellation-token
Feb 18, 2026
Merged

Replace shutdown channels with CancellationToken#9
dirvine merged 7 commits intomainfrom
feat/dht-routing-and-cancellation-token

Conversation

@mickvandijke
Copy link
Copy Markdown
Collaborator

@mickvandijke mickvandijke commented Feb 12, 2026

Summary

  • Replace tokio::sync::broadcast and tokio::sync::watch shutdown channels with tokio_util::CancellationToken in Devnet and RunningNode
  • Shut down devnet nodes concurrently via join_all instead of sequentially
  • Simplify shutdown select branches — CancellationToken::cancelled() needs no extra borrow-check, removing nested if guards

Test plan

  • cargo build --release compiles cleanly
  • cargo test passes all existing tests
  • Devnet startup/shutdown cycle completes without hanging

🤖 Generated with Claude Code

…Token

Switch from tokio broadcast and watch channels to tokio_util CancellationToken
for shutdown signaling in both Devnet and RunningNode. This simplifies the
shutdown logic and enables concurrent node shutdown in devnet.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 12, 2026 13:52
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates devnet and node shutdown signaling to use tokio_util::CancellationToken, and adjusts health monitoring to match a now-synchronous is_running check.

Changes:

  • Replace broadcast/watch shutdown channels with CancellationToken in Devnet and RunningNode
  • Shut down devnet nodes concurrently via futures::future::join_all
  • Update health monitor checks to call is_running() synchronously (no .await)

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
tests/e2e/testnet.rs Updates health monitor to use non-async is_running()
src/node.rs Replaces watch shutdown signaling with CancellationToken in RunningNode
src/devnet.rs Replaces broadcast shutdown signaling with CancellationToken and concurrently shuts down nodes
Cargo.toml Adds tokio-util dependency for CancellationToken

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Cargo.toml

# Async runtime
tokio = { version = "1.35", features = ["full", "signal"] }
tokio-util = { version = "0.7", features = ["rt"] }
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CancellationToken lives under tokio_util::sync and typically requires enabling the tokio-util crate's sync feature. With only features = [\"rt\"], this may fail to compile. Consider switching to tokio-util = { version = \"0.7\", features = [\"sync\"] } (or include both \"sync\" and any other needed features).

Suggested change
tokio-util = { version = "0.7", features = ["rt"] }
tokio-util = { version = "0.7", features = ["rt", "sync"] }

Copilot uses AI. Check for mistakes.
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Feb 12, 2026

Greptile Overview

Greptile Summary

Refactored shutdown signaling from broadcast/watch channels to CancellationToken in both Devnet and RunningNode, simplifying shutdown logic and enabling concurrent devnet node shutdown.

Key changes:

  • Added tokio-util dependency for CancellationToken support
  • Devnet: Replaced broadcast::Sender<()> with CancellationToken, enabling parallel shutdown of all nodes via join_all
  • RunningNode: Replaced watch::Sender<bool> and watch::Receiver<bool> with single CancellationToken, eliminating channel state checks
  • Removed SHUTDOWN_CHANNEL_CAPACITY constant (no longer needed)
  • Updated health monitors to use shutdown.cancelled() instead of channel receive
  • Fixed is_running() call in test to use synchronous API

Note: The PR description mentions DHT-based routing and self-node filtering, but these features are not part of this PR (they were merged in PR #8). This PR only contains the CancellationToken refactor.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The refactor is straightforward, well-tested, and follows Rust best practices. CancellationToken is a proven pattern that simplifies shutdown logic. All changes maintain equivalent semantics while removing boilerplate. No .unwrap(), .expect(), or panic!() violations per CLAUDE.md style guide. The parallel shutdown implementation correctly uses .take() to transfer ownership.
  • No files require special attention

Important Files Changed

Filename Overview
Cargo.toml Added tokio-util dependency with rt feature for CancellationToken support
src/devnet.rs Replaced broadcast channels with CancellationToken for shutdown signaling; parallelized node shutdown with join_all; removed unused constant
src/node.rs Replaced watch channels with CancellationToken for shutdown signaling; simplified shutdown logic by removing channel state checks
tests/e2e/testnet.rs Updated is_running() call to synchronous method (removed .await), consistent with P2PNode API

Sequence Diagram

sequenceDiagram
    participant Caller
    participant Devnet
    participant ShutdownSignal
    participant HealthMonitor
    participant DevnetNode
    participant P2PNode

    Note over Devnet,ShutdownSignal: Shutdown Initiated
    Caller->>Devnet: shutdown()
    Devnet->>ShutdownSignal: cancel()
    
    par Health Monitor Shutdown
        ShutdownSignal-->>HealthMonitor: cancelled() fires
        HealthMonitor->>HealthMonitor: exit loop
    and Node Shutdown (parallel for all nodes)
        Devnet->>DevnetNode: take p2p_node
        Devnet->>P2PNode: shutdown().await
        P2PNode-->>Devnet: Result
        Devnet->>DevnetNode: set state to Stopped
    end
    
    Devnet-->>Caller: shutdown complete
Loading

@mickvandijke mickvandijke changed the title feat: DHT-based chunk routing and CancellationToken shutdown Kademlia DHT chunk routing and CancellationToken shutdown refactor Feb 12, 2026
@mickvandijke mickvandijke changed the title Kademlia DHT chunk routing and CancellationToken shutdown refactor Replace shutdown channels with CancellationToken Feb 12, 2026
mickvandijke and others added 2 commits February 12, 2026 15:09
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 12, 2026 14:31
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/devnet.rs
Comment on lines +410 to +419
let p2p_node = node.p2p_node.take();

shutdown_futures.push(async move {
if let Some(p2p) = p2p_node {
if let Err(e) = p2p.shutdown().await {
warn!("Error shutting down node {}: {}", node_index, e);
}
}
}
*node.state.write().await = NodeState::Stopped;
*node_state.write().await = NodeState::Stopped;
});
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The async block pushed into shutdown_futures is not spawned as a task or wrapped in a future that will execute independently. Since join_all expects futures that are already running or self-contained, this code will execute the shutdown logic sequentially when join_all polls each future, defeating the purpose of concurrent shutdown. Spawn each shutdown as a tokio task (e.g., tokio::spawn(async move { ... })) and collect the join handles instead.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings February 17, 2026 10:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/devnet.rs
*node_state.write().await = NodeState::Stopped;
});
}
futures::future::join_all(shutdown_futures).await;
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing import for futures crate. The code uses futures::future::join_all but there is no corresponding use futures statement at the top of the file. This will cause a compilation error. Add use futures or use futures::future to the imports.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings February 17, 2026 11:55
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Collaborator

@dirvine dirvine left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean refactor: broadcast/watch shutdown channels → CancellationToken. Concurrent shutdown via join_all is a solid improvement. All CI gates pass. LGTM.

@dirvine dirvine merged commit 7f6d4e1 into main Feb 18, 2026
17 of 18 checks passed
@mickvandijke mickvandijke deleted the feat/dht-routing-and-cancellation-token branch February 23, 2026 15:18
mickvandijke added a commit that referenced this pull request Apr 1, 2026
Complete the Section 18 test matrix with the remaining scenarios:

- #3: Fresh replication stores chunk + updates PaidForList on remote nodes
- #9: Fetch retry rotates to alternate source
- #10: Fetch retry exhaustion with single source
- #11: Repeated ApplicationFailure events decrease peer trust score
- #12: Bootstrap node discovers keys stored on multiple peers
- #14: Hint construction covers all locally stored keys
- #15: Data and PaidForList survive node shutdown (partition)
- #17: Neighbor sync request returns valid response (admission test)
- #21: Paid-list majority confirmed from multiple peers via verification
- #24: PaidNotify propagates paid-list entries after fresh replication
- #25: Paid-list convergence verified via majority peer queries
- #44: PaidForList persists across restart (cold-start recovery)
- #45: PaidForList lost in fresh directory (unrecoverable scenario)

All 56 Section 18 scenarios now have test coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
mickvandijke added a commit that referenced this pull request Apr 1, 2026
Complete the Section 18 test matrix with the remaining scenarios:

- #3: Fresh replication stores chunk + updates PaidForList on remote nodes
- #9: Fetch retry rotates to alternate source
- #10: Fetch retry exhaustion with single source
- #11: Repeated ApplicationFailure events decrease peer trust score
- #12: Bootstrap node discovers keys stored on multiple peers
- #14: Hint construction covers all locally stored keys
- #15: Data and PaidForList survive node shutdown (partition)
- #17: Neighbor sync request returns valid response (admission test)
- #21: Paid-list majority confirmed from multiple peers via verification
- #24: PaidNotify propagates paid-list entries after fresh replication
- #25: Paid-list convergence verified via majority peer queries
- #44: PaidForList persists across restart (cold-start recovery)
- #45: PaidForList lost in fresh directory (unrecoverable scenario)

All 56 Section 18 scenarios now have test coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

3 participants