freertos_rust/patterns/
compute_task.rs

1use crate::base::*;
2use crate::mutex::*;
3use crate::prelude::v1::*;
4use crate::queue::*;
5use crate::task::*;
6use crate::units::*;
7
8pub trait ComputeTaskBuilder {
9    fn compute<F, R>(&self, func: F) -> Result<ComputeTask<R>, FreeRtosError>
10    where
11        F: FnOnce() -> R,
12        F: Send + 'static,
13        R: Sync + Send + 'static;
14}
15
16impl ComputeTaskBuilder for TaskBuilder {
17    #[cfg(target_os = "none")]
18    /// Spawn a task that can post a return value to the outside.
19    fn compute<F, R>(&self, func: F) -> Result<ComputeTask<R>, FreeRtosError>
20    where
21        F: FnOnce() -> R,
22        F: Send + 'static,
23        R: Sync + Send + 'static,
24    {
25        let (task, result, status) = {
26            let result = Arc::new(Mutex::new(None)?);
27            let status = Arc::new(Queue::new(1)?);
28
29            let task_result = result.clone();
30            let task_status = status.clone();
31            let task = self.start(move |_this_task| {
32                {
33                    let mut lock = task_result.lock(Duration::infinite()).unwrap();
34                    let r = func();
35                    *lock = Some(r);
36                }
37                // release our reference to the mutex, so it can be deconstructed
38                drop(task_result);
39                task_status
40                    .send(ComputeTaskStatus::Finished, Duration::infinite())
41                    .unwrap();
42            })?;
43
44            (task, result, status)
45        };
46
47        Ok(ComputeTask {
48            task: task,
49            result: result,
50            status: status,
51            finished: false,
52        })
53    }
54
55    #[cfg(not(target_os = "none"))]
56    fn compute<F, R>(&self, func: F) -> Result<ComputeTask<R>, FreeRtosError>
57    where
58        F: FnOnce() -> R,
59        F: Send + 'static,
60        R: Sync + Send + 'static,
61    {
62        let r = func();
63
64        Ok(ComputeTask {
65            task: Task::new().start(|_this_task| {}).unwrap(),
66            result: Arc::new(Mutex::new(Some(r)).unwrap()),
67            status: Arc::new(Queue::new(1).unwrap()),
68            finished: false,
69        })
70    }
71}
72
73/// A task that can terminate and return its return value. Implemented using an
74/// atomically shared mutex.
75///
76/// Sample usage:
77///
78/// ```rust
79/// # use freertos_rs::*;
80/// use freertos_rs::patterns::compute_task::*;
81/// let task = Task::new().compute(|| {
82/// 	CurrentTask::delay(Duration::ms(100));
83/// 	42
84/// }).unwrap();
85///
86/// let result = task.into_result(Duration::ms(1000)).unwrap();
87/// # println!("{}", result);
88/// ```
89
90pub struct ComputeTask<R> {
91    task: Task,
92    result: Arc<Mutex<Option<R>>>,
93    status: Arc<Queue<ComputeTaskStatus>>,
94    finished: bool,
95}
96
97#[allow(dead_code)]
98#[derive(Debug, Copy, Clone)]
99enum ComputeTaskStatus {
100    Finished,
101}
102
103use core::fmt::Debug;
104
105impl<R: Debug> ComputeTask<R> {
106    /// Get the handle of the task that computes the result.
107    pub fn get_task(&self) -> &Task {
108        &self.task
109    }
110
111    /// Wait until the task computes its result. Otherwise, returns a timeout.
112    pub fn wait_for_result<D: DurationTicks>(&mut self, max_wait: D) -> Result<(), FreeRtosError> {
113        if self.finished == true {
114            Ok(())
115        } else {
116            match self.status.receive(max_wait) {
117                Ok(ComputeTaskStatus::Finished) => {
118                    self.finished = true;
119                    Ok(())
120                }
121                Err(e) => Err(e),
122            }
123        }
124    }
125
126    /// Consume the task and unwrap the computed return value.
127    pub fn into_result<D: DurationTicks>(mut self, max_wait: D) -> Result<R, FreeRtosError> {
128        self.wait_for_result(max_wait)?;
129
130        if self.finished != true {
131            panic!("ComputeTask should be finished!");
132        }
133
134        let m = Arc::try_unwrap(self.result)
135            .expect("ComputeTask: Arc should have only one reference left!");
136        let option_r = m.into_inner();
137        let r = option_r.expect("ComputeTask: Result should be a Some(R)!");
138
139        Ok(r)
140    }
141}