Skip to content

Commit

Permalink
Support waiting on a call with optional response (#611)
Browse files Browse the repository at this point in the history
Part of #577. This is going to be used by `AppletExitStatus` too.
  • Loading branch information
ia0 committed Sep 20, 2024
1 parent ed19094 commit f18e1cd
Show file tree
Hide file tree
Showing 31 changed files with 97 additions and 46 deletions.
4 changes: 3 additions & 1 deletion crates/cli-tools/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

### Minor

- Change the flags of `action::AppletRpc` to use `action::Wait`
- Add `action::Wait` for commands returning an optional response
- Increase the default connection timeout from 1 to 5 seconds
- Add `action::PlatformUpdate` for platform update
- Add `action::PlatformList` to list connected platforms
Expand All @@ -20,4 +22,4 @@

## 0.1.0

<!-- Increment to skip CHANGELOG.md test: 4 -->
<!-- Increment to skip CHANGELOG.md test: 5 -->
2 changes: 1 addition & 1 deletion crates/cli-tools/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/cli-tools/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ indicatif = { version = "0.17.8", default-features = false }
rusb = { version = "0.9.4", default-features = false }
serde = { version = "1.0.202", default-features = false, features = ["derive"] }
toml = { version = "0.8.13", default-features = false, features = ["display", "parse"] }
wasefire-protocol = { version = "0.1.1-git", path = "../protocol", features = ["host"] }
wasefire-protocol = { version = "0.2.0-git", path = "../protocol", features = ["host"] }
wasefire-wire = { version = "0.1.1-git", path = "../wire" }

[dependencies.tokio]
version = "1.40.0"
default-features = false
features = ["fs", "io-std", "process", "rt"]
features = ["fs", "io-std", "process", "rt", "time"]

[dependencies.wasefire-protocol-tokio]
version = "0.1.0-git"
Expand Down
66 changes: 56 additions & 10 deletions crates/cli-tools/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@

use std::fmt::Display;
use std::path::{Path, PathBuf};
use std::time::Duration;

use anyhow::{bail, ensure, Result};
use cargo_metadata::{Metadata, MetadataCommand};
use clap::{ValueEnum, ValueHint};
use rusb::GlobalContext;
use tokio::process::Command;
use wasefire_protocol::{self as service, applet, Connection, ConnectionExt};
use wasefire_wire::Yoke;
use wasefire_wire::{self as wire, Yoke};

use crate::{cmd, fs};

Expand Down Expand Up @@ -90,27 +91,72 @@ pub struct AppletRpc {
#[clap(flatten)]
rpc: Rpc,

/// Number of retries to receive a response.
#[arg(long, default_value = "3")]
retries: usize,
#[clap(flatten)]
wait: Wait,
}

impl AppletRpc {
pub async fn run(self, connection: &mut dyn Connection) -> Result<()> {
let AppletRpc { applet, rpc, retries } = self;
let AppletRpc { applet, rpc, mut wait } = self;
let applet_id = match applet {
Some(_) => bail!("applet identifiers are not supported yet"),
None => applet::AppletId,
};
let request = applet::Request { applet_id, request: &rpc.read().await? };
connection.call::<service::AppletRequest>(request).await?.get();
for _ in 0 .. retries {
let response = connection.call::<service::AppletResponse>(applet_id).await?;
if let Some(response) = response.get().response {
return rpc.write(response).await;
wait.ensure_wait();
match wait.run::<service::AppletResponse, &[u8]>(connection, applet_id).await? {
None => bail!("did not receive a response"),
Some(response) => rpc.write(response.get()).await,
}
}
}

/// Options to repeatedly call a command with an optional response.
#[derive(clap::Args)]
pub struct Wait {
/// Waits until there is a response.
///
/// This is equivalent to --period=100ms.
#[arg(long)]
wait: bool,

/// Retries every so often until there is a response.
///
/// The command doesn't return `None` in that case.
#[arg(long, conflicts_with = "wait")]
period: Option<humantime::Duration>,
}

impl Wait {
pub fn ensure_wait(&mut self) {
if self.wait || self.period.is_some() {
return;
}
self.wait = true;
}

pub async fn run<S, T: wire::Wire<'static>>(
self, connection: &mut dyn Connection, request: S::Request<'_>,
) -> Result<Option<Yoke<T::Type<'static>>>>
where S: for<'a> service::Service<Response<'a> = Option<T::Type<'a>>> {
let Wait { wait, period } = self;
let period = match (wait, period) {
(true, None) => Some(Duration::from_millis(100)),
(true, Some(_)) => unreachable!(),
(false, None) => None,
(false, Some(x)) => Some(*x),
};
let request = S::request(request);
loop {
match connection.call_ref::<S>(&request).await?.try_map(|x| x.ok_or(())) {
Ok(x) => break Ok(Some(x)),
Err(()) => match period {
Some(period) => tokio::time::sleep(period).await,
None => break Ok(None),
},
}
}
bail!("did not receive a response after {retries} retries");
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/protocol-tokio/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

## 0.1.0-git

<!-- Increment to skip CHANGELOG.md test: 3 -->
<!-- Increment to skip CHANGELOG.md test: 4 -->
2 changes: 1 addition & 1 deletion crates/protocol-tokio/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/protocol-tokio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ features = ["std"]
optional = true

[dependencies.wasefire-protocol]
version = "0.1.1-git"
version = "0.2.0-git"
path = "../protocol"
features = ["host"]
optional = true
Expand Down
2 changes: 1 addition & 1 deletion crates/protocol-usb/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@

## 0.1.0

<!-- Increment to skip CHANGELOG.md test: 3 -->
<!-- Increment to skip CHANGELOG.md test: 4 -->
2 changes: 1 addition & 1 deletion crates/protocol-usb/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/protocol-usb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ path = "../board"
optional = true

[dependencies.wasefire-protocol]
version = "0.1.1-git"
version = "0.2.0-git"
path = "../protocol"
features = ["host"]
optional = true
Expand Down
7 changes: 6 additions & 1 deletion crates/protocol/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
# Changelog

## 0.1.1-git
## 0.2.0-git

### Major

- Remove `applet::Response` and inline its definition in `AppletResponse::Response`

### Minor

- Add `ConnectionExt::call_ref()` to share a request between calls
- Add a `Service::NAME` constant with `host` feature
- Add `PlatformUpdate{Metadata,Transfer}` calls and `transfer` module for platform updates
- Add a `Connection` abstraction with `host` feature
Expand Down
2 changes: 1 addition & 1 deletion crates/protocol/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/protocol/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wasefire-protocol"
version = "0.1.1-git"
version = "0.2.0-git"
authors = ["Julien Cretin <cretin@google.com>"]
license = "Apache-2.0"
publish = true
Expand Down
2 changes: 1 addition & 1 deletion crates/protocol/crates/schema/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified crates/protocol/crates/schema/device.bin
Binary file not shown.
2 changes: 1 addition & 1 deletion crates/protocol/crates/schema/device.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ result: {Ok=0:() Err=1:(space:u8 code:u16)}
version: 3
0 [0 -] ApiVersion: () -> u32
1 [0 -] AppletRequest: (applet_id:() request:[u8]) -> ()
2 [0 -] AppletResponse: () -> (response:{None=0:() Some=1:[u8]})
2 [0 -] AppletResponse: () -> {None=0:() Some=1:[u8]}
3 [0 -] PlatformReboot: () -> {}
4 [0 -] AppletTunnel: (applet_id:() delimiter:[u8]) -> ()
5 [1 -] PlatformInfo: () -> (serial:[u8] version:[u8])
Expand Down
Binary file modified crates/protocol/crates/schema/host.bin
Binary file not shown.
2 changes: 1 addition & 1 deletion crates/protocol/crates/schema/host.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ result: {Ok=0:() Err=1:(space:u8 code:u16)}
version: 3
0 [0 -] ApiVersion: () -> u32
1 [0 -] AppletRequest: (applet_id:() request:[u8]) -> ()
2 [0 -] AppletResponse: () -> (response:{None=0:() Some=1:[u8]})
2 [0 -] AppletResponse: () -> {None=0:() Some=1:[u8]}
3 [0 -] PlatformReboot: () -> {}
4 [0 -] AppletTunnel: (applet_id:() delimiter:[u8]) -> ()
5 [1 -] PlatformInfo: () -> (serial:[u8] version:[u8])
Expand Down
5 changes: 0 additions & 5 deletions crates/protocol/src/applet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ pub struct Request<'a> {
pub request: &'a [u8],
}

#[derive(Debug, Wire)]
pub struct Response<'a> {
pub response: Option<&'a [u8]>,
}

#[derive(Debug, Copy, Clone, Wire)]
pub struct AppletId;

Expand Down
10 changes: 7 additions & 3 deletions crates/protocol/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,15 @@ pub trait ConnectionExt: Connection {
/// Calls a service on the device.
fn call<S: Service>(
&mut self, request: S::Request<'_>,
) -> impl Future<Output = anyhow::Result<Yoke<S::Response<'static>>>> {
async { self.call_ref::<S>(&S::request(request)).await }
}

fn call_ref<S: Service>(
&mut self, request: &Api<Request>,
) -> impl Future<Output = anyhow::Result<Yoke<S::Response<'static>>>> {
async {
self.send(&S::request(request))
.await
.with_context(|| format!("sending {}", S::NAME))?;
self.send(request).await.with_context(|| format!("sending {}", S::NAME))?;
self.receive::<S>().await.with_context(|| format!("receiving {}", S::NAME))
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ api! {
1 [0 -] AppletRequest: applet::Request<'a> => (),

/// Reads a response from an applet.
2 [0 -] AppletResponse: applet::AppletId => applet::Response<'a>,
2 [0 -] AppletResponse: applet::AppletId => Option<&'a [u8]>,

/// Reboots the platform.
3 [0 -] PlatformReboot: () => !,
Expand Down
2 changes: 1 addition & 1 deletion crates/runner-host/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/runner-nordic/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/scheduler/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,4 @@

## 0.1.0

<!-- Increment to skip CHANGELOG.md test: 4 -->
<!-- Increment to skip CHANGELOG.md test: 5 -->
2 changes: 1 addition & 1 deletion crates/scheduler/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/scheduler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ features = ["toctou"]
optional = true

[dependencies.wasefire-protocol]
version = "0.1.1-git"
version = "0.2.0-git"
path = "../protocol"
features = ["device"]

Expand Down
3 changes: 1 addition & 2 deletions crates/scheduler/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ fn process_event_<B: Board>(
}
Api::AppletResponse(applet_id) => {
let response = applet::<B>(scheduler, applet_id)?.get_response()?;
let response = response.as_deref();
reply::<B, service::AppletResponse>(service::applet::Response { response });
reply::<B, service::AppletResponse>(response.as_deref());
}
Api::PlatformReboot(()) => {
use wasefire_board_api::platform::Api as _;
Expand Down
2 changes: 1 addition & 1 deletion crates/xtask/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/rust/protocol/host/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/rust/protocol/host/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ async fn main() -> Result<()> {
connection.call::<service::AppletRequest>(request).await?.get();
loop {
let response = connection.call::<service::AppletResponse>(AppletId).await?;
if let Some(response) = response.get().response {
if let Some(response) = response.get() {
print!("{}", std::str::from_utf8(response).unwrap());
break Ok(());
}
Expand Down

0 comments on commit f18e1cd

Please sign in to comment.