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}