diff --git a/Cargo.lock b/Cargo.lock index deed4ae1d9..bf4157ef2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1378,6 +1378,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "dyn-clone" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" + [[package]] name = "ecdsa" version = "0.14.8" @@ -4774,6 +4780,7 @@ dependencies = [ name = "reth-tasks" version = "0.1.0" dependencies = [ + "dyn-clone", "futures-util", "thiserror", "tokio", diff --git a/crates/tasks/Cargo.toml b/crates/tasks/Cargo.toml index 08164bb582..6d03dfccad 100644 --- a/crates/tasks/Cargo.toml +++ b/crates/tasks/Cargo.toml @@ -8,11 +8,16 @@ readme = "README.md" description = "Task management" [dependencies] + +## async tokio = { version = "1", features = ["sync", "rt"] } tracing-futures = "0.2" -tracing = { version = "0.1", default-features = false } futures-util = "0.3" + +## misc +tracing = { version = "0.1", default-features = false } thiserror = "1.0" +dyn-clone = "1.0" [dev-dependencies] tokio = { version = "1", features = ["sync", "rt", "rt-multi-thread", "time", "macros"] } diff --git a/crates/tasks/src/lib.rs b/crates/tasks/src/lib.rs index 73b2ef8eb0..a2163f70b4 100644 --- a/crates/tasks/src/lib.rs +++ b/crates/tasks/src/lib.rs @@ -8,6 +8,7 @@ //! reth task management use crate::shutdown::{signal, Shutdown, Signal}; +use dyn_clone::DynClone; use futures_util::{ future::{select, BoxFuture}, pin_mut, Future, FutureExt, @@ -30,17 +31,48 @@ pub mod shutdown; /// /// The main purpose of this type is to abstract over [TaskExecutor] so it's more convenient to /// provide default impls for testing. -pub trait TaskSpawner: Send + Sync { +/// +/// +/// # Examples +/// +/// Use the [TokioTaskExecutor] that spawns with [tokio::task::spawn] +/// +/// ``` +/// # async fn t() { +/// use reth_tasks::{TaskSpawner, TokioTaskExecutor}; +/// let executor = TokioTaskExecutor::default(); +/// +/// let task = executor.spawn(Box::pin(async { +/// // -- snip -- +/// })); +/// task.await.unwrap(); +/// # } +/// ``` +/// +/// Use the [TaskExecutor] that spawns task directly onto the tokio runtime via the [Handle]. +/// +/// ``` +/// # use reth_tasks::TaskManager; +/// fn t() { +/// use reth_tasks::TaskSpawner; +/// let rt = tokio::runtime::Runtime::new().unwrap(); +/// let manager = TaskManager::new(rt.handle().clone()); +/// let executor = manager.executor(); +/// let task = TaskSpawner::spawn(&executor, Box::pin(async { +/// // -- snip -- +/// })); +/// rt.block_on(task).unwrap(); +/// # } +/// ``` +/// +/// The [TaskSpawner] trait is [DynClone] so `Box` are also `Clone`. +pub trait TaskSpawner: Send + Sync + DynClone { /// Spawns the task onto the runtime. /// See also [`Handle::spawn`]. fn spawn(&self, fut: BoxFuture<'static, ()>) -> JoinHandle<()>; } -impl TaskSpawner for Box { - fn spawn(&self, fut: BoxFuture<'static, ()>) -> JoinHandle<()> { - (**self).spawn(fut) - } -} +dyn_clone::clone_trait_object!(TaskSpawner); /// An [TaskSpawner] that uses [tokio::task::spawn] to execute tasks #[derive(Debug, Clone, Default)] @@ -313,6 +345,20 @@ mod tests { use super::*; use std::time::Duration; + #[test] + fn test_cloneable() { + #[derive(Clone)] + struct ExecutorWrapper { + _e: Box, + } + + let executor: Box = Box::::default(); + let _e = dyn_clone::clone_box(&*executor); + + let e = ExecutorWrapper { _e }; + let _e2 = e; + } + #[test] fn test_critical() { let runtime = tokio::runtime::Runtime::new().unwrap();