From ef6b64f1474762605aca6ec6c087557fae5ef3e4 Mon Sep 17 00:00:00 2001 From: tison Date: Thu, 12 Dec 2024 09:32:17 +0800 Subject: [PATCH] use examples Signed-off-by: tison --- .github/workflows/ci.yml | 9 ++ fastimer/Cargo.toml | 21 ++- fastimer/examples/common/mod.rs | 53 ++++++++ fastimer/examples/schedule_arbitrary_delay.rs | 75 +++++++++++ fastimer/examples/schedule_at_fixed_rate.rs | 81 ++++++++++++ ...action.rs => schedule_with_fixed_delay.rs} | 46 +++---- fastimer/examples/time_driver.rs | 33 ++++- fastimer/src/driver.rs | 43 ------ fastimer/src/schedule/arbitrary.rs | 83 +----------- fastimer/src/schedule/mod.rs | 4 +- fastimer/src/schedule/simple.rs | 124 +----------------- xtask/src/main.rs | 2 +- 12 files changed, 304 insertions(+), 270 deletions(-) create mode 100644 fastimer/examples/common/mod.rs create mode 100644 fastimer/examples/schedule_arbitrary_delay.rs create mode 100644 fastimer/examples/schedule_at_fixed_rate.rs rename fastimer/examples/{simple_action.rs => schedule_with_fixed_delay.rs} (71%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea02559..c457f3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,6 +67,15 @@ jobs: - name: Run unit tests run: cargo x test --no-capture shell: bash + - name: Run examples + shell: bash + run: | + cargo run --features="driver" --example time_driver + cargo run --features="tokio,logging" --example schedule_arbitrary_delay + cargo run --features="tokio,logging" --example schedule_at_fixed_rate + cargo run --features="tokio,logging" --example schedule_with_fixed_delay + env: + RUST_LOG: DEBUG required: name: Required diff --git a/fastimer/Cargo.toml b/fastimer/Cargo.toml index c1f8775..f956d13 100644 --- a/fastimer/Cargo.toml +++ b/fastimer/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "fastimer" -version = "0.2.4" +version = "0.3.0" description = "This crate implements runtime-agnostic driver for async timers and scheduled tasks." @@ -36,9 +36,8 @@ tokio = ["tokio-time", "tokio-spawn"] tokio-spawn = ["dep:tokio", "tokio/rt-multi-thread"] tokio-time = ["dep:tokio", "tokio/time"] -test = ["tokio", "driver", "logging"] - [dependencies] +if_chain = { version = "1.0.2" } pin-project = { version = "1.1.7" } atomic-waker = { version = "1.1.2", optional = true } @@ -59,8 +58,20 @@ workspace = true [[example]] doc-scrape-examples = true -name = "simple_action" -path = "examples/simple_action.rs" +name = "schedule_arbitrary_delay" +path = "examples/schedule_arbitrary_delay.rs" +required-features = ["tokio", "logging"] + +[[example]] +doc-scrape-examples = true +name = "schedule_at_fixed_rate" +path = "examples/schedule_at_fixed_rate.rs" +required-features = ["tokio", "logging"] + +[[example]] +doc-scrape-examples = true +name = "schedule_with_fixed_delay" +path = "examples/schedule_with_fixed_delay.rs" required-features = ["tokio", "logging"] [[example]] diff --git a/fastimer/examples/common/mod.rs b/fastimer/examples/common/mod.rs new file mode 100644 index 0000000..6aaaded --- /dev/null +++ b/fastimer/examples/common/mod.rs @@ -0,0 +1,53 @@ +// Copyright 2024 FastLabs Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::future::Future; +use std::sync::Arc; +use std::time::Duration; + +use fastimer::tokio::MakeTokioDelay; +use mea::latch::Latch; +use mea::waitgroup::WaitGroup; + +#[derive(Debug, Clone)] +pub struct Shutdown { + latch: Arc, + wg: WaitGroup, +} + +impl Shutdown { + pub fn new() -> Self { + Shutdown { + latch: Arc::new(Latch::new(1)), + wg: WaitGroup::new(), + } + } + + pub fn shutdown(&self) { + self.latch.count_down(); + } + + pub async fn is_shutdown(&self) { + self.latch.wait().await; + } + + pub async fn await_shutdown(self) { + self.wg.await; + } +} + +pub async fn timeout(fut: impl Future) { + const T: Duration = Duration::from_secs(10); + fastimer::timeout(T, fut, MakeTokioDelay).await.unwrap(); +} diff --git a/fastimer/examples/schedule_arbitrary_delay.rs b/fastimer/examples/schedule_arbitrary_delay.rs new file mode 100644 index 0000000..46b993e --- /dev/null +++ b/fastimer/examples/schedule_arbitrary_delay.rs @@ -0,0 +1,75 @@ +// Copyright 2024 FastLabs Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::future::Future; +use std::time::Duration; +use std::time::Instant; + +use fastimer::make_instant_from_now; +use fastimer::schedule::ArbitraryDelayAction; +use fastimer::schedule::ArbitraryDelayActionExt; +use fastimer::schedule::BaseAction; +use fastimer::tokio::MakeTokioDelay; +use fastimer::tokio::TokioSpawn; + +use crate::common::Shutdown; + +mod common; + +#[derive(Debug)] +struct TickAction { + count: u32, + shutdown: Shutdown, +} + +impl BaseAction for TickAction { + fn name(&self) -> &str { + "tick-arbitrary" + } + + fn is_shutdown(&self) -> impl Future + Send { + self.shutdown.is_shutdown() + } +} + +impl ArbitraryDelayAction for TickAction { + async fn run(&mut self) -> Instant { + self.count += 1; + log::info!("tick: {}", self.count); + make_instant_from_now(Duration::from_secs(self.count as u64)) + } +} + +fn main() { + logforth::stderr().apply(); + + let shutdown = Shutdown::new(); + let tick = TickAction { + count: 0, + shutdown: shutdown.clone(), + }; + + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(async move { + tick.schedule( + &TokioSpawn::current(), + MakeTokioDelay, + Some(Duration::from_secs(1)), + ); + + tokio::time::sleep(Duration::from_secs(10)).await; + shutdown.shutdown(); + common::timeout(shutdown.await_shutdown()).await; + }); +} diff --git a/fastimer/examples/schedule_at_fixed_rate.rs b/fastimer/examples/schedule_at_fixed_rate.rs new file mode 100644 index 0000000..910ac7b --- /dev/null +++ b/fastimer/examples/schedule_at_fixed_rate.rs @@ -0,0 +1,81 @@ +// Copyright 2024 FastLabs Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::future::Future; +use std::time::Duration; + +use fastimer::schedule::BaseAction; +use fastimer::schedule::SimpleAction; +use fastimer::schedule::SimpleActionExt; +use fastimer::tokio::MakeTokioDelay; +use fastimer::tokio::TokioSpawn; + +use crate::common::Shutdown; + +mod common; + +#[derive(Debug)] +struct TickAction { + name: String, + count: u32, + sleep: Duration, + shutdown: Shutdown, +} + +impl BaseAction for TickAction { + fn name(&self) -> &str { + &self.name + } + + fn is_shutdown(&self) -> impl Future + Send { + self.shutdown.is_shutdown() + } +} + +impl SimpleAction for TickAction { + async fn run(&mut self) { + self.count += 1; + log::info!("[{}] tick start: {}", self.name(), self.count); + tokio::time::sleep(self.sleep).await; + log::info!("[{}] tick end: {}", self.name(), self.count); + } +} + +async fn do_schedule_at_fixed_rate(sleep: u64, period: u64) { + let shutdown = Shutdown::new(); + let tick = TickAction { + name: format!("fixed-rate-{sleep}/{period}"), + count: 0, + sleep: Duration::from_secs(sleep), + shutdown: shutdown.clone(), + }; + tick.schedule_at_fixed_rate( + &TokioSpawn::current(), + MakeTokioDelay, + None, + Duration::from_secs(period), + ); + tokio::time::sleep(Duration::from_secs(10)).await; + shutdown.shutdown(); + common::timeout(shutdown.await_shutdown()).await; +} + +fn main() { + logforth::stderr().apply(); + + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(do_schedule_at_fixed_rate(1, 2)); + rt.block_on(do_schedule_at_fixed_rate(3, 2)); + rt.block_on(do_schedule_at_fixed_rate(5, 2)); +} diff --git a/fastimer/examples/simple_action.rs b/fastimer/examples/schedule_with_fixed_delay.rs similarity index 71% rename from fastimer/examples/simple_action.rs rename to fastimer/examples/schedule_with_fixed_delay.rs index 487da44..3f418ec 100644 --- a/fastimer/examples/simple_action.rs +++ b/fastimer/examples/schedule_with_fixed_delay.rs @@ -13,46 +13,48 @@ // limitations under the License. use std::future::Future; -use std::sync::Arc; use std::time::Duration; +use fastimer::schedule::BaseAction; use fastimer::schedule::SimpleAction; use fastimer::schedule::SimpleActionExt; use fastimer::tokio::MakeTokioDelay; use fastimer::tokio::TokioSpawn; -use mea::latch::Latch; -use mea::waitgroup::WaitGroup; +use crate::common::Shutdown; + +mod common; + +#[derive(Debug)] struct TickAction { count: u32, - - latch: Arc, - _wg: WaitGroup, + shutdown: Shutdown, } -impl SimpleAction for TickAction { +impl BaseAction for TickAction { fn name(&self) -> &str { - "tick" + "tick-fixed-delay" } - async fn run(&mut self) { - self.count += 1; - println!("tick: {}", self.count); + fn is_shutdown(&self) -> impl Future + Send { + self.shutdown.is_shutdown() } +} - fn is_shutdown(&self) -> impl Future + Send { - self.latch.wait() +impl SimpleAction for TickAction { + async fn run(&mut self) { + self.count += 1; + log::info!("tick: {}", self.count); } } fn main() { - let wg = WaitGroup::new(); - let latch = Arc::new(Latch::new(1)); + logforth::stderr().apply(); + let shutdown = Shutdown::new(); let tick = TickAction { count: 0, - latch: latch.clone(), - _wg: wg.clone(), + shutdown: shutdown.clone(), }; let rt = tokio::runtime::Runtime::new().unwrap(); @@ -60,14 +62,12 @@ fn main() { tick.schedule_with_fixed_delay( &TokioSpawn::current(), MakeTokioDelay, - None, + Some(Duration::from_secs(1)), Duration::from_secs(1), ); - tokio::time::sleep(Duration::from_secs(5)).await; - latch.count_down(); - fastimer::timeout(Duration::from_secs(5), wg, MakeTokioDelay) - .await - .unwrap(); + tokio::time::sleep(Duration::from_secs(10)).await; + shutdown.shutdown(); + common::timeout(shutdown.await_shutdown()).await; }); } diff --git a/fastimer/examples/time_driver.rs b/fastimer/examples/time_driver.rs index 5dd2a0d..c56778e 100644 --- a/fastimer/examples/time_driver.rs +++ b/fastimer/examples/time_driver.rs @@ -12,16 +12,41 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::time::Duration; +use std::time::Instant; + +use fastimer::make_instant_from_now; + +fn assert_duration_eq(actual: Duration, expected: Duration) { + if expected.abs_diff(expected) > Duration::from_millis(5) { + panic!("expected: {:?}, actual: {:?}", expected, actual); + } +} + fn main() { let (mut driver, context, shutdown) = fastimer::driver::driver(); - + let (tx, rx) = std::sync::mpsc::channel(); std::thread::spawn(move || loop { if driver.turn() { + tx.send(()).unwrap(); break; } }); - let delay = context.delay(std::time::Duration::from_secs(1)); - pollster::block_on(delay); // finish after 1 second - shutdown.shutdown(); + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(async move { + let now = Instant::now(); + + context.delay(Duration::from_secs(2)).await; + assert_duration_eq(now.elapsed(), Duration::from_secs(2)); + + let future = make_instant_from_now(Duration::from_secs(3)); + let f1 = context.delay_until(future); + let f2 = context.delay_until(future); + tokio::join!(f1, f2); + assert_duration_eq(now.elapsed(), Duration::from_secs(3)); + + shutdown.shutdown(); + }); + rx.recv_timeout(Duration::from_secs(1)).unwrap(); } diff --git a/fastimer/src/driver.rs b/fastimer/src/driver.rs index 605a7c4..0f8476f 100644 --- a/fastimer/src/driver.rs +++ b/fastimer/src/driver.rs @@ -248,46 +248,3 @@ impl MakeDelay for MakeFastimerDelay { self.0.delay(duration) } } - -#[cfg(all(test, feature = "test"))] -mod tests { - use std::time::Duration; - use std::time::Instant; - - use crate::make_instant_from_now; - - #[test] - fn test_time_driver() { - let (mut driver, context, shutdown) = super::driver(); - let (tx, rx) = std::sync::mpsc::channel(); - std::thread::spawn(move || loop { - if driver.turn() { - tx.send(()).unwrap(); - break; - } - }); - - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(async move { - let now = Instant::now(); - - context.delay(Duration::from_secs(2)).await; - assert_duration_eq(now.elapsed(), Duration::from_secs(2)); - - let future = make_instant_from_now(Duration::from_secs(3)); - let f1 = context.delay_until(future); - let f2 = context.delay_until(future); - tokio::join!(f1, f2); - assert_duration_eq(now.elapsed(), Duration::from_secs(3)); - - shutdown.shutdown(); - }); - rx.recv_timeout(Duration::from_secs(1)).unwrap(); - } - - fn assert_duration_eq(actual: Duration, expected: Duration) { - if expected.abs_diff(expected) > Duration::from_millis(5) { - panic!("expected: {:?}, actual: {:?}", expected, actual); - } - } -} diff --git a/fastimer/src/schedule/arbitrary.rs b/fastimer/src/schedule/arbitrary.rs index 6d0cb31..398996b 100644 --- a/fastimer/src/schedule/arbitrary.rs +++ b/fastimer/src/schedule/arbitrary.rs @@ -16,14 +16,13 @@ use std::future::Future; use std::time::Duration; use std::time::Instant; -use crate::schedule::select::select; -use crate::schedule::select::Either; use crate::schedule::shutdown_or_delay; +use crate::schedule::BaseAction; use crate::MakeDelay; use crate::Spawn; /// Repeatable action that can be scheduled with arbitrary delay. -pub trait ArbitraryDelayAction: Send + 'static { +pub trait ArbitraryDelayAction: BaseAction { /// Run the action. /// /// Return an Instant that indicates when to schedule the next run. @@ -48,12 +47,11 @@ pub trait ArbitraryDelayActionExt: ArbitraryDelayAction { initial_delay ); - if let Some(initial_delay) = initial_delay { - if initial_delay > Duration::ZERO { - if shutdown_or_delay(&mut self, make_delay.delay(initial_delay)).await { - return; - } - } + if_chain::if_chain! { + if let Some(initial_delay) = initial_delay; + if initial_delay > Duration::ZERO; + if shutdown_or_delay(&mut self, make_delay.delay(initial_delay)).await; + then { return; } } loop { @@ -70,70 +68,3 @@ pub trait ArbitraryDelayActionExt: ArbitraryDelayAction { } impl ArbitraryDelayActionExt for T {} - -#[cfg(all(test, feature = "test"))] -mod tests { - use std::future::Future; - use std::sync::Arc; - use std::time::Duration; - use std::time::Instant; - - use mea::latch::Latch; - use mea::waitgroup::WaitGroup; - - use crate::schedule::ArbitraryDelayAction; - use crate::schedule::ArbitraryDelayActionExt; - use crate::timeout; - use crate::tokio::MakeTokioDelay; - use crate::tokio::TokioSpawn; - - struct TickAction { - name: String, - count: u32, - - latch: Arc, - _wg: WaitGroup, - } - - impl ArbitraryDelayAction for TickAction { - fn name(&self) -> &str { - &self.name - } - - async fn run(&mut self) -> Instant { - self.count += 1; - log::info!("[{}] tick count: {}", self.name, self.count); - Instant::now() + Duration::from_secs(self.count as u64) - } - - fn is_shutdown(&self) -> impl Future + Send { - self.latch.wait() - } - } - - #[test] - fn test_schedule() { - let _ = logforth::stderr().try_apply(); - - let rt = tokio::runtime::Runtime::new().unwrap(); - let wg = WaitGroup::new(); - let latch = Arc::new(Latch::new(1)); - - rt.block_on(async move { - let action = TickAction { - name: "fixed-delay".to_string(), - count: 0, - latch: latch.clone(), - _wg: wg.clone(), - }; - - action.schedule(&TokioSpawn::current(), MakeTokioDelay, None); - - tokio::time::sleep(Duration::from_secs(10)).await; - latch.count_down(); - timeout(Duration::from_secs(5), wg, MakeTokioDelay) - .await - .unwrap(); - }); - } -} diff --git a/fastimer/src/schedule/mod.rs b/fastimer/src/schedule/mod.rs index 93a57f7..8c7e519 100644 --- a/fastimer/src/schedule/mod.rs +++ b/fastimer/src/schedule/mod.rs @@ -20,9 +20,11 @@ mod arbitrary; pub use arbitrary::*; mod simple; -use crate::schedule::select::{select, Either}; pub use simple::*; +use crate::schedule::select::select; +use crate::schedule::select::Either; + mod select; /// Base trait for shutdown-able scheduled actions. diff --git a/fastimer/src/schedule/simple.rs b/fastimer/src/schedule/simple.rs index d52add5..0b4586d 100644 --- a/fastimer/src/schedule/simple.rs +++ b/fastimer/src/schedule/simple.rs @@ -19,9 +19,8 @@ use std::time::Instant; use crate::far_future; use crate::make_instant_from; use crate::make_instant_from_now; -use crate::schedule::select::select; -use crate::schedule::select::Either; -use crate::schedule::{shutdown_or_delay, BaseAction}; +use crate::schedule::shutdown_or_delay; +use crate::schedule::BaseAction; use crate::MakeDelay; use crate::Spawn; @@ -58,12 +57,11 @@ pub trait SimpleActionExt: SimpleAction { initial_delay ); - if let Some(initial_delay) = initial_delay { - if initial_delay > Duration::ZERO { - if shutdown_or_delay(&mut self, make_delay.delay(initial_delay)).await { - return; - } - } + if_chain::if_chain! { + if let Some(initial_delay) = initial_delay; + if initial_delay > Duration::ZERO; + if shutdown_or_delay(&mut self, make_delay.delay(initial_delay)).await; + then { return; } } loop { @@ -163,111 +161,3 @@ pub trait SimpleActionExt: SimpleAction { } impl SimpleActionExt for T {} - -#[cfg(all(test, feature = "test"))] -mod tests { - use std::future::Future; - use std::sync::Arc; - use std::time::Duration; - - use mea::latch::Latch; - use mea::waitgroup::WaitGroup; - - use super::SimpleActionExt; - use crate::timeout; - use crate::tokio::MakeTokioDelay; - use crate::tokio::TokioSpawn; - - struct TickAction { - name: String, - count: u32, - sleep: Duration, - - latch: Arc, - _wg: WaitGroup, - } - - impl super::SimpleAction for TickAction { - fn name(&self) -> &str { - &self.name - } - - async fn run(&mut self) { - self.count += 1; - log::info!("[{}] tick count: {}", self.name, self.count); - tokio::time::sleep(self.sleep).await; - } - - fn is_shutdown(&self) -> impl Future + Send { - self.latch.wait() - } - } - - #[test] - fn test_schedule_with_fixed_delay() { - let _ = logforth::stderr().try_apply(); - - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(async move { - let wg = WaitGroup::new(); - let latch = Arc::new(Latch::new(1)); - - let action = TickAction { - name: "fixed-delay".to_string(), - count: 0, - sleep: Duration::from_secs(1), - latch: latch.clone(), - _wg: wg.clone(), - }; - - action.schedule_with_fixed_delay( - &TokioSpawn::current(), - MakeTokioDelay, - None, - Duration::from_secs(2), - ); - - tokio::time::sleep(Duration::from_secs(10)).await; - latch.count_down(); - timeout(Duration::from_secs(5), wg, MakeTokioDelay) - .await - .unwrap(); - }); - } - - #[test] - fn test_schedule_at_fixed_rate() { - let _ = logforth::stderr().try_apply(); - - async fn do_schedule_at_fixed_rate(sleep: u64, period: u64) { - let wg = WaitGroup::new(); - let latch = Arc::new(Latch::new(1)); - - let action = TickAction { - name: format!("fixed-rate-{sleep}/{period}"), - count: 0, - sleep: Duration::from_secs(sleep), - latch: latch.clone(), - _wg: wg.clone(), - }; - - action.schedule_at_fixed_rate( - &TokioSpawn::current(), - MakeTokioDelay, - None, - Duration::from_secs(period), - ); - - tokio::time::sleep(Duration::from_secs(10)).await; - latch.count_down(); - timeout(Duration::from_secs(5), wg, MakeTokioDelay) - .await - .unwrap(); - } - - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(do_schedule_at_fixed_rate(1, 2)); - rt.block_on(do_schedule_at_fixed_rate(3, 2)); - rt.block_on(do_schedule_at_fixed_rate(5, 2)); - } -} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index f1e69af..8f0513b 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -63,7 +63,7 @@ struct CommandTest { impl CommandTest { fn run(self) { - run_command(make_test_cmd(self.no_capture, true, &["test"])); + run_command(make_test_cmd(self.no_capture, true, &[])); } }