blob: 2853026d9429081a1b07b3a9b6f4563605609084 [file] [log] [blame]
//! 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);
}
}