Merge #261 261: Begin overhauling libtock_platform's syscalls trait for Tock 2.0. r=hudson-ayers a=jrvanwhy I made the following changes to the design of `Syscalls`: 1. The trait is now designed for the (work-in-progress) Tock 2.0 syscalls rather than the Tock 1.0 syscalls. 2. The higher-level API that I previously intended to implement in the Platform type is now expressed in the form of provided functions in Syscalls. So far, I have only implemented `yield`. I'm sending this for review now because it is already getting large, and I'd prefer to split it up into several PRs rather than send a single huge PR. There is still significant design work to be done in terms of error handling for `subscribe`, `command`, both `allow`s, and `memop`. EDIT: To everyone reviewing this, here is a [direct link to syscalls.rs](https://github.com/jrvanwhy/libtock-rs/blob/syscalls-2.0/core/platform/src/syscalls.rs), in case you want to read it *without* seeing the diff from the previous version. Co-authored-by: Johnathan Van Why <jrvanwhy@google.com>
diff --git a/core/platform/src/async_traits.rs b/core/platform/src/async_traits.rs new file mode 100644 index 0000000..2853026 --- /dev/null +++ b/core/platform/src/async_traits.rs
@@ -0,0 +1,93 @@ +//! 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); + } +}
diff --git a/core/platform/src/lib.rs b/core/platform/src/lib.rs index b269712..0588d14 100644 --- a/core/platform/src/lib.rs +++ b/core/platform/src/lib.rs
@@ -9,12 +9,14 @@ // unit test environments. [DONE] mod allows; +mod async_traits; mod error_code; mod raw_syscalls; mod syscalls; mod syscalls_impl; pub use allows::{AllowReadable, Allowed}; +pub use async_traits::{CallbackContext, FreeCallback, Locator, MethodCallback}; pub use error_code::ErrorCode; pub use raw_syscalls::{OneArgMemop, RawSyscalls, YieldType, ZeroArgMemop}; pub use syscalls::Syscalls;