juggle/
lib.rs

1//! This crate provides a way to switch between tasks on single-thread or embedded environments utilizing
2//! [cooperative multitasking](https://en.wikipedia.org/wiki/Cooperative_multitasking).
3//!
4//! It uses async/await mechanisms of Rust, tasks should have manual suspension points inside
5//! async functions to allow scheduler to switch them (see [`yield_once!()`](macro.yield_once.html)
6//! macro). Tasks can be dynamically [created]/[cancelled]/[suspended]/[resumed]
7//! and can `await` external events (e.g from other threads or interrupts) as any normal async
8//! functions. For more information about scheduler
9//! see [`Wheel`](struct.Wheel.html).
10//!
11//! [created]: struct.WheelHandle.html#method.spawn
12//! [cancelled]: struct.WheelHandle.html#method.cancel
13//! [suspended]: struct.WheelHandle.html#method.suspend
14//! [resumed]: struct.WheelHandle.html#method.resume
15//! # Examples
16//! Simple program that reads data from sensor and processes it.
17//! ```
18//! # extern crate alloc;
19//! use juggle::*;
20//! use alloc::collections::VecDeque;
21//! use core::cell::RefCell;
22//!
23//! # use core::sync::atomic::*;
24//! # async fn read_temperature_sensor()->i32 { 10 }
25//! # fn init_timer(){}
26//! # static CNT: AtomicUsize = AtomicUsize::new(0);
27//! # fn get_timer_value()->u32 { CNT.fetch_add(1,Ordering::Relaxed) as _ }
28//! # fn reset_timer(){CNT.store(0,Ordering::Relaxed);}
29//! # fn shutdown_timer(){}
30//! # fn process_data(queue: &mut VecDeque<i32>){
31//! #     while let Some(_) = queue.pop_front() { }
32//! # }
33//! async fn collect_temperature(queue: &RefCell<VecDeque<i32>>,handle: WheelHandle<'_>){
34//!     loop{ // loop forever or until cancelled
35//!         let temperature: i32 = read_temperature_sensor().await;
36//!         queue.borrow_mut().push_back(temperature);
37//!         yield_once!(); // give scheduler opportunity to execute other tasks
38//!     }
39//! }
40//!
41//! async fn wait_for_timer(id: IdNum,queue: &RefCell<VecDeque<i32>>,handle: WheelHandle<'_>){
42//!     init_timer();
43//!     for _ in 0..5 {
44//!         yield_while!(get_timer_value() < 200); // busy wait but also executes other tasks.
45//!         process_data(&mut queue.borrow_mut());
46//!         reset_timer();
47//!     }
48//!     handle.cancel(id); // cancel 'collect_temperature' task.
49//!     shutdown_timer();
50//! }
51//!
52//! fn main(){
53//!     let queue = &RefCell::new(VecDeque::new());
54//!     let wheel = Wheel::new();
55//!     let handle = wheel.handle(); // handle to manage tasks, can be cloned inside this thread
56//!
57//!     let temp_id = handle.spawn(SpawnParams::default(),
58//!                                collect_temperature(queue,handle.clone()));
59//!     handle.spawn(SpawnParams::default(),
60//!                  wait_for_timer(temp_id.unwrap(),queue,handle.clone()));
61//!
62//!     // execute tasks
63//!     smol::block_on(wheel).unwrap(); // or any other utility to block on future.
64//! }
65//! ```
66
67
68#![warn(missing_docs)]
69#![warn(missing_debug_implementations)]
70#![cfg_attr(not(feature = "std"), no_std)]
71
72#![warn(clippy::cargo,
73    clippy::needless_borrow,
74    clippy::pedantic,
75    clippy::nursery)]
76
77extern crate alloc;
78
79pub mod utils;
80mod round;
81mod yield_helper;
82mod block;
83
84pub use self::block::{block_on, spin_block_on};
85pub use self::round::{IdNum, LockedWheel, SpawnParams, State, SuspendError, Wheel, WheelHandle};
86pub use self::yield_helper::{Yield, YieldTimes, YieldWhile};
87
88
89
90/// Yield current task. Gives the scheduler opportunity to switch to another task.
91/// Equivalent to [`Yield::once().await`](struct.Yield.html#method.once).
92///
93/// # Examples
94///```
95/// # #[macro_use]
96/// # extern crate juggle;
97/// # fn do_some_work(){}
98/// # fn do_more_work(){}
99/// # fn do_even_more_work(){}
100/// async fn some_task(){
101///     do_some_work();
102///     yield_once!();
103///     do_more_work();
104///     yield_once!();
105///     do_even_more_work();
106/// }
107/// # fn main(){ smol::block_on(some_task()); }
108/// ```
109#[macro_export]
110macro_rules! yield_once {
111    () => {
112        $crate::Yield::once().await
113    }
114}
115
116
117/// Yield current task until specific expression becomes false.
118/// Gives the scheduler opportunity to switch to another task.
119///
120/// It is recommended to use this function instead of busy wait inside tasks within scheduler.
121///
122/// # Examples
123///```
124/// # #[macro_use]
125/// # extern crate juggle;
126/// # fn init_external_timer(){}
127/// # fn get_timer_value()->u32{ 20 }
128/// # fn shutdown_external_timer(){ }
129/// # fn do_some_work(){}
130/// async fn timer_task(){
131///     init_external_timer();
132///     yield_while!(get_timer_value() < 10);
133///     do_some_work();
134///     shutdown_external_timer();
135/// }
136/// # fn main(){ smol::block_on(timer_task()); }
137/// ```
138#[macro_export]
139macro_rules! yield_while {
140    ($test_expr:expr) => {
141        $crate::Yield::yield_while(||{
142            let ret: bool = $test_expr;
143            ret
144        }).await
145    }
146}