| //! Traits for building lightweight asynchronous APIs. These traits lack the |
| //! dynamic capabilities of `core::future::Future`, but have much smaller code |
| //! size and RAM usage costs. |
| //! |
| //! Tock kernel developers will be familiar with the 2-phase pattern of |
| //! operation these traits support. Client code (code using an asynchronous |
| //! component) calls a function or method provided by the asynchronous component |
| //! to start an asynchronous operation. When the operation is complete, the |
| //! asynchronous component calls a callback defined using the `FreeCallback` |
| //! and/or `MethodCallback` traits. |
| //! |
| //! Note that asynchronous callbacks must only be called from within "callback |
| //! context" -- that is, within a kernel callback (registered using the |
| //! `subscribe` system call). To enforce that, the callback traits require the |
| //! `CallbackContext` type, which is only instantiated by an instance of the |
| //! `Syscalls` trait. |
| //! |
| //! There is no prohibition on a callback calling back into the asynchronous |
| //! component that called it. In other words, if component B calls |
| //! `<A as FreeCallback<Done>>::call(...)`, then `call()` can call back into B |
| //! to start a new operation. Asynchronous components should clean up their |
| //! state internally before calling callbacks so as to support reentrant calls |
| //! into themselves! |
| //! |
| //! In client code, prefer to implement `FreeCallback` instead of |
| //! `MethodCallback` when possible, as it is easier to pass a `FreeCallback` to |
| //! an asynchronous component. |
| //! |
| //! To bridge the gap between `FreeCallback` and `MethodCallback`, we also have |
| //! the `Locator` trait. `Locator` allows `FreeCallback` implementations to find |
| //! their global state, and also provides `FreeCallback` versions of callbacks |
| //! for types that implement `MethodCallback`. In general, `Locator` should be |
| //! implemented by `libtock_runtime::static_component!` (in real Tock apps) and |
| //! `libtock_unittest::test_component!` (in unit tests), rather than directly by |
| //! user code. |
| // TODO: At the time of this writing, neither `libtock_runtime` nor |
| // `libtock_unittest` are implemented. Remove this TODO when they are |
| // implemented. |
| |
| /// `FreeCallback` is the callback equivalent of a free function: it does not |
| /// have access to the client component's data. `FreeCallback` is used by |
| /// asynchronous components -- such as `Syscalls` -- which cannot efficiently |
| /// store a client reference. |
| pub trait FreeCallback<AsyncResponse> { |
| fn call(context: CallbackContext, response: AsyncResponse); |
| } |
| |
| /// `MethodCallback` is a callback method; it can access the client component's |
| /// data. Note that asynchronous components generally need to use interior |
| /// mutability to mutate data, as `MethodCallback` is designed under the |
| /// assumption that there are multiple references to most asynchronous |
| /// components at any given time. |
| pub trait MethodCallback<AsyncResponse> { |
| fn call(&self, context: CallbackContext, response: AsyncResponse); |
| } |
| |
| /// `Syscalls` instantiates a `CallbackContext` when a kernel callback is |
| /// called. The lifetime prevents the `CallbackContext` from being copied into |
| /// storage that outlives the callback. Code that is only safe to call from |
| /// callback context can request a `CallbackContext` argument. `CallbackContext` |
| /// is a zero-sized type so passing it around has no runtime cost. |
| #[derive(Clone, Copy)] |
| pub struct CallbackContext<'c> { |
| // `_phantom` serves three purposes. It uses the `c lifetime to avoid an |
| // "unused lifetime" error, it provides the proper variance over 'c, and it |
| // prevents code outside this crate from directly constructing a |
| // CallbackContext (because of its visibility control). Code outside this |
| // crate can copy a CallbackContext, but that is fine as copying the |
| // CallbackContext preserves its associated lifetime. |
| pub(crate) _phantom: core::marker::PhantomData<&'c ()>, |
| } |
| |
| /// Provides access to a global instance of type `Target`. Every call to |
| /// `locate()` on a given Locator type should return a reference to the same |
| /// instance of `Target`. An instance of `Locator` generally isn't instantiated |
| /// directly; instead, its type is passed to where it is needed via generic |
| /// arguments. |
| /// |
| /// For convenience, Locator provides a `FreeCallback` implementation for every |
| /// `MethodCallback` implementation that `Target` has. |
| pub trait Locator: 'static { |
| type Target; |
| fn locate() -> &'static Self::Target; |
| } |
| |
| impl<L: Locator, AsyncResponse> FreeCallback<AsyncResponse> for L |
| where |
| L::Target: MethodCallback<AsyncResponse>, |
| { |
| fn call(context: CallbackContext, response: AsyncResponse) { |
| L::locate().call(context, response); |
| } |
| } |