riot_wrappers/
async_helpers.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
//! Tools used internally to create futures more easily

use core::future::Future;

/// A trait similar to Future that is practical to implement for the typical RIOT situations where
/// a waker needs to be converted into a function and argument pointer.
///
/// Wrapped in a [RiotStylePollStruct], it implements [Future], and the conversion between the arg
/// pointer and the full struct is taken care of. That wrapper may also do any optimizations such
/// as not really storing the waker if it can be compressed to a single word instead.
///
/// ## Implementing
///
/// While this can legally be implemented without unsafe, practical use will require unsafe, and
/// that requires sticking to the rules:
///
/// * Whenever [.poll()][Self::poll()] is called, do whatever the future needs to do after having been awoken. If
///   this returns [core::task::Poll::Pending] (and the future wants to be polled ever again), it
///   must then pass on the `arg` to some RIOT callback setter together with a static function of a
///   suitable signature. Conventionally, that function is called `Self::callback()`.
///
/// * When that callback function is called (and has any arguments), it may inspect the arguments
///   to decide to return early (for example, if it receives "chatter" that is unrelated to the
///   completion of the future). If it decides that this is now the callback that should make
///   progress, it must call [`RiotStylePollStruct::<Self>::callback(arg)`], with `arg` being the
///   value that was passed around through RIOT from the poll function.
///
/// * To the author's knowledge, the mechanism itself has no requirements of not shuffling any
///   items in and out of any `&mut` that are involved (otherwise, they would be pinned). However,
///   the callback mechanism itself may require no such shuffling to occur, in which case it is the
///   implementor's responsibility to not just move its data around.
pub(crate) trait RiotStyleFuture {
    type Output;
    fn poll(&mut self, arg: *mut riot_sys::libc::c_void) -> core::task::Poll<Self::Output>;
}

/// Wrapper that makes a [Future] out of a [RiotStyleFuture] (see there for usage)
// FIXME: I'm not sure the presence and absence of #[pin] is right about these ones, but anyway,
// given they're not pub, and this module is what captures the unsafety guarantees (assisted by the
// requirements on RiotStyleFuture), this should be no worse than manually safe-declaring any
// access to args and waker.
#[pin_project::pin_project]
pub(crate) struct RiotStylePollStruct<A: RiotStyleFuture> {
    // The order of these is important: args is dropped first, thereby unregistering any callbacks.
    // Only then, the waker too can be dropped.
    args: A,
    // We can probably save that one if we rely on the waker pointing to a task, but let's not
    // force this on the system yet. (The TaskRef is short enough we could store it in the argument
    // of the callback).
    waker: Option<core::task::Waker>,
}
impl<A: RiotStyleFuture> RiotStylePollStruct<A> {
    pub(crate) fn new(args: A) -> Self {
        Self { args, waker: None }
    }

    /// Reconstruct a Self and run its waker (if one is present)
    pub(crate) unsafe fn callback(arg: *mut riot_sys::libc::c_void) {
        // Actually Pin<>, but we just promise not to move it.
        let f: &mut Self = &mut *(arg as *mut _);
        // If it fires multiple times, we ignore it (the waker has been taken) -- unless the future
        // has been polled again, there is no use in waking for it multiple times. (We could also
        // remove the callback, but who knows how costly that might be).
        f.waker.take().map(|w| w.wake());
    }
}
impl<A: RiotStyleFuture> Future for RiotStylePollStruct<A> {
    type Output = A::Output;
    fn poll(
        mut self: core::pin::Pin<&mut Self>,
        context: &mut core::task::Context<'_>,
    ) -> core::task::Poll<Self::Output> {
        let arg = unsafe { self.as_mut().get_unchecked_mut() } as *mut Self as *mut _;
        match self.args.poll(arg) {
            core::task::Poll::Pending => {
                // Actually we only need to do that if we're returning Pending
                self.waker = Some(context.waker().clone());

                core::task::Poll::Pending
            }
            ready => ready,
        }
    }
}