From 03d584aa1060a5f073cedb77b004d7e6b971f418 Mon Sep 17 00:00:00 2001 From: Falco Hirschenberger Date: Tue, 29 Oct 2024 19:37:09 +0100 Subject: [PATCH 01/18] Add tokio taskname registration for use in tokio-console This uses the `tokio-unstable` feature that has to be enabled and also the runtime variable `RUSTFLAGS="--cfg tokio_unstable"` must be set to enable tokio taskname registration. See example program `tokio-console.rs` for usage. --- Cargo.toml | 6 ++++ examples/tokio-console.rs | 60 +++++++++++++++++++++++++++++++++++++++ src/runner.rs | 30 ++++++++++++++++++-- 3 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 examples/tokio-console.rs diff --git a/Cargo.toml b/Cargo.toml index dbe911a..f822778 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ exclude = [ "/UPCOMING_VERSION_CHANGES.txt", ] +[features] +tokio-unstable = ["tokio/tracing"] + [dependencies] tracing = { version = "0.1.37", default-features = false } @@ -67,6 +70,9 @@ headers = ">= 0.3.5" # Required to fix minimal-versions serde_urlencoded = ">= 0.7.1" # Required to fix minimal-versions unicode-linebreak = ">= 0.1.5" # Required to fix minimal-versions +# tokio-console +console-subscriber = "0.2.0" + # For testing unix signals [target.'cfg(unix)'.dev-dependencies] nix = { version = "0.29.0", default-features = false, features = ["signal"] } diff --git a/examples/tokio-console.rs b/examples/tokio-console.rs new file mode 100644 index 0000000..563f992 --- /dev/null +++ b/examples/tokio-console.rs @@ -0,0 +1,60 @@ +//! This example demonstrates how to use the tokio-console application for tracing tokio tasks's +//! runtime behaviour. Subsystems will appear under their registration names. +//! +//! To make this work, +//! +//! * Compile `tokio-graceful-shutdown` with the `tokio-unstable` feature to register subsystem +//! task names. +//! +//! * Run this example with the environment variable: +//! +//! ``` +//! RUSTFLAGS=="--cfg tokio_unstable" +//! ``` +//! +//! * Run the `tokio-console` CLI application and watch your snappy low-latency tasks + +use miette::Result; +use tokio::time::{sleep, Duration}; +use tokio_graceful_shutdown::{SubsystemBuilder, SubsystemHandle, Toplevel}; +use tracing_subscriber::prelude::*; + +async fn subsys1(subsys: SubsystemHandle) -> Result<()> { + subsys.start(SubsystemBuilder::new("Subsys2", subsys2)); + tracing::info!("Subsystem1 started."); + subsys.on_shutdown_requested().await; + tracing::info!("Shutting down Subsystem1 ..."); + sleep(Duration::from_millis(500)).await; + tracing::info!("Subsystem1 stopped."); + Ok(()) +} + +async fn subsys2(subsys: SubsystemHandle) -> Result<()> { + tracing::info!("Subsystem2 started."); + subsys.on_shutdown_requested().await; + tracing::info!("Shutting down Subsystem2 ..."); + sleep(Duration::from_millis(500)).await; + tracing::info!("Subsystem2 stopped."); + Ok(()) +} + +#[tokio::main] +async fn main() -> Result<()> { + let console_layer = console_subscriber::spawn(); + // Init logging + tracing_subscriber::registry() + .with(console_layer) + .with(tracing_subscriber::fmt::layer()) + .init(); + + // Setup and execute subsystem tree + Toplevel::new(|s| async move { + s.start(SubsystemBuilder::new("Subsys1", subsys1)); + s.start(SubsystemBuilder::new("Subsys2", subsys2)); + s.start(SubsystemBuilder::new("Subsys3", subsys1)); + }) + .catch_signals() + .handle_shutdown_requests(Duration::from_millis(1000)) + .await + .map_err(Into::into) +} diff --git a/src/runner.rs b/src/runner.rs index 837465c..7e2b4da 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -8,6 +8,8 @@ use std::{future::Future, sync::Arc}; +use tokio::task::AbortHandle; + use crate::{ errors::{SubsystemError, SubsystemFailure}, ErrTypeTraits, SubsystemHandle, @@ -32,12 +34,36 @@ impl SubsystemRunner { Fut: 'static + Future> + Send, Err: Into, { - let future = async { run_subsystem(name, subsystem, subsystem_handle, guard).await }; - let aborthandle = tokio::spawn(future).abort_handle(); + let future = { + let name = Arc::clone(&name); + async move { run_subsystem(name, subsystem, subsystem_handle, guard).await } + }; + + let aborthandle = spawn(future, name); SubsystemRunner { aborthandle } } } +#[cfg(not(feature = "tokio-unstable"))] +fn spawn(f: F, _name: Arc) -> AbortHandle +where + ::Output: Send, +{ + tokio::spawn(f).abort_handle() +} + +#[cfg(feature = "tokio-unstable")] +fn spawn(f: F, name: Arc) -> AbortHandle +where + ::Output: Send, +{ + tokio::task::Builder::new() + .name(&name) + .spawn(f) + .expect("spawning a task does not fail") + .abort_handle() +} + impl Drop for SubsystemRunner { fn drop(&mut self) { self.aborthandle.abort() From 2e29fde9623ce0e5347c079124e826831f75e8b0 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Fri, 1 Nov 2024 21:11:10 +0100 Subject: [PATCH 02/18] Attempt to fix clippy workflow --- .github/workflows/rust-clippy.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust-clippy.yml b/.github/workflows/rust-clippy.yml index 5faca56..ca7fdca 100644 --- a/.github/workflows/rust-clippy.yml +++ b/.github/workflows/rust-clippy.yml @@ -22,6 +22,13 @@ jobs: clippy: name: Clippy runs-on: ubuntu-latest + strategy: + matrix: + features: + - flags: "--all-features" + env: 'RUSTFLAGS="--cfg tokio_unstable"' + - flags: "" + - flags: "--no-default-features" permissions: contents: read security-events: write @@ -42,8 +49,9 @@ jobs: - name: Run rust-clippy run: + ${{ matrix.features.env }} cargo clippy - --all-features + ${{ matrix.features.flags }} --all-targets --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt continue-on-error: true From 34f754920dde083c43608b178945fc1b1f67863c Mon Sep 17 00:00:00 2001 From: Finomnis Date: Fri, 1 Nov 2024 21:19:22 +0100 Subject: [PATCH 03/18] Attempt to fix coverage workflow --- .github/workflows/coverage.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index efe80ea..7d48c60 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -12,6 +12,13 @@ jobs: name: Codecov.io continue-on-error: true runs-on: ubuntu-latest + strategy: + matrix: + features: + - flags: "--all-features" + env: 'RUSTFLAGS="--cfg tokio_unstable"' + - flags: "" + - flags: "--no-default-features" env: RUST_TEST_THREADS: "1" steps: @@ -23,10 +30,9 @@ jobs: uses: actions/checkout@v4 - name: Install llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - #- uses: Swatinem/rust-cache@v1 - name: Compute Coverage run: - cargo llvm-cov --all-features --workspace --ignore-filename-regex tests.rs --codecov --output-path codecov.json + ${{ matrix.features.env }} cargo llvm-cov ${{ matrix.features.flags }} --workspace --ignore-filename-regex tests.rs --codecov --output-path codecov.json - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 env: From 8d62809fcd8a665f4a0339d2c539eb0025a55e5d Mon Sep 17 00:00:00 2001 From: Finomnis Date: Fri, 1 Nov 2024 21:19:37 +0100 Subject: [PATCH 04/18] Adjust error message --- src/runner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runner.rs b/src/runner.rs index 7e2b4da..706ff57 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -60,7 +60,7 @@ where tokio::task::Builder::new() .name(&name) .spawn(f) - .expect("spawning a task does not fail") + .expect("a task should be spawned") .abort_handle() } From 4d6f79a7624d194a4f65eee9d79981729d1a89c2 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Fri, 1 Nov 2024 21:31:17 +0100 Subject: [PATCH 05/18] Fix more CI steps for tokio-unstable feature --- .github/workflows/ci.yml | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b85318..dc6d213 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,11 @@ jobs: - aarch64-unknown-linux-gnu - x86_64-pc-windows-gnu - x86_64-unknown-linux-gnu + features: + - flags: "--all-features" + env: 'RUSTFLAGS="--cfg tokio_unstable"' + - flags: "" + - flags: "--no-default-features" steps: - name: Checkout sources uses: actions/checkout@v4 @@ -30,11 +35,18 @@ jobs: uses: taiki-e/install-action@cross - name: Build - run: cross build --all-features --all-targets --release --target=${{ matrix.target }} + run: ${{ matrix.features.env }} cross build ${{ matrix.features.flags }} --all-targets --release --target=${{ matrix.target }} test: name: Test Suite runs-on: ubuntu-latest + strategy: + matrix: + features: + - flags: "--all-features" + env: 'RUSTFLAGS="--cfg tokio_unstable"' + - flags: "" + - flags: "--no-default-features" needs: [lints, docs] env: RUSTFLAGS: "-D warnings" @@ -45,10 +57,8 @@ jobs: - name: Install stable toolchain uses: dtolnay/rust-toolchain@stable - #- uses: Swatinem/rust-cache@v1 - - name: Run cargo test - run: cargo test -- --test-threads 1 + run: ${{ matrix.features.env }} cargo test ${{ matrix.features.flags }} -- --test-threads 1 msrv: name: Minimum Supported Rust Version @@ -66,8 +76,6 @@ jobs: - name: Install cargo-msrv run: cargo binstall --version 0.16.0-beta.17 --no-confirm cargo-msrv - #- uses: Swatinem/rust-cache@v1 - - name: Check MSRV run: cargo msrv verify --log-target=stdout --output-format=json @@ -140,6 +148,12 @@ jobs: lints: name: Lints runs-on: ubuntu-latest + matrix: + features: + - flags: "--all-features" + env: 'RUSTFLAGS="--cfg tokio_unstable"' + - flags: "" + - flags: "--no-default-features" steps: - name: Checkout sources uses: actions/checkout@v4 @@ -153,7 +167,7 @@ jobs: run: cargo fmt --all -- --check - name: Run cargo clippy - run: cargo clippy --all-features --all-targets -- -D warnings + run: ${{ matrix.features.env }} cargo clippy ${{ matrix.features.flags }} --all-targets -- -D warnings docs: name: Documentation @@ -185,18 +199,16 @@ jobs: - name: Install nightly toolchain uses: dtolnay/rust-toolchain@nightly - #- uses: Swatinem/rust-cache@v1 - - name: Build env: RUSTFLAGS: "-Z sanitizer=address" - run: cargo build --target x86_64-unknown-linux-gnu --tests + run: ${{ matrix.features.env }} cargo build ${{ matrix.features.flags }} --target x86_64-unknown-linux-gnu --tests - name: Run tests with leak sanitizer env: RUSTFLAGS: "-Z sanitizer=address" run: - cargo test + ${{ matrix.features.env }} cargo test ${{ matrix.features.flags }} --target x86_64-unknown-linux-gnu --tests -- --test-threads 1 From dabeef9212e0cc4be15b8b1b557b748b556dd9b1 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Fri, 1 Nov 2024 21:33:40 +0100 Subject: [PATCH 06/18] Fix typo --- .github/workflows/ci.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc6d213..6ed8f30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -148,12 +148,13 @@ jobs: lints: name: Lints runs-on: ubuntu-latest - matrix: - features: - - flags: "--all-features" - env: 'RUSTFLAGS="--cfg tokio_unstable"' - - flags: "" - - flags: "--no-default-features" + strategy: + matrix: + features: + - flags: "--all-features" + env: 'RUSTFLAGS="--cfg tokio_unstable"' + - flags: "" + - flags: "--no-default-features" steps: - name: Checkout sources uses: actions/checkout@v4 From d7e91140d8fa26be1e75b0c1945243a4247a1ec7 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 2 Nov 2024 00:19:50 +0100 Subject: [PATCH 07/18] Rewrite task spawning; rework tokio-console example --- Cargo.toml | 4 +++ examples/tokio-console.rs | 60 -------------------------------------- examples/tokio_console.rs | 61 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/runner.rs | 27 ++--------------- src/tokio_unstable.rs | 21 ++++++++++++++ src/toplevel.rs | 11 ++++--- 7 files changed, 99 insertions(+), 88 deletions(-) delete mode 100644 examples/tokio-console.rs create mode 100644 examples/tokio_console.rs create mode 100644 src/tokio_unstable.rs diff --git a/Cargo.toml b/Cargo.toml index f822778..23d30ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,3 +80,7 @@ nix = { version = "0.29.0", default-features = false, features = ["signal"] } # Make leak sanitizer more reliable [profile.dev] opt-level = 1 + +# Define `tokio_unstable` config for linter +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tokio_unstable)'] } diff --git a/examples/tokio-console.rs b/examples/tokio-console.rs deleted file mode 100644 index 563f992..0000000 --- a/examples/tokio-console.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! This example demonstrates how to use the tokio-console application for tracing tokio tasks's -//! runtime behaviour. Subsystems will appear under their registration names. -//! -//! To make this work, -//! -//! * Compile `tokio-graceful-shutdown` with the `tokio-unstable` feature to register subsystem -//! task names. -//! -//! * Run this example with the environment variable: -//! -//! ``` -//! RUSTFLAGS=="--cfg tokio_unstable" -//! ``` -//! -//! * Run the `tokio-console` CLI application and watch your snappy low-latency tasks - -use miette::Result; -use tokio::time::{sleep, Duration}; -use tokio_graceful_shutdown::{SubsystemBuilder, SubsystemHandle, Toplevel}; -use tracing_subscriber::prelude::*; - -async fn subsys1(subsys: SubsystemHandle) -> Result<()> { - subsys.start(SubsystemBuilder::new("Subsys2", subsys2)); - tracing::info!("Subsystem1 started."); - subsys.on_shutdown_requested().await; - tracing::info!("Shutting down Subsystem1 ..."); - sleep(Duration::from_millis(500)).await; - tracing::info!("Subsystem1 stopped."); - Ok(()) -} - -async fn subsys2(subsys: SubsystemHandle) -> Result<()> { - tracing::info!("Subsystem2 started."); - subsys.on_shutdown_requested().await; - tracing::info!("Shutting down Subsystem2 ..."); - sleep(Duration::from_millis(500)).await; - tracing::info!("Subsystem2 stopped."); - Ok(()) -} - -#[tokio::main] -async fn main() -> Result<()> { - let console_layer = console_subscriber::spawn(); - // Init logging - tracing_subscriber::registry() - .with(console_layer) - .with(tracing_subscriber::fmt::layer()) - .init(); - - // Setup and execute subsystem tree - Toplevel::new(|s| async move { - s.start(SubsystemBuilder::new("Subsys1", subsys1)); - s.start(SubsystemBuilder::new("Subsys2", subsys2)); - s.start(SubsystemBuilder::new("Subsys3", subsys1)); - }) - .catch_signals() - .handle_shutdown_requests(Duration::from_millis(1000)) - .await - .map_err(Into::into) -} diff --git a/examples/tokio_console.rs b/examples/tokio_console.rs new file mode 100644 index 0000000..3dd2341 --- /dev/null +++ b/examples/tokio_console.rs @@ -0,0 +1,61 @@ +//! This example demonstrates how to use the tokio-console application for tracing tokio tasks's +//! runtime behaviour. Subsystems will appear under their registration names. +//! +//! Run this example with: +//! +//! ``` +//! RUSTFLAGS="--cfg tokio_unstable" cargo run --features "tokio-unstable" --example tokio_console +//! ``` +//! +//! Then, open the `tokio-console` application (see https://crates.io/crates/tokio-console) to +//! follow the subsystem tasks live. + +use miette::Result; +use tokio::time::{sleep, Duration}; +use tokio_graceful_shutdown::{FutureExt, SubsystemBuilder, SubsystemHandle, Toplevel}; +use tracing_subscriber::prelude::*; + +async fn subsys(subsys: SubsystemHandle) -> Result<()> { + tracing::info!("Parent started."); + + let mut iteration = 0; + while !subsys.is_shutdown_requested() { + subsys.start(SubsystemBuilder::new(format!("child{iteration}"), child)); + iteration += 1; + + sleep(Duration::from_millis(1000)) + .cancel_on_shutdown(&subsys) + .await + .ok(); + } + + tracing::info!("Parent stopped."); + Ok(()) +} + +async fn child(subsys: SubsystemHandle) -> Result<()> { + sleep(Duration::from_millis(3000)) + .cancel_on_shutdown(&subsys) + .await + .ok(); + Ok(()) +} + +#[tokio::main] +async fn main() -> Result<()> { + let console_layer = console_subscriber::spawn(); + // Init logging + tracing_subscriber::registry() + .with(console_layer) + .with(tracing_subscriber::fmt::layer().compact()) + .init(); + + // Setup and execute subsystem tree + Toplevel::new(|s| async move { + s.start(SubsystemBuilder::new("parent", subsys)); + }) + .catch_signals() + .handle_shutdown_requests(Duration::from_millis(1000)) + .await + .map_err(Into::into) +} diff --git a/src/lib.rs b/src/lib.rs index 9ecc147..985023c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,3 +124,6 @@ pub use subsystem::SubsystemBuilder; pub use subsystem::SubsystemFinishedFuture; pub use subsystem::SubsystemHandle; pub use toplevel::Toplevel; + +mod tokio_unstable; +use tokio_unstable::spawn; diff --git a/src/runner.rs b/src/runner.rs index 706ff57..bd0d245 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -8,8 +8,6 @@ use std::{future::Future, sync::Arc}; -use tokio::task::AbortHandle; - use crate::{ errors::{SubsystemError, SubsystemFailure}, ErrTypeTraits, SubsystemHandle, @@ -39,31 +37,11 @@ impl SubsystemRunner { async move { run_subsystem(name, subsystem, subsystem_handle, guard).await } }; - let aborthandle = spawn(future, name); + let aborthandle = crate::spawn(future, "subsystem_runner").abort_handle(); SubsystemRunner { aborthandle } } } -#[cfg(not(feature = "tokio-unstable"))] -fn spawn(f: F, _name: Arc) -> AbortHandle -where - ::Output: Send, -{ - tokio::spawn(f).abort_handle() -} - -#[cfg(feature = "tokio-unstable")] -fn spawn(f: F, name: Arc) -> AbortHandle -where - ::Output: Send, -{ - tokio::task::Builder::new() - .name(&name) - .spawn(f) - .expect("a task should be spawned") - .abort_handle() -} - impl Drop for SubsystemRunner { fn drop(&mut self) { self.aborthandle.abort() @@ -83,7 +61,8 @@ async fn run_subsystem( let mut redirected_subsystem_handle = subsystem_handle.delayed_clone(); let future = async { subsystem(subsystem_handle).await.map_err(|e| e.into()) }; - let join_handle = tokio::spawn(future); + + let join_handle = crate::spawn(future, &name); // Abort on drop guard.on_cancel({ diff --git a/src/tokio_unstable.rs b/src/tokio_unstable.rs new file mode 100644 index 0000000..28c8215 --- /dev/null +++ b/src/tokio_unstable.rs @@ -0,0 +1,21 @@ +use std::future::Future; +use tokio::task::JoinHandle; + +#[cfg(not(all(tokio_unstable, feature = "tokio-unstable")))] +pub(crate) fn spawn(f: F, _name: &str) -> JoinHandle +where + ::Output: Send + 'static, +{ + tokio::spawn(f) +} + +#[cfg(all(tokio_unstable, feature = "tokio-unstable"))] +pub(crate) fn spawn(f: F, name: &str) -> JoinHandle +where + ::Output: Send + 'static, +{ + tokio::task::Builder::new() + .name(name) + .spawn(f) + .expect("a task should be spawned") +} diff --git a/src/toplevel.rs b/src/toplevel.rs index 9e7b4c1..eae9c35 100644 --- a/src/toplevel.rs +++ b/src/toplevel.rs @@ -121,10 +121,13 @@ impl Toplevel { pub fn catch_signals(self) -> Self { let shutdown_token = self.root_handle.get_cancellation_token().clone(); - tokio::spawn(async move { - wait_for_signal().await; - shutdown_token.cancel(); - }); + crate::spawn( + async move { + wait_for_signal().await; + shutdown_token.cancel(); + }, + "catch_signals", + ); self } From ff99fb22f72621105c85dd4c20dbc0846dd00dd5 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 2 Nov 2024 00:26:19 +0100 Subject: [PATCH 08/18] Revert incorrect changes in runner --- src/runner.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index bd0d245..d5cff69 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -32,11 +32,7 @@ impl SubsystemRunner { Fut: 'static + Future> + Send, Err: Into, { - let future = { - let name = Arc::clone(&name); - async move { run_subsystem(name, subsystem, subsystem_handle, guard).await } - }; - + let future = async { run_subsystem(name, subsystem, subsystem_handle, guard).await }; let aborthandle = crate::spawn(future, "subsystem_runner").abort_handle(); SubsystemRunner { aborthandle } } @@ -61,7 +57,6 @@ async fn run_subsystem( let mut redirected_subsystem_handle = subsystem_handle.delayed_clone(); let future = async { subsystem(subsystem_handle).await.map_err(|e| e.into()) }; - let join_handle = crate::spawn(future, &name); // Abort on drop From 49bd49068ad14197b89513f69d1f7724946381be Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 2 Nov 2024 00:37:59 +0100 Subject: [PATCH 09/18] Remove tokio-unstable from CI --- .github/workflows/ci.yml | 31 ++++++------------------------- .github/workflows/coverage.yml | 9 +-------- .github/workflows/rust-clippy.yml | 9 --------- 3 files changed, 7 insertions(+), 42 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff670bc..df4a3bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,11 +22,6 @@ jobs: - aarch64-unknown-linux-gnu - x86_64-pc-windows-gnu - x86_64-unknown-linux-gnu - features: - - flags: "--all-features" - env: 'RUSTFLAGS="--cfg tokio_unstable"' - - flags: "" - - flags: "--no-default-features" steps: - name: Checkout sources uses: actions/checkout@v4 @@ -35,14 +30,14 @@ jobs: uses: taiki-e/install-action@cross - name: Build - run: cross build --all-features --release --target=${{ matrix.target }} + run: cross build --release --target=${{ matrix.target }} build-examples: name: Build Examples runs-on: ubuntu-latest needs: [lints, docs] env: - RUSTFLAGS: "-D warnings" + RUSTFLAGS: "-D warnings --cfg tokio_unstable" steps: - name: Checkout sources uses: actions/checkout@v4 @@ -56,13 +51,6 @@ jobs: test: name: Test Suite runs-on: ubuntu-latest - strategy: - matrix: - features: - - flags: "--all-features" - env: 'RUSTFLAGS="--cfg tokio_unstable"' - - flags: "" - - flags: "--no-default-features" needs: [lints, docs] env: RUSTFLAGS: "-D warnings" @@ -74,7 +62,7 @@ jobs: uses: dtolnay/rust-toolchain@stable - name: Run cargo test - run: ${{ matrix.features.env }} cargo test ${{ matrix.features.flags }} -- --test-threads 1 + run: cargo test -- --test-threads 1 msrv: name: Minimum Supported Rust Version @@ -164,13 +152,6 @@ jobs: lints: name: Lints runs-on: ubuntu-latest - strategy: - matrix: - features: - - flags: "--all-features" - env: 'RUSTFLAGS="--cfg tokio_unstable"' - - flags: "" - - flags: "--no-default-features" steps: - name: Checkout sources uses: actions/checkout@v4 @@ -184,7 +165,7 @@ jobs: run: cargo fmt --all -- --check - name: Run cargo clippy - run: ${{ matrix.features.env }} cargo clippy ${{ matrix.features.flags }} --all-targets -- -D warnings + run: cargo clippy --all-targets -- -D warnings docs: name: Documentation @@ -219,13 +200,13 @@ jobs: - name: Build env: RUSTFLAGS: "-Z sanitizer=address" - run: ${{ matrix.features.env }} cargo build ${{ matrix.features.flags }} --target x86_64-unknown-linux-gnu --tests + run: cargo build --target x86_64-unknown-linux-gnu --tests - name: Run tests with leak sanitizer env: RUSTFLAGS: "-Z sanitizer=address" run: - ${{ matrix.features.env }} cargo test ${{ matrix.features.flags }} + cargo test --target x86_64-unknown-linux-gnu --tests -- --test-threads 1 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 7d48c60..fd8ad51 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -12,13 +12,6 @@ jobs: name: Codecov.io continue-on-error: true runs-on: ubuntu-latest - strategy: - matrix: - features: - - flags: "--all-features" - env: 'RUSTFLAGS="--cfg tokio_unstable"' - - flags: "" - - flags: "--no-default-features" env: RUST_TEST_THREADS: "1" steps: @@ -32,7 +25,7 @@ jobs: uses: taiki-e/install-action@cargo-llvm-cov - name: Compute Coverage run: - ${{ matrix.features.env }} cargo llvm-cov ${{ matrix.features.flags }} --workspace --ignore-filename-regex tests.rs --codecov --output-path codecov.json + cargo llvm-cov --workspace --ignore-filename-regex tests.rs --codecov --output-path codecov.json - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 env: diff --git a/.github/workflows/rust-clippy.yml b/.github/workflows/rust-clippy.yml index ca7fdca..b244db5 100644 --- a/.github/workflows/rust-clippy.yml +++ b/.github/workflows/rust-clippy.yml @@ -22,13 +22,6 @@ jobs: clippy: name: Clippy runs-on: ubuntu-latest - strategy: - matrix: - features: - - flags: "--all-features" - env: 'RUSTFLAGS="--cfg tokio_unstable"' - - flags: "" - - flags: "--no-default-features" permissions: contents: read security-events: write @@ -49,9 +42,7 @@ jobs: - name: Run rust-clippy run: - ${{ matrix.features.env }} cargo clippy - ${{ matrix.features.flags }} --all-targets --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt continue-on-error: true From b42db5a6d44bba05a13cead9444c492033ccf418 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 2 Nov 2024 00:46:44 +0100 Subject: [PATCH 10/18] Dummy to see if CI actually compiles example --- examples/tokio_console.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tokio_console.rs b/examples/tokio_console.rs index 3dd2341..3ac5508 100644 --- a/examples/tokio_console.rs +++ b/examples/tokio_console.rs @@ -9,7 +9,7 @@ //! //! Then, open the `tokio-console` application (see https://crates.io/crates/tokio-console) to //! follow the subsystem tasks live. - +xxx use miette::Result; use tokio::time::{sleep, Duration}; use tokio_graceful_shutdown::{FutureExt, SubsystemBuilder, SubsystemHandle, Toplevel}; From 56cbe79b85ce2d2c6576b5a90f4d52d0416f2475 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 2 Nov 2024 00:54:36 +0100 Subject: [PATCH 11/18] Fix more CI errors --- Cargo.toml | 2 ++ examples/tokio_console.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 23d30ee..9b97f4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,8 @@ headers = ">= 0.3.5" # Required to fix minimal-versions serde_urlencoded = ">= 0.7.1" # Required to fix minimal-versions unicode-linebreak = ">= 0.1.5" # Required to fix minimal-versions +gcc = ">= 0.3.4" # Required to fix minimal-versions + # tokio-console console-subscriber = "0.2.0" diff --git a/examples/tokio_console.rs b/examples/tokio_console.rs index 3ac5508..3dd2341 100644 --- a/examples/tokio_console.rs +++ b/examples/tokio_console.rs @@ -9,7 +9,7 @@ //! //! Then, open the `tokio-console` application (see https://crates.io/crates/tokio-console) to //! follow the subsystem tasks live. -xxx + use miette::Result; use tokio::time::{sleep, Duration}; use tokio_graceful_shutdown::{FutureExt, SubsystemBuilder, SubsystemHandle, Toplevel}; From c94712ed3265e94bcc92303255b62181d60459dc Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 2 Nov 2024 00:59:11 +0100 Subject: [PATCH 12/18] Make tokio_console example depend on tokio-unstable feature --- Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 9b97f4e..3adf473 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,11 @@ exclude = [ [features] tokio-unstable = ["tokio/tracing"] +[[example]] +name = "tokio_console" +required-features = ["tokio-unstable"] + + [dependencies] tracing = { version = "0.1.37", default-features = false } From 337b99a86da64121351a7cfa3ca6e44a649f3ed1 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 2 Nov 2024 01:05:22 +0100 Subject: [PATCH 13/18] Remove test suite from min versions check --- .github/workflows/ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df4a3bd..5842f9f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -112,9 +112,6 @@ jobs: - name: Check with minimal versions run: cargo minimal-versions check --workspace --ignore-private - - name: Test with minimal versions - run: cargo minimal-versions test -- --test-threads 1 - min-versions-msrv: name: Minimal Dependency Versions (MSRV) runs-on: ubuntu-latest From 69c92ecbdd58f72dd4b93b3391d7bf5c4b2965f6 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 2 Nov 2024 01:11:22 +0100 Subject: [PATCH 14/18] Minor refactoring in example --- examples/tokio_console.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/tokio_console.rs b/examples/tokio_console.rs index 3dd2341..ada1e42 100644 --- a/examples/tokio_console.rs +++ b/examples/tokio_console.rs @@ -15,7 +15,15 @@ use tokio::time::{sleep, Duration}; use tokio_graceful_shutdown::{FutureExt, SubsystemBuilder, SubsystemHandle, Toplevel}; use tracing_subscriber::prelude::*; -async fn subsys(subsys: SubsystemHandle) -> Result<()> { +async fn child(subsys: SubsystemHandle) -> Result<()> { + sleep(Duration::from_millis(3000)) + .cancel_on_shutdown(&subsys) + .await + .ok(); + Ok(()) +} + +async fn parent(subsys: SubsystemHandle) -> Result<()> { tracing::info!("Parent started."); let mut iteration = 0; @@ -33,18 +41,10 @@ async fn subsys(subsys: SubsystemHandle) -> Result<()> { Ok(()) } -async fn child(subsys: SubsystemHandle) -> Result<()> { - sleep(Duration::from_millis(3000)) - .cancel_on_shutdown(&subsys) - .await - .ok(); - Ok(()) -} - #[tokio::main] async fn main() -> Result<()> { + // Init tokio-console server and tracing let console_layer = console_subscriber::spawn(); - // Init logging tracing_subscriber::registry() .with(console_layer) .with(tracing_subscriber::fmt::layer().compact()) @@ -52,7 +52,7 @@ async fn main() -> Result<()> { // Setup and execute subsystem tree Toplevel::new(|s| async move { - s.start(SubsystemBuilder::new("parent", subsys)); + s.start(SubsystemBuilder::new("parent", parent)); }) .catch_signals() .handle_shutdown_requests(Duration::from_millis(1000)) From 01cadd0086384606c4ef8deeb544550b22e0a62f Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 2 Nov 2024 11:17:47 +0100 Subject: [PATCH 15/18] Rename crate::tokio_unstable to crate::tokio_task --- src/lib.rs | 4 +--- src/runner.rs | 4 ++-- src/{tokio_unstable.rs => tokio_task.rs} | 0 src/toplevel.rs | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) rename src/{tokio_unstable.rs => tokio_task.rs} (100%) diff --git a/src/lib.rs b/src/lib.rs index 985023c..3a745be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,6 +113,7 @@ mod into_subsystem; mod runner; mod signal_handling; mod subsystem; +mod tokio_task; mod toplevel; mod utils; @@ -124,6 +125,3 @@ pub use subsystem::SubsystemBuilder; pub use subsystem::SubsystemFinishedFuture; pub use subsystem::SubsystemHandle; pub use toplevel::Toplevel; - -mod tokio_unstable; -use tokio_unstable::spawn; diff --git a/src/runner.rs b/src/runner.rs index d5cff69..4811a1a 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -33,7 +33,7 @@ impl SubsystemRunner { Err: Into, { let future = async { run_subsystem(name, subsystem, subsystem_handle, guard).await }; - let aborthandle = crate::spawn(future, "subsystem_runner").abort_handle(); + let aborthandle = crate::tokio_task::spawn(future, "subsystem_runner").abort_handle(); SubsystemRunner { aborthandle } } } @@ -57,7 +57,7 @@ async fn run_subsystem( let mut redirected_subsystem_handle = subsystem_handle.delayed_clone(); let future = async { subsystem(subsystem_handle).await.map_err(|e| e.into()) }; - let join_handle = crate::spawn(future, &name); + let join_handle = crate::tokio_task::spawn(future, &name); // Abort on drop guard.on_cancel({ diff --git a/src/tokio_unstable.rs b/src/tokio_task.rs similarity index 100% rename from src/tokio_unstable.rs rename to src/tokio_task.rs diff --git a/src/toplevel.rs b/src/toplevel.rs index eae9c35..1c1c9d1 100644 --- a/src/toplevel.rs +++ b/src/toplevel.rs @@ -121,7 +121,7 @@ impl Toplevel { pub fn catch_signals(self) -> Self { let shutdown_token = self.root_handle.get_cancellation_token().clone(); - crate::spawn( + crate::tokio_task::spawn( async move { wait_for_signal().await; shutdown_token.cancel(); From 7e7ce653f49b58a9bfe50a3a96d4eb02b1de5feb Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 2 Nov 2024 11:52:11 +0100 Subject: [PATCH 16/18] Rename `tokio_unstable` feature to `tracing` --- Cargo.toml | 4 ++-- examples/tokio_console.rs | 2 +- src/tokio_task.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3adf473..24a0bac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,11 +21,11 @@ exclude = [ ] [features] -tokio-unstable = ["tokio/tracing"] +tracing = ["tokio/tracing"] [[example]] name = "tokio_console" -required-features = ["tokio-unstable"] +required-features = ["tracing"] [dependencies] diff --git a/examples/tokio_console.rs b/examples/tokio_console.rs index ada1e42..28d21c2 100644 --- a/examples/tokio_console.rs +++ b/examples/tokio_console.rs @@ -4,7 +4,7 @@ //! Run this example with: //! //! ``` -//! RUSTFLAGS="--cfg tokio_unstable" cargo run --features "tokio-unstable" --example tokio_console +//! RUSTFLAGS="--cfg tokio_unstable" cargo run --features "tracing" --example tokio_console //! ``` //! //! Then, open the `tokio-console` application (see https://crates.io/crates/tokio-console) to diff --git a/src/tokio_task.rs b/src/tokio_task.rs index 28c8215..80dd1b2 100644 --- a/src/tokio_task.rs +++ b/src/tokio_task.rs @@ -1,7 +1,7 @@ use std::future::Future; use tokio::task::JoinHandle; -#[cfg(not(all(tokio_unstable, feature = "tokio-unstable")))] +#[cfg(not(all(tokio_unstable, feature = "tracing")))] pub(crate) fn spawn(f: F, _name: &str) -> JoinHandle where ::Output: Send + 'static, @@ -9,7 +9,7 @@ where tokio::spawn(f) } -#[cfg(all(tokio_unstable, feature = "tokio-unstable"))] +#[cfg(all(tokio_unstable, feature = "tracing"))] pub(crate) fn spawn(f: F, name: &str) -> JoinHandle where ::Output: Send + 'static, From f25f8feaa002b263a13f1c78782992aefc0e1461 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 2 Nov 2024 15:44:35 +0100 Subject: [PATCH 17/18] Reduce verbosity in example; name toplevel task --- examples/tokio_console.rs | 9 +++++++-- src/subsystem/subsystem_handle.rs | 6 +++++- src/toplevel.rs | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/examples/tokio_console.rs b/examples/tokio_console.rs index 28d21c2..b5c8570 100644 --- a/examples/tokio_console.rs +++ b/examples/tokio_console.rs @@ -13,7 +13,8 @@ use miette::Result; use tokio::time::{sleep, Duration}; use tokio_graceful_shutdown::{FutureExt, SubsystemBuilder, SubsystemHandle, Toplevel}; -use tracing_subscriber::prelude::*; +use tracing::Level; +use tracing_subscriber::{fmt::writer::MakeWriterExt, prelude::*}; async fn child(subsys: SubsystemHandle) -> Result<()> { sleep(Duration::from_millis(3000)) @@ -47,7 +48,11 @@ async fn main() -> Result<()> { let console_layer = console_subscriber::spawn(); tracing_subscriber::registry() .with(console_layer) - .with(tracing_subscriber::fmt::layer().compact()) + .with( + tracing_subscriber::fmt::layer() + .with_writer(std::io::stdout.with_max_level(Level::DEBUG)) + .compact(), + ) .init(); // Setup and execute subsystem tree diff --git a/src/subsystem/subsystem_handle.rs b/src/subsystem/subsystem_handle.rs index 08dd32b..c75901f 100644 --- a/src/subsystem/subsystem_handle.rs +++ b/src/subsystem/subsystem_handle.rs @@ -82,7 +82,11 @@ impl SubsystemHandle { Err: Into, { self.start_with_abs_name( - Arc::from(format!("{}/{}", self.inner.name, builder.name)), + if self.inner.name.as_ref() == "/" { + Arc::from(format!("/{}", builder.name)) + } else { + Arc::from(format!("{}/{}", self.inner.name, builder.name)) + }, builder.subsystem, ErrorActions { on_failure: Atomic::new(builder.failure_action), diff --git a/src/toplevel.rs b/src/toplevel.rs index 1c1c9d1..c799c52 100644 --- a/src/toplevel.rs +++ b/src/toplevel.rs @@ -78,7 +78,7 @@ impl Toplevel { }); let toplevel_subsys = root_handle.start_with_abs_name( - Arc::from(""), + Arc::from("/"), move |s| async move { subsystem(s).await; Result::<(), ErrType>::Ok(()) From bc5b151e059af4ea7df94535855e0dd650a5ee21 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 2 Nov 2024 16:32:19 +0100 Subject: [PATCH 18/18] Add caller tracking --- Cargo.toml | 1 + src/runner.rs | 97 +++++++++++++++++-------------- src/subsystem/subsystem_handle.rs | 2 + src/tokio_task.rs | 2 + src/toplevel.rs | 2 + 5 files changed, 59 insertions(+), 45 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 24a0bac..01d8810 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ exclude = [ ] [features] +# Enable task naming and task caller location. tracing = ["tokio/tracing"] [[example]] diff --git a/src/runner.rs b/src/runner.rs index 4811a1a..c7f7dc8 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -21,6 +21,7 @@ pub(crate) struct SubsystemRunner { } impl SubsystemRunner { + #[track_caller] pub(crate) fn new( name: Arc, subsystem: Subsys, @@ -32,7 +33,7 @@ impl SubsystemRunner { Fut: 'static + Future> + Send, Err: Into, { - let future = async { run_subsystem(name, subsystem, subsystem_handle, guard).await }; + let future = run_subsystem(name, subsystem, subsystem_handle, guard); let aborthandle = crate::tokio_task::spawn(future, "subsystem_runner").abort_handle(); SubsystemRunner { aborthandle } } @@ -44,12 +45,14 @@ impl Drop for SubsystemRunner { } } -async fn run_subsystem( +#[track_caller] +fn run_subsystem( name: Arc, subsystem: Subsys, mut subsystem_handle: SubsystemHandle, guard: AliveGuard, -) where +) -> impl Future + 'static +where Subsys: 'static + FnOnce(SubsystemHandle) -> Fut + Send, Fut: 'static + Future> + Send, Err: Into, @@ -59,52 +62,56 @@ async fn run_subsystem( let future = async { subsystem(subsystem_handle).await.map_err(|e| e.into()) }; let join_handle = crate::tokio_task::spawn(future, &name); - // Abort on drop - guard.on_cancel({ - let abort_handle = join_handle.abort_handle(); - let name = Arc::clone(&name); - move || { - if !abort_handle.is_finished() { - tracing::warn!("Subsystem cancelled: '{}'", name); + async move { + // Abort on drop + guard.on_cancel({ + let abort_handle = join_handle.abort_handle(); + let name = Arc::clone(&name); + move || { + if !abort_handle.is_finished() { + tracing::warn!("Subsystem cancelled: '{}'", name); + } + abort_handle.abort(); } - abort_handle.abort(); - } - }); + }); - let failure = match join_handle.await { - Ok(Ok(())) => None, - Ok(Err(e)) => Some(SubsystemError::Failed(name, SubsystemFailure(e))), - Err(e) => { - // We can assume that this is a panic, because a cancellation - // can never happen as long as we still hold `guard`. - assert!(e.is_panic()); - Some(SubsystemError::Panicked(name)) - } - }; + let failure = match join_handle.await { + Ok(Ok(())) => None, + Ok(Err(e)) => Some(SubsystemError::Failed(name, SubsystemFailure(e))), + Err(e) => { + // We can assume that this is a panic, because a cancellation + // can never happen as long as we still hold `guard`. + assert!(e.is_panic()); + Some(SubsystemError::Panicked(name)) + } + }; - // Retrieve the handle that was passed into the subsystem. - // Originally it was intended to pass the handle as reference, but due - // to complications (https://stackoverflow.com/questions/77172947/async-lifetime-issues-of-pass-by-reference-parameters) - // it was decided to pass ownership instead. - // - // It is still important that the handle does not leak out of the subsystem. - let subsystem_handle = match redirected_subsystem_handle.try_recv() { - Ok(s) => s, - Err(_) => { - tracing::error!("The SubsystemHandle object must not be leaked out of the subsystem!"); - panic!("The SubsystemHandle object must not be leaked out of the subsystem!"); + // Retrieve the handle that was passed into the subsystem. + // Originally it was intended to pass the handle as reference, but due + // to complications (https://stackoverflow.com/questions/77172947/async-lifetime-issues-of-pass-by-reference-parameters) + // it was decided to pass ownership instead. + // + // It is still important that the handle does not leak out of the subsystem. + let subsystem_handle = match redirected_subsystem_handle.try_recv() { + Ok(s) => s, + Err(_) => { + tracing::error!( + "The SubsystemHandle object must not be leaked out of the subsystem!" + ); + panic!("The SubsystemHandle object must not be leaked out of the subsystem!"); + } + }; + + // Raise potential errors + let joiner_token = subsystem_handle.joiner_token; + if let Some(failure) = failure { + joiner_token.raise_failure(failure); } - }; - // Raise potential errors - let joiner_token = subsystem_handle.joiner_token; - if let Some(failure) = failure { - joiner_token.raise_failure(failure); + // Wait for children to finish before we destroy the `SubsystemHandle` object. + // Otherwise the children would be cancelled immediately. + // + // This is the main mechanism that forwards a cancellation to all the children. + joiner_token.downgrade().join().await; } - - // Wait for children to finish before we destroy the `SubsystemHandle` object. - // Otherwise the children would be cancelled immediately. - // - // This is the main mechanism that forwards a cancellation to all the children. - joiner_token.downgrade().join().await; } diff --git a/src/subsystem/subsystem_handle.rs b/src/subsystem/subsystem_handle.rs index c75901f..1547029 100644 --- a/src/subsystem/subsystem_handle.rs +++ b/src/subsystem/subsystem_handle.rs @@ -72,6 +72,7 @@ impl SubsystemHandle { /// Ok(()) /// } /// ``` + #[track_caller] pub fn start( &self, builder: SubsystemBuilder, @@ -96,6 +97,7 @@ impl SubsystemHandle { ) } + #[track_caller] pub(crate) fn start_with_abs_name( &self, name: Arc, diff --git a/src/tokio_task.rs b/src/tokio_task.rs index 80dd1b2..75ed1eb 100644 --- a/src/tokio_task.rs +++ b/src/tokio_task.rs @@ -2,6 +2,7 @@ use std::future::Future; use tokio::task::JoinHandle; #[cfg(not(all(tokio_unstable, feature = "tracing")))] +#[track_caller] pub(crate) fn spawn(f: F, _name: &str) -> JoinHandle where ::Output: Send + 'static, @@ -10,6 +11,7 @@ where } #[cfg(all(tokio_unstable, feature = "tracing"))] +#[track_caller] pub(crate) fn spawn(f: F, name: &str) -> JoinHandle where ::Output: Send + 'static, diff --git a/src/toplevel.rs b/src/toplevel.rs index c799c52..d252bc3 100644 --- a/src/toplevel.rs +++ b/src/toplevel.rs @@ -57,6 +57,7 @@ impl Toplevel { /// * `subsystem` - The subsystem that should be spawned as the root node. /// Usually the job of this subsystem is to spawn further subsystems. #[allow(clippy::new_without_default)] + #[track_caller] pub fn new(subsystem: Subsys) -> Self where Subsys: 'static + FnOnce(SubsystemHandle) -> Fut + Send, @@ -118,6 +119,7 @@ impl Toplevel { /// /// Especially the caveats from [tokio::signal::unix::Signal] are important for Unix targets. /// + #[track_caller] pub fn catch_signals(self) -> Self { let shutdown_token = self.root_handle.get_cancellation_token().clone();