freertos_rust/patterns/
compute_task.rs

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