1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! This crate provides a way to switch between tasks on single-thread or embedded environments utilizing

//! [cooperative multitasking](https://en.wikipedia.org/wiki/Cooperative_multitasking).

//!

//! It uses async/await mechanisms of Rust, tasks should have manual suspension points inside

//! async functions to allow scheduler to switch them (see [`yield_once!()`](macro.yield_once.html)

//! macro). Tasks can be dynamically [created]/[cancelled]/[suspended]/[resumed]

//! and can `await` external events (e.g from other threads or interrupts) as any normal async

//! functions. For more information about scheduler

//! see [`Wheel`](struct.Wheel.html).

//!

//! [created]: struct.WheelHandle.html#method.spawn

//! [cancelled]: struct.WheelHandle.html#method.cancel

//! [suspended]: struct.WheelHandle.html#method.suspend

//! [resumed]: struct.WheelHandle.html#method.resume

//! # Examples

//! Simple program that reads data from sensor and processes it.

//! ```

//! # extern crate alloc;

//! use juggle::*;

//! use alloc::collections::VecDeque;

//! use core::cell::RefCell;

//!

//! # use core::sync::atomic::*;

//! # async fn read_temperature_sensor()->i32 { 10 }

//! # fn init_timer(){}

//! # static CNT: AtomicUsize = AtomicUsize::new(0);

//! # fn get_timer_value()->u32 { CNT.fetch_add(1,Ordering::Relaxed) as _ }

//! # fn reset_timer(){CNT.store(0,Ordering::Relaxed);}

//! # fn shutdown_timer(){}

//! # fn process_data(queue: &mut VecDeque<i32>){

//! #     while let Some(_) = queue.pop_front() { }

//! # }

//! async fn collect_temperature(queue: &RefCell<VecDeque<i32>>,handle: WheelHandle<'_>){

//!     loop{ // loop forever or until cancelled

//!         let temperature: i32 = read_temperature_sensor().await;

//!         queue.borrow_mut().push_back(temperature);

//!         yield_once!(); // give scheduler opportunity to execute other tasks

//!     }

//! }

//!

//! async fn wait_for_timer(id: IdNum,queue: &RefCell<VecDeque<i32>>,handle: WheelHandle<'_>){

//!     init_timer();

//!     for _ in 0..5 {

//!         yield_while!(get_timer_value() < 200); // busy wait but also executes other tasks.

//!         process_data(&mut queue.borrow_mut());

//!         reset_timer();

//!     }

//!     handle.cancel(id); // cancel 'collect_temperature' task.

//!     shutdown_timer();

//! }

//!

//! fn main(){

//!     let queue = &RefCell::new(VecDeque::new());

//!     let wheel = Wheel::new();

//!     let handle = wheel.handle(); // handle to manage tasks, can be cloned inside this thread

//!

//!     let temp_id = handle.spawn(SpawnParams::default(),

//!                                collect_temperature(queue,handle.clone()));

//!     handle.spawn(SpawnParams::default(),

//!                  wait_for_timer(temp_id.unwrap(),queue,handle.clone()));

//!

//!     // execute tasks

//!     smol::block_on(wheel).unwrap(); // or any other utility to block on future.

//! }

//! ```



#![warn(missing_docs)]
#![warn(missing_debug_implementations)]
#![cfg_attr(not(feature = "std"), no_std)]

#![warn(clippy::cargo,
    clippy::needless_borrow,
    clippy::pedantic,
    clippy::nursery)]

extern crate alloc;

pub mod utils;
mod round;
mod yield_helper;
mod block;

pub use self::block::{block_on, spin_block_on};
pub use self::round::{IdNum, LockedWheel, SpawnParams, State, SuspendError, Wheel, WheelHandle};
pub use self::yield_helper::{Yield, YieldTimes, YieldWhile};



/// Yield current task. Gives the scheduler opportunity to switch to another task.

/// Equivalent to [`Yield::once().await`](struct.Yield.html#method.once).

///

/// # Examples

///```

/// # #[macro_use]

/// # extern crate juggle;

/// # fn do_some_work(){}

/// # fn do_more_work(){}

/// # fn do_even_more_work(){}

/// async fn some_task(){

///     do_some_work();

///     yield_once!();

///     do_more_work();

///     yield_once!();

///     do_even_more_work();

/// }

/// # fn main(){ smol::block_on(some_task()); }

/// ```

#[macro_export]
macro_rules! yield_once {
    () => {
        $crate::Yield::once().await
    }
}


/// Yield current task until specific expression becomes false.

/// Gives the scheduler opportunity to switch to another task.

///

/// It is recommended to use this function instead of busy wait inside tasks within scheduler.

///

/// # Examples

///```

/// # #[macro_use]

/// # extern crate juggle;

/// # fn init_external_timer(){}

/// # fn get_timer_value()->u32{ 20 }

/// # fn shutdown_external_timer(){ }

/// # fn do_some_work(){}

/// async fn timer_task(){

///     init_external_timer();

///     yield_while!(get_timer_value() < 10);

///     do_some_work();

///     shutdown_external_timer();

/// }

/// # fn main(){ smol::block_on(timer_task()); }

/// ```

#[macro_export]
macro_rules! yield_while {
    ($test_expr:expr) => {
        $crate::Yield::yield_while(||{
            let ret: bool = $test_expr;
            ret
        }).await
    }
}