1use std::io::Cursor;
40use std::pin::Pin;
41use std::time::Duration;
42
43use anyhow::Result;
44use future::Future;
45use futures::Stream;
46use futures::future;
47use futures::stream::FusedStream;
48use futures::task::Context;
49use futures::task::Poll;
50use hyperactor::ActorAddr;
51use hyperactor::Endpoint as _;
52use hyperactor::OncePortRef;
53use hyperactor::PortRef;
54use hyperactor::context;
55use hyperactor::mailbox::OncePortReceiver;
56use hyperactor::mailbox::PortReceiver;
57use hyperactor::mailbox::open_once_port;
58use hyperactor::mailbox::open_port;
59use pin_project::pin_project;
60use pin_project::pinned_drop;
61use serde::Deserialize;
62use serde::Serialize;
63use tokio::io::AsyncRead;
64use tokio::io::AsyncWrite;
65use tokio_util::io::StreamReader;
66use typeuri::Named;
67
68const CONNECT_TIMEOUT: Duration = Duration::from_secs(5);
70
71#[derive(Debug, Serialize, Deserialize, Named, Clone)]
73enum Io {
74 Data(#[serde(with = "serde_bytes")] Vec<u8>),
76 Eof,
78}
79wirevalue::register_type!(Io);
80
81struct OwnedReadHalfStream {
82 port: PortReceiver<Io>,
83 exhausted: bool,
84}
85
86#[pin_project]
88pub struct OwnedReadHalf<C: context::Actor> {
89 peer: ActorAddr,
90 _caps: C,
91 #[pin]
92 inner: StreamReader<OwnedReadHalfStream, Cursor<Vec<u8>>>,
93}
94
95#[pin_project(PinnedDrop)]
97pub struct OwnedWriteHalf<C: context::Actor> {
98 peer: ActorAddr,
99 #[pin]
100 caps: C,
101 #[pin]
102 port: PortRef<Io>,
103 #[pin]
104 shutdown: bool,
105}
106
107#[pin_project]
109pub struct ActorConnection<C: context::Actor> {
110 #[pin]
111 reader: OwnedReadHalf<C>,
112 #[pin]
113 writer: OwnedWriteHalf<C>,
114}
115
116impl<C: context::Actor> ActorConnection<C> {
117 pub fn into_split(self) -> (OwnedReadHalf<C>, OwnedWriteHalf<C>) {
118 (self.reader, self.writer)
119 }
120
121 pub fn peer(&self) -> &ActorAddr {
122 self.reader.peer()
123 }
124}
125
126impl<C: context::Actor> OwnedReadHalf<C> {
127 fn new(peer: ActorAddr, caps: C, port: PortReceiver<Io>) -> Self {
128 Self {
129 peer,
130 _caps: caps,
131 inner: StreamReader::new(OwnedReadHalfStream {
132 port,
133 exhausted: false,
134 }),
135 }
136 }
137
138 pub fn peer(&self) -> &ActorAddr {
139 &self.peer
140 }
141
142 pub fn reunited(self, other: OwnedWriteHalf<C>) -> ActorConnection<C> {
143 ActorConnection {
144 reader: self,
145 writer: other,
146 }
147 }
148}
149
150impl<C: context::Actor> OwnedWriteHalf<C> {
151 fn new(peer: ActorAddr, caps: C, port: PortRef<Io>) -> Self {
152 Self {
153 peer,
154 caps,
155 port,
156 shutdown: false,
157 }
158 }
159
160 pub fn peer(&self) -> &ActorAddr {
161 &self.peer
162 }
163
164 pub fn reunited(self, other: OwnedReadHalf<C>) -> ActorConnection<C> {
165 ActorConnection {
166 reader: other,
167 writer: self,
168 }
169 }
170}
171
172#[pinned_drop]
173impl<C: context::Actor> PinnedDrop for OwnedWriteHalf<C> {
174 fn drop(self: Pin<&mut Self>) {
175 let this = self.project();
176 if !*this.shutdown {
177 let _ = this.port.post(&*this.caps, Io::Eof);
178 }
179 }
180}
181
182impl<C: context::Actor> AsyncRead for ActorConnection<C> {
183 fn poll_read(
184 self: Pin<&mut Self>,
185 cx: &mut Context<'_>,
186 buf: &mut tokio::io::ReadBuf<'_>,
187 ) -> Poll<std::io::Result<()>> {
188 let this = self.project();
190 this.reader.poll_read(cx, buf)
191 }
192}
193
194impl<C: context::Actor> AsyncWrite for ActorConnection<C> {
195 fn poll_write(
196 self: Pin<&mut Self>,
197 cx: &mut Context<'_>,
198 buf: &[u8],
199 ) -> Poll<Result<usize, std::io::Error>> {
200 let this = self.project();
202 this.writer.poll_write(cx, buf)
203 }
204
205 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
206 let this = self.project();
207 this.writer.poll_flush(cx)
208 }
209
210 fn poll_shutdown(
211 self: Pin<&mut Self>,
212 cx: &mut Context<'_>,
213 ) -> Poll<Result<(), std::io::Error>> {
214 let this = self.project();
215 this.writer.poll_shutdown(cx)
216 }
217}
218
219impl Stream for OwnedReadHalfStream {
220 type Item = std::io::Result<Cursor<Vec<u8>>>;
221
222 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
223 if self.exhausted {
225 return Poll::Ready(None);
226 }
227
228 let result = futures::ready!(Box::pin(self.port.recv()).as_mut().poll(cx));
229 match result {
230 Err(err) => Poll::Ready(Some(Err(std::io::Error::other(err)))),
231 Ok(Io::Data(buf)) => Poll::Ready(Some(Ok(Cursor::new(buf)))),
232 Ok(Io::Eof) => {
234 self.exhausted = true;
235 Poll::Ready(None)
236 }
237 }
238 }
239}
240
241impl FusedStream for OwnedReadHalfStream {
242 fn is_terminated(&self) -> bool {
243 self.exhausted
244 }
245}
246
247impl<C: context::Actor> AsyncRead for OwnedReadHalf<C> {
248 fn poll_read(
249 self: Pin<&mut Self>,
250 cx: &mut Context<'_>,
251 buf: &mut tokio::io::ReadBuf<'_>,
252 ) -> Poll<std::io::Result<()>> {
253 self.project().inner.poll_read(cx, buf)
254 }
255}
256
257impl<C: context::Actor> AsyncWrite for OwnedWriteHalf<C> {
258 fn poll_write(
259 self: Pin<&mut Self>,
260 _cx: &mut Context<'_>,
261 buf: &[u8],
262 ) -> Poll<Result<usize, std::io::Error>> {
263 let this = self.project();
264 if *this.shutdown {
265 return Poll::Ready(Err(std::io::Error::new(
266 std::io::ErrorKind::BrokenPipe,
267 "write after shutdown",
268 )));
269 }
270 this.port.post(&*this.caps, Io::Data(buf.into()));
271 Poll::Ready(Ok(buf.len()))
272 }
273
274 fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
275 Poll::Ready(Ok(()))
276 }
277
278 fn poll_shutdown(
279 self: Pin<&mut Self>,
280 _cx: &mut Context<'_>,
281 ) -> Poll<Result<(), std::io::Error>> {
282 self.port.post(&self.caps, Io::Eof);
284 let mut this = self.project();
285 *this.shutdown = true;
286 Poll::Ready(Ok(()))
287 }
288}
289
290pub struct ConnectionCompleter<C> {
292 caps: C,
293 conn: PortReceiver<Io>,
294 port: OncePortReceiver<Accept>,
295}
296
297impl<C: context::Actor + Clone> ConnectionCompleter<C> {
298 pub async fn complete(self) -> Result<ActorConnection<C>> {
301 let accept = tokio::time::timeout(CONNECT_TIMEOUT, self.port.recv()).await??;
302 Ok(ActorConnection {
303 reader: OwnedReadHalf::new(accept.id.clone(), self.caps.clone(), self.conn),
304 writer: OwnedWriteHalf::new(accept.id, self.caps, accept.conn),
305 })
306 }
307}
308
309#[derive(Debug, Serialize, Deserialize, Named, Clone)]
311pub struct Connect {
312 id: ActorAddr,
314 conn: PortRef<Io>,
315 return_conn: OncePortRef<Accept>,
317}
318wirevalue::register_type!(Connect);
319
320impl Connect {
321 pub fn allocate<C: context::Actor + Clone>(
324 id: ActorAddr,
325 caps: C,
326 ) -> (Self, ConnectionCompleter<C>) {
327 let (conn_tx, conn_rx) = open_port::<Io>(&caps);
328 let (return_tx, return_rx) = open_once_port::<Accept>(&caps);
329 (
330 Self {
331 id,
332 conn: conn_tx.bind(),
333 return_conn: return_tx.bind(),
334 },
335 ConnectionCompleter {
336 caps,
337 conn: conn_rx,
338 port: return_rx,
339 },
340 )
341 }
342}
343
344#[derive(Debug, Serialize, Deserialize, Named, Clone)]
347struct Accept {
348 id: ActorAddr,
350 conn: PortRef<Io>,
352}
353wirevalue::register_type!(Accept);
354
355pub async fn accept<C: context::Actor + Clone>(
358 caps: C,
359 self_id: ActorAddr,
360 message: Connect,
361) -> Result<ActorConnection<C>> {
362 let (tx, rx) = open_port::<Io>(&caps);
363 message.return_conn.post(
364 &caps,
365 Accept {
366 id: self_id,
367 conn: tx.bind(),
368 },
369 );
370 Ok(ActorConnection {
371 reader: OwnedReadHalf::new(message.id.clone(), caps.clone(), rx),
372 writer: OwnedWriteHalf::new(message.id, caps, message.conn),
373 })
374}
375
376#[cfg(test)]
377mod tests {
378 use anyhow::Result;
379 use async_trait::async_trait;
380 use futures::try_join;
381 use hyperactor::Actor;
382 use hyperactor::Context;
383 use hyperactor::Handler;
384 use hyperactor::proc::Proc;
385 use tokio::io::AsyncReadExt;
386 use tokio::io::AsyncWriteExt;
387
388 use super::*;
389
390 #[derive(Debug, Default)]
391 struct EchoActor {}
392
393 impl Actor for EchoActor {}
394
395 #[async_trait]
396 impl Handler<Connect> for EchoActor {
397 async fn handle(
398 &mut self,
399 cx: &Context<Self>,
400 message: Connect,
401 ) -> Result<(), anyhow::Error> {
402 let (mut rd, mut wr) = accept(cx, cx.self_addr().clone(), message)
403 .await?
404 .into_split();
405 tokio::io::copy(&mut rd, &mut wr).await?;
406 wr.shutdown().await?;
407 Ok(())
408 }
409 }
410
411 #[tokio::test]
412 async fn test_simple_connection() -> Result<()> {
413 let proc = Proc::isolated();
414 let client = proc.client("client");
415 let (connect, completer) = Connect::allocate(client.self_addr().clone(), client);
416 let actor = proc.spawn(EchoActor {});
417 actor.post(&completer.caps, connect);
418 let (mut rd, mut wr) = completer.complete().await?.into_split();
419 let send = [3u8, 4u8, 5u8, 6u8];
420 try_join!(
421 async move {
422 wr.write_all(&send).await?;
423 wr.shutdown().await?;
424 anyhow::Ok(())
425 },
426 async {
427 let mut recv = vec![];
428 rd.read_to_end(&mut recv).await?;
429 assert_eq!(&send, recv.as_slice());
430 anyhow::Ok(())
431 },
432 )?;
433 Ok(())
434 }
435
436 #[tokio::test]
437 async fn test_connection_close_on_drop() -> Result<()> {
438 let proc = Proc::isolated();
439 let client = proc.client("client");
440 let client_addr = client.self_addr().clone();
441 let server = proc.client("server");
442 let server_addr = server.self_addr().clone();
443
444 let (connect, completer) = Connect::allocate(client_addr, client);
445 let (mut rd, _) = accept(server, server_addr, connect).await?.into_split();
446 let (_, mut wr) = completer.complete().await?.into_split();
447
448 let send = [1u8, 2u8, 3u8];
450 wr.write_all(&send).await?;
451
452 drop(wr);
454
455 let mut recv = vec![];
457 rd.read_to_end(&mut recv).await?;
458 assert_eq!(&send, recv.as_slice());
459
460 Ok(())
461 }
462
463 #[tokio::test]
464 async fn test_no_eof_on_drop_after_shutdown() -> Result<()> {
465 let proc = Proc::isolated();
466 let client = proc.client("client");
467 let client_addr = client.self_addr().clone();
468 let server = proc.client("server");
469 let server_addr = server.self_addr().clone();
470
471 let (connect, completer) = Connect::allocate(client_addr, client);
472 let (mut rd, _) = accept(server, server_addr, connect).await?.into_split();
473 let (_, mut wr) = completer.complete().await?.into_split();
474
475 let send = [1u8, 2u8, 3u8];
477 wr.write_all(&send).await?;
478
479 wr.shutdown().await?;
481
482 let mut recv = vec![];
484 rd.read_to_end(&mut recv).await?;
485 assert_eq!(&send, recv.as_slice());
486
487 drop(wr);
489
490 assert!(rd.inner.into_inner().port.try_recv().unwrap().is_none());
492
493 Ok(())
494 }
495}