Skip to content

Commit

Permalink
chore(transport): document and test Send async rustc bug
Browse files Browse the repository at this point in the history
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
  • Loading branch information
rvolosatovs committed Jul 5, 2024
1 parent 0f97412 commit 38be7d1
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 5 deletions.
11 changes: 9 additions & 2 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wrpc"
version = "0.2.0"
version = "0.2.1"
description = "WebAssembly component-native RPC framework based on WIT"

authors.workspace = true
Expand Down Expand Up @@ -109,6 +109,7 @@ quote = { version = "1", default-features = false }
rcgen = { version = "0.13", default-features = false }
reqwest = { version = "0.11", default-features = false }
rustls = { version = "0.23", default-features = false }
send-future = { version = "0.1", default-features = false }
serde = { version = "1", default-features = false }
serde_json = { version = "1", default-features = false }
syn = { version = "2", default-features = false, features = ["printing"] }
Expand Down
3 changes: 2 additions & 1 deletion crates/transport/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wrpc-transport"
version = "0.26.1"
version = "0.26.2"
description = "wRPC core transport functionality"

authors.workspace = true
Expand All @@ -24,6 +24,7 @@ tokio = { workspace = true, features = ["macros", "rt"] }
tokio-stream = { workspace = true }
tokio-util = { workspace = true, features = ["codec", "io"] }
tracing = { workspace = true, features = ["attributes"] }
send-future = { workspace = true }
wasm-tokio = { workspace = true, features = ["tracing"] }

[dev-dependencies]
Expand Down
72 changes: 71 additions & 1 deletion crates/transport/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod value;

#[cfg(feature = "frame")]
pub use frame::{Decoder as FrameDecoder, Encoder as FrameEncoder, FrameRef};
pub use send_future::SendFuture;
pub use value::*;

use core::future::Future;
Expand Down Expand Up @@ -48,6 +49,45 @@ pub trait Invoke: Send + Sync + 'static {
type Incoming: AsyncRead + Index<Self::Incoming> + Send + Sync + Unpin + 'static;

/// Invoke function `func` on instance `instance`
///
/// Note, that compilation of code calling methods on [`Invoke`] implementations within [`Send`] async functions
/// may fail with hard-to-debug errors due to a compiler bug:
/// [https://github.com/rust-lang/rust/issues/96865](https://github.com/rust-lang/rust/issues/96865)
///
/// The following fails to compile with rustc 1.78.0:
///
/// ```compile_fail
/// use core::future::Future;
///
/// fn invoke_send<T>() -> impl Future<Output = anyhow::Result<(T::Outgoing, T::Incoming)>> + Send
/// where
/// T: wrpc_transport::Invoke<Context = ()> + Default,
/// {
/// async { T::default().invoke((), "compiler-bug", "free", "since".into(), [[Some(2024)].as_slice(); 0]).await }
/// }
/// ```
///
/// ```text
/// async { T::default().invoke((), "compiler-bug", "free", "since".into(), [[Some(2024)].as_slice(); 0]).await }
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `AsRef` is not general enough
/// |
/// = note: `[&'0 [Option<usize>]; 0]` must implement `AsRef<[&'1 [Option<usize>]]>`, for any two lifetimes `'0` and `'1`...
/// = note: ...but it actually implements `AsRef<[&[Option<usize>]]>`
/// ```
///
/// The fix is to call [`send`](SendFuture::send) provided by [`send_future::SendFuture`], re-exported by this crate, on the future before awaiting:
/// ```
/// use core::future::Future;
/// use wrpc_transport::SendFuture as _;
///
/// fn invoke_send<T>() -> impl Future<Output = anyhow::Result<(T::Outgoing, T::Incoming)>> + Send
/// where
/// T: wrpc_transport::Invoke<Context = ()> + Default,
/// {
/// async { T::default().invoke((), "compiler-bug", "free", "since".into(), [[Some(2024)].as_slice(); 0]).send().await }
/// }
/// ```
fn invoke<P>(
&self,
cx: Self::Context,
Expand Down Expand Up @@ -325,6 +365,36 @@ mod tests {

use super::*;

fn invoke_values_send<T>() -> impl Future<
Output = anyhow::Result<(
Pin<
Box<
dyn Stream<Item = Vec<Pin<Box<dyn Future<Output = String> + Send + Sync>>>>
+ Send
+ Sync,
>,
>,
)>,
> + Send
where
T: Invoke<Context = ()> + Default,
{
async {
let wrpc = T::default();
let ((r0,), _) = wrpc
.invoke_values(
(),
"wrpc-test:integration/async",
"with-streams",
(),
[[None].as_slice()],
)
.send()
.await?;
Ok(r0)
}
}

async fn call_invoke<T: Invoke>(
i: &T,
cx: T::Context,
Expand All @@ -333,7 +403,7 @@ mod tests {
i.invoke(cx, "foo", "bar", Bytes::default(), &paths).await
}

pub async fn call_invoke_async<T>(
async fn call_invoke_async<T>(
) -> anyhow::Result<(Pin<Box<dyn Stream<Item = Bytes> + Send + Sync>>,)>
where
T: Invoke<Context = ()> + Default,
Expand Down

0 comments on commit 38be7d1

Please sign in to comment.