1use core::mem;
4use core::pin::pin;
5use core::time::Duration;
6
7use anyhow::Context as _;
8use bytes::{Bytes, BytesMut};
9use futures::TryStreamExt as _;
10use tokio::io::AsyncWriteExt as _;
11use tokio::{select, try_join};
12use tokio_util::codec::{Encoder as _, FramedRead};
13use tracing::{Instrument as _, debug, instrument, trace};
14
15use crate::frame::{Incoming, Outgoing};
16use crate::{BufferedIncoming, Deferred as _, TupleDecode, TupleEncode};
17
18pub trait Invoke: Send + Sync {
24 type Context: Send + Sync;
26
27 fn invoke<P>(
29 &self,
30 cx: Self::Context,
31 instance: &str,
32 func: &str,
33 params: Bytes,
34 paths: impl AsRef<[P]> + Send,
35 ) -> impl Future<Output = anyhow::Result<(Outgoing, Incoming)>> + Send
36 where
37 P: AsRef<[Option<usize>]> + Send + Sync;
38}
39
40#[derive(Clone, Copy, Debug, Eq, PartialEq)]
42pub struct Timeout<'a, T: ?Sized> {
43 pub inner: &'a T,
45 pub timeout: Duration,
47}
48
49impl<T: Invoke> Invoke for Timeout<'_, T> {
50 type Context = T::Context;
51
52 #[instrument(level = "trace", skip(self, cx, params, paths))]
53 async fn invoke<P>(
54 &self,
55 cx: Self::Context,
56 instance: &str,
57 func: &str,
58 params: Bytes,
59 paths: impl AsRef<[P]> + Send,
60 ) -> anyhow::Result<(Outgoing, Incoming)>
61 where
62 P: AsRef<[Option<usize>]> + Send + Sync,
63 {
64 tokio::time::timeout(
65 self.timeout,
66 self.inner.invoke(cx, instance, func, params, paths),
67 )
68 .await
69 .context("invocation timed out")?
70 }
71}
72
73#[derive(Clone, Copy, Debug, Eq, PartialEq)]
75pub struct TimeoutOwned<T> {
76 pub inner: T,
78 pub timeout: Duration,
80}
81
82impl<T: Invoke> Invoke for TimeoutOwned<T> {
83 type Context = T::Context;
84
85 #[instrument(level = "trace", skip(self, cx, params, paths))]
86 async fn invoke<P>(
87 &self,
88 cx: Self::Context,
89 instance: &str,
90 func: &str,
91 params: Bytes,
92 paths: impl AsRef<[P]> + Send,
93 ) -> anyhow::Result<(Outgoing, Incoming)>
94 where
95 P: AsRef<[Option<usize>]> + Send + Sync,
96 {
97 self.inner
98 .timeout(self.timeout)
99 .invoke(cx, instance, func, params, paths)
100 .await
101 }
102}
103
104pub trait InvokeExt: Invoke {
106 #[instrument(level = "trace", skip(self, cx, params, paths))]
108 fn invoke_values<P, Params, Results, Paths>(
109 &self,
110 cx: Self::Context,
111 instance: &str,
112 func: &str,
113 params: Params,
114 paths: Paths,
115 ) -> impl Future<
116 Output = anyhow::Result<(
117 Results,
118 Option<
119 impl Future<Output = anyhow::Result<()>>
120 + Send
121 + 'static
122 + use<Self, P, Params, Results, Paths>,
123 >,
124 )>,
125 > + Send
126 where
127 Paths: AsRef<[P]> + Send,
128 P: AsRef<[Option<usize>]> + Send + Sync,
129 Params: TupleEncode + Send,
130 Results: TupleDecode + Send,
131 <Params::Encoder as tokio_util::codec::Encoder<Params>>::Error:
132 core::error::Error + Send + Sync + 'static,
133 <Results::Decoder as tokio_util::codec::Decoder>::Error:
134 core::error::Error + Send + Sync + 'static,
135 {
136 async {
137 let mut buf = BytesMut::default();
138 let mut enc = Params::Encoder::default();
139 trace!("encoding parameters");
140 enc.encode(params, &mut buf)
141 .context("failed to encode parameters")?;
142 debug!("invoking function");
143 let (mut outgoing, incoming) = self
144 .invoke(cx, instance, func, buf.freeze(), paths)
145 .await
146 .context("failed to invoke function")?;
147 trace!("shutdown synchronous parameter channel");
148 outgoing
149 .shutdown()
150 .await
151 .context("failed to shutdown synchronous parameter channel")?;
152 let mut tx = enc.take_deferred().map(|tx| {
153 tokio::spawn(
154 async {
155 debug!("transmitting async parameters");
156 tx(outgoing, Vec::default())
157 .await
158 .context("failed to write async parameters")
159 }
160 .in_current_span(),
161 )
162 });
163
164 let mut dec = FramedRead::new(incoming, Results::Decoder::default());
165 let results = async {
166 debug!("receiving sync results");
167 dec.try_next()
168 .await
169 .context("failed to receive sync results")?
170 .context("incomplete results")
171 };
172 let results = match tx.take() {
173 Some(mut fut) => {
174 let mut results = pin!(results);
175 select! {
176 res = &mut results => {
177 tx = Some(fut);
178 res?
179 }
180 res = &mut fut => {
181 res??;
182 results.await?
183 }
184 }
185 }
186 _ => results.await?,
187 };
188 trace!("received sync results");
189 let buffer = mem::take(dec.read_buffer_mut());
190 let rx = dec.decoder_mut().take_deferred();
191 let incoming = BufferedIncoming {
192 buffer,
193 inner: dec.into_inner(),
194 };
195 Ok((
196 results,
197 (tx.is_some() || rx.is_some()).then_some(
198 async {
199 match (tx, rx) {
200 (Some(tx), Some(rx)) => {
201 try_join!(
202 async {
203 debug!("receiving async results");
204 rx(incoming, Vec::default())
205 .await
206 .context("receiving async results failed")
207 },
208 async {
209 tx.await.context("transmitting async parameters failed")?
210 }
211 )?;
212 }
213 (Some(tx), None) => {
214 tx.await.context("transmitting async parameters failed")??;
215 }
216 (None, Some(rx)) => {
217 debug!("receiving async results");
218 rx(incoming, Vec::default())
219 .await
220 .context("receiving async results failed")?;
221 }
222 _ => {}
223 }
224 Ok(())
225 }
226 .in_current_span(),
227 ),
228 ))
229 }
230 }
231
232 #[instrument(level = "trace", skip_all)]
235 fn invoke_values_blocking<P, Params, Results>(
236 &self,
237 cx: Self::Context,
238 instance: &str,
239 func: &str,
240 params: Params,
241 paths: impl AsRef<[P]> + Send,
242 ) -> impl Future<Output = anyhow::Result<Results>> + Send
243 where
244 P: AsRef<[Option<usize>]> + Send + Sync,
245 Params: TupleEncode + Send,
246 Results: TupleDecode + Send,
247 <Params::Encoder as tokio_util::codec::Encoder<Params>>::Error:
248 core::error::Error + Send + Sync + 'static,
249 <Results::Decoder as tokio_util::codec::Decoder>::Error:
250 core::error::Error + Send + Sync + 'static,
251 {
252 async {
253 let (ret, io) = self
254 .invoke_values(cx, instance, func, params, paths)
255 .await?;
256 if let Some(io) = io {
257 trace!("awaiting I/O completion");
258 io.await?;
259 }
260 Ok(ret)
261 }
262 }
263
264 fn timeout(&self, timeout: Duration) -> Timeout<'_, Self> {
267 Timeout {
268 inner: self,
269 timeout,
270 }
271 }
272
273 fn timeout_owned(self, timeout: Duration) -> TimeoutOwned<Self>
275 where
276 Self: Sized,
277 {
278 TimeoutOwned {
279 inner: self,
280 timeout,
281 }
282 }
283}
284
285impl<T: Invoke> InvokeExt for T {}
286
287#[allow(dead_code)]
288#[cfg(test)]
289mod tests {
290 use core::pin::Pin;
291
292 use std::sync::Arc;
293
294 use futures::{Stream, StreamExt as _};
295
296 use super::*;
297
298 #[allow(clippy::manual_async_fn)]
299 fn invoke_values_send<T>() -> impl Future<
300 Output = anyhow::Result<(
301 Pin<Box<dyn Stream<Item = Vec<Pin<Box<dyn Future<Output = String> + Send>>>> + Send>>,
302 )>,
303 > + Send
304 where
305 T: Invoke<Context = ()> + Default,
306 {
307 async {
308 let wrpc = T::default();
309 let ((r0,), _) = wrpc
310 .invoke_values(
311 (),
312 "wrpc-test:integration/async",
313 "with-streams",
314 (),
315 [[None].as_slice()],
316 )
317 .await?;
318 Ok(r0)
319 }
320 }
321
322 async fn call_invoke<T: Invoke>(
323 i: &T,
324 cx: T::Context,
325 paths: Arc<[Arc<[Option<usize>]>]>,
326 ) -> anyhow::Result<(Outgoing, Incoming)> {
327 i.invoke(cx, "foo", "bar", Bytes::default(), &paths).await
328 }
329
330 async fn call_invoke_async<T>() -> anyhow::Result<(Pin<Box<dyn Stream<Item = Bytes> + Send>>,)>
331 where
332 T: Invoke<Context = ()> + Default,
333 {
334 let wrpc = T::default();
335 let ((r0,), _) = wrpc
336 .invoke_values(
337 (),
338 "wrpc-test:integration/async",
339 "with-streams",
340 (),
341 [
342 [Some(1), Some(2)].as_slice(),
343 [None].as_slice(),
344 [Some(42)].as_slice(),
345 ],
346 )
347 .await?;
348 Ok(r0)
349 }
350
351 trait Handler {
352 fn foo() -> impl Future<Output = anyhow::Result<()>>;
353 }
354
355 impl<T> Handler for T
356 where
357 T: Invoke<Context = ()> + Default,
358 {
359 async fn foo() -> anyhow::Result<()> {
360 let (st,) = call_invoke_async::<Self>().await?;
361 st.collect::<Vec<_>>().await;
362 Ok(())
363 }
364 }
365}