Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A conditionally async Control trait and RPC infra #184

Open
1 task
rrbutani opened this issue Jul 28, 2022 · 0 comments
Open
1 task

A conditionally async Control trait and RPC infra #184

rrbutani opened this issue Jul 28, 2022 · 0 comments
Assignees
Labels
✨ feature New things! P-low Low priority T-rpc Topic: RPC T-wasm Topic: WASM support

Comments

@rrbutani
Copy link
Member

what

As ut-utp/tui#12 explains, supporting a WebSerial backed Transport implementation is tricky because the WebSerial APIs are all async.

This requires us to make the Transport trait's methods async which is turn requires the Control trait to be async (because of the Control impl on Controller), etc.

We don't want to do this unconditionally (i.e. on all platforms) because currently the solution for async traits makes heavy use of trait objects and allocates which is problematic for embedded devices.

Ideally we'd have something that looks like keyword generics to support this use case but that's a ways off. Luckily, there's good prior art in the space of using proc macros to emulate this kind of functionality: maybe-async.


Since we're looking to tie the "async-ness" of some of our traits and functions to the target we're compiling for (instead of to a cargo feature) we can leverage maybe-async's must_be_async and must_be_sync attributes in conjunction with some carefully crafted #[cfg_attr(..., ...)] attributes.

We can even go a step further and spin this off into our own proc_macro attributes along with a wrapper crate so that we don't have to explicitly depend on maybe-async everywhere:

/* macro-impl crate */

/// Effectively equivalent to:
/// ```ignore
/// #[cfg_attr(target_family = "wasm", maybe_async::must_be_async)]
/// #[cfg_attr(not(target_family = "wasm"), maybe_async::must_be_sync)]
/// ...
/// ```
///
/// Note that `async-trait` must be a dep when targeting `wasm`; you probably
/// want to add something like this to your `Cargo.toml`:
/// ```toml
/// [target.'cfg(target_family = "wasm")'.dependencies]
/// async-trait = "0.1"
/// ```
#[proc_macro_attribute]
pub fn async_on_wasm(args: TokenStream, input: TokenStream) -> TokenStream {
    let mut out: TokenStream = quote! {
        #[cfg_attr(target_family = "wasm", ::lc3_macros::macro_support::must_be_async)]
        #[cfg_attr(not(target_family = "wasm"), ::lc3_macros::macro_support::must_be_sync)]
    }.into();
    out.extend([input]);
    out
}
/* macro crate */
#[doc(hidden)]
pub mod macro_support {
    pub use maybe_async::{must_be_async, must_be_sync};
}

#[doc(inline)]
pub use lc3_macros_impl::async_on_wasm;
/* example usage */
#[lc3_macros::async_on_wasm]
pub trait Foo {  }

steps

  • ...

where

branch: feat/async-everywhere

open questions

@rrbutani rrbutani added ✨ feature New things! P-low Low priority T-rpc Topic: RPC T-wasm Topic: WASM support labels Jul 28, 2022
@rrbutani rrbutani self-assigned this Jul 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
✨ feature New things! P-low Low priority T-rpc Topic: RPC T-wasm Topic: WASM support
Projects
None yet
Development

No branches or pull requests

1 participant