veecle_freertos_integration/task/
block_on_future.rs

1use core::future::Future;
2use core::pin::pin;
3use core::task::{Context, Poll};
4
5use crate::{CurrentTask, Duration, Task};
6
7mod waker {
8    use core::task::{RawWaker, RawWakerVTable, Waker};
9
10    use veecle_freertos_sys::bindings::TaskHandle_t;
11
12    use crate::{Task, TaskNotification};
13
14    static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop);
15
16    /// # Safety
17    ///
18    /// The handle must be a [`TaskHandle_t`] to a task that will never be deleted.
19    unsafe fn clone(handle: *const ()) -> RawWaker {
20        // The task must be forever valid so we don't need to track ref-counts.
21        RawWaker::new(handle, &VTABLE)
22    }
23
24    /// # Safety
25    ///
26    /// The handle must be a [`TaskHandle_t`] to a still valid task.
27    unsafe fn wake(handle: *const ()) {
28        let handle: TaskHandle_t = handle.cast_mut().cast();
29        // SAFETY:
30        // The handle is guaranteed to be a `TaskHandle_t` to a still valid task to by this function's requirement.
31        let task = unsafe { Task::from_raw_handle(handle) };
32        task.notify(TaskNotification::Increment);
33    }
34
35    fn drop(_handle: *const ()) {
36        // The task must be forever valid so we don't need to track ref-counts.
37    }
38
39    /// Create a [`Waker`] that wakes a [`Task`] via [`Task::notify`].
40    pub fn new(task: Task) -> Waker {
41        let handle: TaskHandle_t = task.raw_handle();
42        Task::assert_no_task_deletion();
43        // SAFETY: This must guarantee the safety requirements of the functions used in `VTABLE`:
44        //
45        //  * `Task` is guaranteed to reference a forever valid undeleted task based on above guarantee.
46        //  * We know it is a `TaskHandle_t` because we just created it above.
47        unsafe { Waker::new(handle.cast(), &VTABLE) }
48    }
49}
50
51/// Runs a future to completion on the current task and returns its output value.
52///
53/// # Panics
54///
55/// If run from outside a [`Task`].
56///
57/// ```should_panic
58/// veecle_freertos_integration::task::block_on_future(async { 2 + 2 });
59/// ```
60///
61/// # Examples
62///
63/// ```
64/// veecle_freertos_integration::Task::new().start(|_| {
65///     let result = veecle_freertos_integration::task::block_on_future(async { 2 + 2 });
66///     assert_eq!(result, 4);
67///     # unsafe { veecle_freertos_sys::bindings::vTaskEndScheduler() };
68/// });
69/// # veecle_freertos_integration::scheduler::start_scheduler();
70/// ```
71pub fn block_on_future<T>(future: impl Future<Output = T>) -> T {
72    let task = Task::current().expect(
73        "Could not find the task of the current execution context. Ensure that the method is called inside a \
74         FreeRTOS task.",
75    );
76
77    let waker = waker::new(task);
78    let mut context = Context::from_waker(&waker);
79
80    let mut future = pin!(future);
81    loop {
82        if let Poll::Ready(value) = future.as_mut().poll(&mut context) {
83            break value;
84        }
85        CurrentTask::take_notification(true, Duration::max());
86    }
87}