Skip to main content

clockctrl/
ctrl.rs

1use crate::{Error, Interval, Target};
2use async_std::{
3    sync::{Arc, Barrier},
4    task,
5};
6use std::{collections::BTreeMap, hash::Hash};
7
8/// A control object for different clocking scopes
9///
10/// Each clock target can be configured individually via the [`Target`]
11/// type, returned by [`setup()`].  Additionally you need to provide
12/// some type that implements `Hash`.  It's recomended to just use an
13/// enum that can be mapped onto each of your reactors internal tasks.
14///
15/// [`Target`]: struct.Target.html
16/// [`setup()`]: struct.ClockCtrl.html#method.setup
17pub struct ClockCtrl<K>
18where
19    K: Hash + Ord,
20{
21    clocks: BTreeMap<K, Target>,
22}
23
24/// A wrapper type around different clocking strategies
25///
26/// This type is returned by the `ClockCtl::start` function, to
27/// provide an easy hook for any consumer of this API to regulate
28/// their internal scheduling.  For details on what the two run modes
29/// are, consult the variant docs.
30pub enum Scheduler {
31    /// The clocking schedule is constrained internally
32    ///
33    /// This corresponds to a clock type that was configured via the
34    /// builder API, and can internally to the `ClockCtl` controller
35    /// regulate the schedule of the selected task.  The only thing
36    /// for you to do is poll the provided Barrier.
37    Internal(Arc<Barrier>),
38    /// The clock needs to be controlled externally
39    ///
40    /// This corresponds to not setting any additional constraints on
41    /// the `Clock` builder, and instead letting the consumer of this
42    /// API regulate itself: the clock control is only used as a
43    /// toggle to determine it's runtime behaviour.
44    External {
45        /// A delay factor that can be added to any low-power timing
46        /// inside the reactor
47        delay: f32,
48        /// One half of the barrier (give to task)
49        a: Arc<Barrier>,
50        /// Other half of the barrier (give to scheduler)
51        b: Arc<Barrier>,
52    },
53}
54
55impl<K> ClockCtrl<K>
56where
57    K: Hash + Ord,
58{
59    /// Create a new, empty clock controller
60    pub fn new() -> Self {
61        Self {
62            clocks: Default::default(),
63        }
64    }
65
66    /// Override the default clocking scheme for a particular target
67    ///
68    /// It's already possible to constrain clock settings witout
69    /// setting custom bounds, just because the consumer of the
70    /// `ClockCtl` type can fall back to some defaults when this
71    /// builder returns an object filled with `None`.
72    ///
73    /// Canonically, the default constraints could be used to enable a
74    /// low battery mode, whereas more low power embedded platforms
75    /// can be further optimised.
76    pub fn setup(&mut self, trgt: K) -> &mut Target {
77        self.clocks.entry(trgt).or_insert(Target::default())
78    }
79
80    /// Start clock scheduler for a given task
81    ///
82    /// This function returns a Barrier which can be used in the
83    /// corresponding task.
84    pub fn start(&mut self, target: K) -> Result<Scheduler, Error> {
85        let b = Arc::new(Barrier::new(2));
86        match self.clocks.remove(&target) {
87            Some(Target { interval, fence }) => match (interval, fence) {
88                // A raw external scheduler
89                (None, None) => Ok(Scheduler::External {
90                    delay: 1.0,
91                    a: Arc::clone(&b),
92                    b,
93                }),
94
95                // An external scheduler, with a delay modifier
96                (Some(Interval::Delay(d)), None) => Ok(Scheduler::External {
97                    delay: d,
98                    a: Arc::clone(&b),
99                    b,
100                }),
101
102                // A linearly timed internal scheduler
103                (Some(Interval::Timed(dur)), None) => {
104                    let a = Arc::clone(&b);
105                    task::spawn(async move {
106                        loop {
107                            task::sleep(dur).await;
108                            a.wait().await;
109                        }
110                    });
111
112                    Ok(Scheduler::Internal(b))
113                }
114
115                // A manually clock stepped fence scheduler
116                (Some(Interval::Stepped), Some(fence)) => {
117                    let a = Arc::clone(&b);
118                    task::spawn(async move {
119                        fence(a);
120                    });
121
122                    Ok(Scheduler::Internal(b))
123                },
124                
125                (_, _) => panic!("Invalid scheduler setup"),
126            },
127            None => Err(Error::NoTarget),
128        }
129    }
130}