sans_io_runtime/task/
switcher.rs

1use std::{marker::PhantomData, ops::Deref};
2
3use crate::collections::BitVec;
4
5/// Task group outputs state
6/// This is used for jumping between multi groups of tasks
7/// For example, we have two group G1 and G2,
8/// Each cycle we need to call G1.output until it returns None, then we call G2.output until it returns None, then we will have this cycle is finished
9/// Then we will start a new cycle and it loop like that
10///
11/// ```rust
12/// use std::time::Instant;
13/// use sans_io_runtime::TaskSwitcher;
14///
15/// // This will create a task switcher with 2 tasks, use 2 bytes, we can adjust max to 16 tasks
16/// let mut switcher = TaskSwitcher::new(2);
17///
18///
19/// //we need to pop task index from wait queue
20/// switcher.flag_task(0 as usize);
21/// assert_eq!(switcher.current(), Some(0));
22/// switcher.finished(0 as usize);
23/// assert_eq!(switcher.current(), None);
24///
25/// ```
26
27pub trait TaskSwitcherChild<Out> {
28    type Time: Copy;
29    fn empty_event(&self) -> Out;
30    fn is_empty(&self) -> bool;
31    fn pop_output(&mut self, now: Self::Time) -> Option<Out>;
32}
33
34#[derive(Debug)]
35pub struct TaskSwitcherBranch<Task, Out> {
36    task_type: usize,
37    pub task: Task,
38    /// If the task just sent empty output, we set this to true for avoiding stuck with send empty output loop.
39    is_empty: bool,
40    _tmp: PhantomData<Out>,
41}
42
43impl<Task: Default, Out> TaskSwitcherBranch<Task, Out> {
44    pub fn default<TT: Into<usize>>(tt: TT) -> Self {
45        Self {
46            task_type: tt.into(),
47            task: Default::default(),
48            is_empty: false,
49            _tmp: Default::default(),
50        }
51    }
52}
53
54impl<Task, Out> TaskSwitcherBranch<Task, Out> {
55    pub fn new<TT: Into<usize>>(task: Task, tt: TT) -> Self {
56        Self {
57            task_type: tt.into(),
58            task,
59            is_empty: false,
60            _tmp: Default::default(),
61        }
62    }
63
64    pub fn input(&mut self, s: &mut TaskSwitcher) -> &mut Task {
65        s.flag_task(self.task_type);
66        &mut self.task
67    }
68}
69
70impl<Task: TaskSwitcherChild<Out>, Out> TaskSwitcherBranch<Task, Out> {
71    pub fn is_empty(&self) -> bool {
72        self.task.is_empty()
73    }
74
75    pub fn pop_output(&mut self, now: Task::Time, s: &mut TaskSwitcher) -> Option<Out> {
76        let out = self.task.pop_output(now);
77        if out.is_none() {
78            if !self.is_empty {
79                if self.task.is_empty() {
80                    // we will send empty output once, if it's still empty, we will not send again
81                    self.is_empty = true;
82                    return Some(self.task.empty_event());
83                }
84            } else {
85                #[allow(clippy::collapsible_else_if)]
86                if !self.task.is_empty() {
87                    self.is_empty = false;
88                }
89            }
90
91            s.finished(self.task_type);
92        }
93        out
94    }
95}
96
97impl<Task, Out> Deref for TaskSwitcherBranch<Task, Out> {
98    type Target = Task;
99
100    fn deref(&self) -> &Self::Target {
101        &self.task
102    }
103}
104
105#[derive(Debug)]
106pub struct TaskSwitcher {
107    bits: BitVec,
108}
109
110impl TaskSwitcher {
111    pub fn new(len: usize) -> Self {
112        Self {
113            bits: BitVec::news(len),
114        }
115    }
116
117    pub fn set_tasks(&mut self, tasks: usize) {
118        self.bits.set_len(tasks);
119    }
120
121    pub fn tasks(&self) -> usize {
122        self.bits.len()
123    }
124
125    /// Returns the current index of the task group, if it's not finished. Otherwise, returns None.
126    pub fn current(&mut self) -> Option<usize> {
127        self.bits.first_set_index()
128    }
129
130    pub fn flag_all(&mut self) {
131        self.bits.set_all(true);
132    }
133
134    /// Flag that the current task group is finished.
135    pub fn finished<I: Into<usize>>(&mut self, index: I) {
136        self.bits.set_bit(index.into(), false);
137    }
138
139    pub fn flag_task<I: Into<usize>>(&mut self, index: I) {
140        self.bits.set_bit(index.into(), true);
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use crate::TaskSwitcher;
147
148    #[test]
149    fn queue_with_stack_like_style() {
150        let mut state = TaskSwitcher::new(5);
151        state.flag_all();
152
153        assert_eq!(state.current(), Some(0));
154        state.finished(0 as usize);
155        assert_eq!(state.current(), Some(1));
156        state.finished(1 as usize);
157        assert_eq!(state.current(), Some(2));
158        state.finished(2 as usize);
159        assert_eq!(state.current(), Some(3));
160        state.finished(3 as usize);
161        assert_eq!(state.current(), Some(4));
162        state.finished(4 as usize);
163        assert_eq!(state.current(), None);
164
165        state.flag_task(3 as usize);
166
167        assert_eq!(state.current(), Some(3));
168        state.finished(3 as usize);
169        assert_eq!(state.current(), None);
170    }
171
172    #[test]
173    fn queue_test2() {
174        let mut state = TaskSwitcher::new(2);
175        state.flag_all();
176        assert_eq!(state.current(), Some(0));
177        state.finished(0 as usize);
178        assert_eq!(state.current(), Some(1));
179        state.finished(1 as usize);
180        assert_eq!(state.current(), None);
181
182        // next cycle
183        state.flag_all();
184        assert_eq!(state.current(), Some(0));
185        state.finished(0 as usize);
186        assert_eq!(state.current(), Some(1));
187        state.finished(1 as usize);
188        assert_eq!(state.current(), None);
189    }
190
191    //TODO test TaskSwitcherChild
192}