#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, OwnedSocket};
use std::{
cell::Cell,
future::poll_fn,
io::{self, IoSlice, IoSliceMut, Read, Write},
net::{Ipv4Addr, TcpListener, TcpStream, UdpSocket},
rc::Rc,
sync::{
Arc, Mutex,
atomic::{AtomicBool, AtomicUsize, Ordering},
mpsc,
},
task::{Context, Waker},
thread,
time::Duration,
};
#[cfg(unix)]
use std::{
io::{PipeReader, PipeWriter},
os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd},
process::{Command, Stdio},
};
use futures::{AsyncReadExt, AsyncWriteExt, future::join};
use ntest::timeout;
use socket2::SockRef;
use super::{or, yield_now};
use crate::{AsyncIo, Local, LocalRuntime, Mode, Readiness, Runtime, Shared, SharedRuntime};
macro_rules! in_both_modes {
($(#[$attr:meta])* fn $name:ident<$mode:ident>() $body:block) => {
$(#[$attr])*
mod $name {
use super::*;
#[test]
#[timeout(15000)]
fn local() {
type $mode = Local;
$body
}
#[test]
#[timeout(15000)]
fn shared() {
type $mode = Shared;
$body
}
}
};
($(#[$attr:meta])* fn $name:ident<$mode:ident, $pointer:ident>() $body:block) => {
$(#[$attr])*
mod $name {
use super::*;
#[test]
#[timeout(15000)]
fn local() {
type $mode = Local;
use std::rc::Rc as $pointer;
$body
}
#[test]
#[timeout(15000)]
fn shared() {
type $mode = Shared;
use std::sync::Arc as $pointer;
$body
}
}
};
}
in_both_modes! {
fn new_puts_a_blocking_source_in_non_blocking_mode<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, _far) = tcp_pair();
#[cfg(unix)]
assert!(!SockRef::from(&near).nonblocking().unwrap());
let near = AsyncIo::new(&runtime, near).unwrap();
#[cfg(unix)]
assert!(SockRef::from(&near).nonblocking().unwrap());
let mut stream = near.get_ref();
let error = stream.read(&mut [0]).unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::WouldBlock);
}
}
in_both_modes! {
#[cfg(unix)]
fn new_nonblocking_leaves_the_mode_as_it_was<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (blocking, _blocking_far) = tcp_pair();
let (non_blocking, _non_blocking_far) = tcp_pair();
non_blocking.set_nonblocking(true).unwrap();
let blocking = AsyncIo::new_nonblocking(&runtime, blocking).unwrap();
let non_blocking = AsyncIo::new_nonblocking(&runtime, non_blocking).unwrap();
assert!(!SockRef::from(&blocking).nonblocking().unwrap());
assert!(SockRef::from(&non_blocking).nonblocking().unwrap());
}
}
in_both_modes! {
fn new_nonblocking_takes_a_source_that_is_in_non_blocking_mode_already<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, mut far) = tcp_pair();
near.set_nonblocking(true).unwrap();
let near = AsyncIo::new_nonblocking(&runtime, near).unwrap();
runtime.block_on(async {
let (received, ()) = join(read_exactly(&near, 5), async {
runtime.sleep(DELAY).await;
far.write_all(b"hello").unwrap();
})
.await;
assert_eq!(received, b"hello");
});
}
}
in_both_modes! {
fn read_with_and_write_with_carry_bytes_between_two_asyncs<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, far) = tcp_pair();
let near = AsyncIo::new(&runtime, near).unwrap();
let far = AsyncIo::new(&runtime, far).unwrap();
runtime.block_on(async {
let (received, ()) = join(read_exactly(&far, 4), async {
runtime.sleep(DELAY).await;
write_everything(&near, b"ping").await;
})
.await;
assert_eq!(received, b"ping");
write_everything(&far, b"pong").await;
assert_eq!(read_exactly(&near, 4).await, b"pong");
});
}
}
in_both_modes! {
fn a_write_larger_than_the_socket_buffers_waits_for_room<M>() {
const LEN: usize = 16 << 20;
const PIECE: usize = 64 << 10;
let runtime = Runtime::<M>::new().unwrap();
let (near, far) = tcp_pair();
let writer = AsyncIo::new(&runtime, near).unwrap();
let reader = AsyncIo::new(&runtime, far).unwrap();
let bytes = pattern(LEN);
let blocked = Cell::new(0);
let (received, ()) = runtime.block_on(join(read_exactly(&reader, LEN), async {
let mut sent = 0;
while sent < LEN {
sent += writer
.write_with(|mut stream| {
let result = stream.write(&bytes[sent..LEN.min(sent + PIECE)]);
if would_block(&result) {
blocked.set(blocked.get() + 1);
}
result
})
.await
.unwrap();
}
}));
assert!(
received == bytes,
"the bytes arrived changed or out of order"
);
assert!(blocked.get() > 0, "the writer never ran out of room");
}
}
in_both_modes! {
fn an_interrupted_operation_is_made_again_without_waiting<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, _far) = tcp_pair();
let near = AsyncIo::new(&runtime, near).unwrap();
let calls = Cell::new(0);
let interrupted_twice = |_: &TcpStream| {
calls.set(calls.get() + 1);
if calls.get() % 3 == 0 {
Ok(calls.get())
} else {
Err(io::ErrorKind::Interrupted.into())
}
};
let results = runtime.block_on(async {
let read = near.read_with(&interrupted_twice).await.unwrap();
let write = near.write_with(&interrupted_twice).await.unwrap();
let polled_read = poll_fn(|cx| near.poll_read_with(cx, &interrupted_twice));
let polled_write = poll_fn(|cx| near.poll_write_with(cx, &interrupted_twice));
[
read,
write,
polled_read.await.unwrap(),
polled_write.await.unwrap(),
]
});
assert_eq!(results, [3, 6, 9, 12]);
}
}
in_both_modes! {
fn an_operation_that_fails_resolves_to_its_error<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, _far) = tcp_pair();
let near = AsyncIo::new(&runtime, near).unwrap();
runtime.block_on(async {
let error = near
.read_with(|_| Err::<(), _>(io::ErrorKind::BrokenPipe.into()))
.await
.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::BrokenPipe);
let error = near
.write_with(|_| Err::<(), _>(io::Error::new(io::ErrorKind::TimedOut, "too slow")))
.await
.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::TimedOut);
assert_eq!(error.to_string(), "too slow");
});
}
}
in_both_modes! {
fn several_tasks_read_with_on_one_async_at_once<M, P>() {
const TASKS: usize = 4;
let runtime = Runtime::<M>::new().unwrap();
let (near, far) = udp_pair();
let receiver = P::new(AsyncIo::new(&runtime, near).unwrap());
let waiting = Arc::new(AtomicUsize::new(0));
let received = Arc::new(AtomicUsize::new(0));
let tasks: Vec<_> = (0..TASKS)
.map(|_| {
let (receiver, waiting, received) =
(receiver.clone(), waiting.clone(), received.clone());
runtime.spawn("a reader", async move {
waiting.fetch_add(1, Ordering::SeqCst);
let mut datagram = [0];
let len = receiver
.read_with(|socket| socket.recv(&mut datagram))
.await
.unwrap();
received.fetch_add(1, Ordering::SeqCst);
assert_eq!(len, 1);
datagram[0]
})
})
.collect();
let mut datagrams = runtime.block_on(async {
while waiting.load(Ordering::SeqCst) < TASKS {
yield_now().await;
}
for datagram in 0..TASKS {
far.send(&[datagram as u8]).unwrap();
while received.load(Ordering::SeqCst) <= datagram {
yield_now().await;
}
}
let mut datagrams = Vec::new();
for task in tasks {
datagrams.push(task.await.unwrap());
}
datagrams
});
datagrams.sort();
assert_eq!(datagrams, [0, 1, 2, 3]);
}
}
in_both_modes! {
fn several_tasks_wait_for_readable_at_once<M, P>() {
const TASKS: usize = 4;
let runtime = Runtime::<M>::new().unwrap();
let (near, mut far) = tcp_pair();
let reader = P::new(AsyncIo::new(&runtime, near).unwrap());
let waiting = Arc::new(AtomicUsize::new(0));
let tasks: Vec<_> = (0..TASKS)
.map(|_| {
let (reader, waiting) = (reader.clone(), waiting.clone());
runtime.spawn("a waiter", async move {
waiting.fetch_add(1, Ordering::SeqCst);
reader.readable().await.unwrap();
})
})
.collect();
runtime.block_on(async {
while waiting.load(Ordering::SeqCst) < TASKS {
yield_now().await;
}
for _ in 0..5 {
yield_now().await;
}
assert!(tasks.iter().all(|task| !task.is_finished()));
far.write_all(b"!").unwrap();
for task in tasks {
task.await.unwrap();
}
reader.readable().await.unwrap();
});
}
}
in_both_modes! {
fn writable_completes_on_a_fresh_socket<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, _far) = tcp_pair();
let near = AsyncIo::new(&runtime, near).unwrap();
runtime.block_on(near.writable()).unwrap();
}
}
in_both_modes! {
#[cfg(any(target_os = "linux", target_os = "android"))]
fn writable_waits_while_the_socket_has_no_room<M>() {
use std::pin::pin;
use futures::future::poll_immediate;
let runtime = Runtime::<M>::new().unwrap();
let (near, mut far) = tcp_pair();
let near = AsyncIo::new(&runtime, near).unwrap();
far.write_all(b"!").unwrap();
let filled = fill(&near);
runtime.block_on(async {
let mut writable = pin!(near.writable());
for _ in 0..5 {
assert!(poll_immediate(writable.as_mut()).await.is_none());
yield_now().await;
}
far.read_exact(&mut vec![0; filled]).unwrap();
writable.await.unwrap();
});
}
}
in_both_modes! {
fn a_dropped_read_with_does_not_keep_a_later_one_from_completing<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, mut far) = tcp_pair();
let near = AsyncIo::new(&runtime, near).unwrap();
runtime.block_on(async {
let outcome = or(async { Some(read_exactly(&near, 1).await) }, async {
runtime.sleep(DELAY).await;
None
})
.await;
assert!(outcome.is_none());
far.write_all(b"!").unwrap();
assert_eq!(read_exactly(&near, 1).await, b"!");
});
}
}
in_both_modes! {
#[cfg(any(target_os = "linux", target_os = "android"))]
fn several_tasks_write_with_on_one_async_at_once<M, P>() {
const TASKS: usize = 3;
let runtime = Runtime::<M>::new().unwrap();
let (near, mut far) = tcp_pair();
let writer = P::new(AsyncIo::new(&runtime, near).unwrap());
let filled = fill(&writer);
let waiting = Arc::new(AtomicUsize::new(0));
let tasks: Vec<_> = (0..TASKS)
.map(|_| {
let (writer, waiting) = (writer.clone(), waiting.clone());
runtime.spawn("a writer", async move {
waiting.fetch_add(1, Ordering::SeqCst);
writer
.write_with(|mut stream| stream.write(&[1]))
.await
.unwrap()
})
})
.collect();
runtime.block_on(async {
while waiting.load(Ordering::SeqCst) < TASKS {
yield_now().await;
}
for _ in 0..5 {
yield_now().await;
}
assert!(tasks.iter().all(|task| !task.is_finished()));
far.read_exact(&mut vec![0; filled]).unwrap();
for task in tasks {
assert_eq!(task.await.unwrap(), 1);
}
});
}
}
in_both_modes! {
fn giving_up_one_wait_leaves_the_other_waiting<M, P>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, mut far) = tcp_pair();
let reader = P::new(AsyncIo::new(&runtime, near).unwrap());
let waiting = Arc::new(AtomicUsize::new(0));
let mut waits = (0..2).map(|_| {
let (reader, waiting) = (reader.clone(), waiting.clone());
runtime.spawn("a reader", async move {
waiting.fetch_add(1, Ordering::SeqCst);
read_exactly(&reader, 1).await
})
});
let (given_up, kept) = (waits.next().unwrap(), waits.next().unwrap());
runtime.block_on(async {
while waiting.load(Ordering::SeqCst) < 2 {
yield_now().await;
}
assert_eq!(given_up.cancel().await, None);
far.write_all(b"!").unwrap();
assert_eq!(kept.await.unwrap(), b"!");
});
}
}
in_both_modes! {
fn the_io_traits_carry_bytes_through_an_async<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, far) = tcp_pair();
let mut near = AsyncIo::new(&runtime, near).unwrap();
let mut far = AsyncIo::new(&runtime, far).unwrap();
runtime.block_on(async {
let mut received = [0; 5];
let (read, written) = join(far.read_exact(&mut received), async {
runtime.sleep(DELAY).await;
near.write_all(b"hello").await
})
.await;
read.unwrap();
written.unwrap();
assert_eq!(&received, b"hello");
});
}
}
in_both_modes! {
fn reading_through_the_traits_goes_on_to_the_end_of_the_stream<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, far) = tcp_pair();
let mut near = AsyncIo::new(&runtime, near).unwrap();
let mut far = AsyncIo::new(&runtime, far).unwrap();
runtime.block_on(async {
near.write_all(b"the end").await.unwrap();
drop(near);
let mut received = Vec::new();
far.read_to_end(&mut received).await.unwrap();
assert_eq!(received, b"the end");
assert_eq!(far.read(&mut [0; 8]).await.unwrap(), 0);
});
}
}
in_both_modes! {
fn a_reader_and_a_writer_share_one_async_through_references<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, far) = tcp_pair();
let near = AsyncIo::new(&runtime, near).unwrap();
let far = AsyncIo::new(&runtime, far).unwrap();
let (mut near_reader, mut near_writer) = (&near, &near);
let (mut far_reader, mut far_writer) = (&far, &far);
let (mut at_near, mut at_far) = ([0; 4], [0; 4]);
let ((near_read, near_written), (far_read, far_written)) = runtime.block_on(join(
join(near_reader.read_exact(&mut at_near), async {
runtime.sleep(DELAY).await;
near_writer.write_all(b"ping").await
}),
join(far_reader.read_exact(&mut at_far), async {
runtime.sleep(DELAY).await;
far_writer.write_all(b"pong").await
}),
));
near_read.unwrap();
near_written.unwrap();
far_read.unwrap();
far_written.unwrap();
assert_eq!(&at_near, b"pong");
assert_eq!(&at_far, b"ping");
}
}
in_both_modes! {
fn vectored_reads_and_writes_carry_every_buffer<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, far) = tcp_pair();
let mut near = AsyncIo::new(&runtime, near).unwrap();
let mut far = AsyncIo::new(&runtime, far).unwrap();
runtime.block_on(async {
let written = near
.write_vectored(&[IoSlice::new(b"ab"), IoSlice::new(b"cde")])
.await
.unwrap();
assert_eq!(written, 5);
let mut received = Vec::new();
while received.len() < 5 {
let (mut first, mut second) = ([0; 2], [0; 3]);
let read = far
.read_vectored(&mut [IoSliceMut::new(&mut first), IoSliceMut::new(&mut second)])
.await
.unwrap();
assert_ne!(read, 0, "the stream ended early");
let in_first = read.min(first.len());
received.extend_from_slice(&first[..in_first]);
received.extend_from_slice(&second[..read - in_first]);
}
assert_eq!(received, b"abcde");
});
}
}
in_both_modes! {
fn closing_flushes_and_leaves_the_source_open<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, mut far) = tcp_pair();
let mut near = AsyncIo::new(&runtime, near).unwrap();
runtime.block_on(async {
near.write_all(b"one ").await.unwrap();
near.flush().await.unwrap();
near.close().await.unwrap();
let mut source = near.get_ref();
source.write_all(b"two ").unwrap();
let mut reference = &near;
reference.write_all(b"three ").await.unwrap();
reference.close().await.unwrap();
source.write_all(b"four").unwrap();
});
let mut received = [0; 18];
far.read_exact(&mut received).unwrap();
assert_eq!(&received, b"one two three four");
}
}
in_both_modes! {
fn into_inner_hands_the_source_back<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, mut far) = tcp_pair();
let raw = raw_id(&near);
let near = AsyncIo::new(&runtime, near).unwrap();
runtime.block_on(async {
let (read, ()) = join(read_exactly(&near, 1), async {
runtime.sleep(DELAY).await;
(&far).write_all(b"!").unwrap();
})
.await;
assert_eq!(read, b"!");
});
let mut cx = Context::from_waker(Waker::noop());
let waiting = near.poll_read_with(&mut cx, |mut stream| stream.read(&mut [0]));
assert!(waiting.is_pending());
let near = near.into_inner();
assert_eq!(raw_id(&near), raw);
let mut stream = &near;
let error = stream.read(&mut [0]).unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::WouldBlock);
stream.write_all(b"back").unwrap();
let mut received = [0; 4];
far.read_exact(&mut received).unwrap();
assert_eq!(&received, b"back");
}
}
#[test]
#[timeout(15000)]
fn into_inner_returns_while_another_thread_waits_on_the_source() {
let runtime = SharedRuntime::new().unwrap();
let (near, mut far) = tcp_pair();
let (trigger_near, mut trigger_far) = tcp_pair();
let watched = AsyncIo::new(&runtime, near).unwrap();
let trigger = AsyncIo::new(&runtime, trigger_near).unwrap();
let mut cx = Context::from_waker(Waker::noop());
let waiting = watched.poll_read_with(&mut cx, |mut stream| stream.read(&mut [0]));
assert!(waiting.is_pending());
let (inside, entered) = mpsc::channel();
let driver = {
let runtime = runtime.clone();
thread::spawn(move || {
runtime.block_on(async {
inside.send(()).unwrap();
let mut byte = [0];
trigger
.read_with(|mut stream| stream.read(&mut byte))
.await
.unwrap();
});
})
};
entered.recv().unwrap();
thread::sleep(DELAY);
let near = watched.into_inner();
let mut stream = &near;
stream.write_all(b"back").unwrap();
let mut received = [0; 4];
far.read_exact(&mut received).unwrap();
assert_eq!(&received, b"back");
trigger_far.write_all(b"!").unwrap();
driver.join().unwrap();
}
#[test]
#[timeout(15000)]
fn a_destructor_run_by_a_wait_takes_other_sources_back() {
const INNER: usize = 32;
let runtime = SharedRuntime::new().unwrap();
let (returned, destructed) = mpsc::channel();
let mut peers = Vec::new();
let mut inner = Vec::new();
for _ in 0..INNER {
let (near, far) = tcp_pair();
inner.push(AsyncIo::new(&runtime, near).unwrap());
peers.push(far);
}
let (near, _far) = tcp_pair();
let outer = AsyncIo::new(
&runtime,
TakesBack {
stream: near,
inner,
returned,
},
)
.unwrap();
let mut cx = Context::from_waker(Waker::noop());
for handle in &outer.get_ref().inner {
let waiting = handle.poll_read_with(&mut cx, |mut stream| stream.read(&mut [0]));
assert!(waiting.is_pending());
}
let waiting = outer.poll_read_with(&mut cx, |source| (&source.stream).read(&mut [0]));
assert!(waiting.is_pending());
let (trigger_near, mut trigger_far) = tcp_pair();
let trigger = AsyncIo::new(&runtime, trigger_near).unwrap();
let (inside, entered) = mpsc::channel();
let driver = {
let runtime = runtime.clone();
thread::spawn(move || {
runtime.block_on(async {
inside.send(()).unwrap();
let mut byte = [0];
trigger
.read_with(|mut stream| stream.read(&mut byte))
.await
.unwrap();
});
})
};
entered.recv().unwrap();
thread::sleep(DELAY);
drop(outer);
let taken_back = destructed.recv_timeout(Duration::from_secs(5)).unwrap();
assert_eq!(taken_back, INNER);
trigger_far.write_all(b"!").unwrap();
driver.join().unwrap();
}
in_both_modes! {
fn a_wait_runs_no_code_of_a_source<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (inner, _inner_far) = tcp_pair();
let (near, _far) = tcp_pair();
let inner = AsyncIo::new(&runtime, inner).unwrap();
let outer = AsyncIo::new(
&runtime,
AsksToTakeBack {
stream: near,
inner: Mutex::new(Some(inner)),
armed: AtomicBool::new(false),
asked: AtomicUsize::new(0),
},
)
.unwrap();
outer.get_ref().armed.store(true, Ordering::SeqCst);
let mut cx = Context::from_waker(Waker::noop());
let waiting = outer.poll_read_with(&mut cx, |source| (&source.stream).read(&mut [0]));
assert!(waiting.is_pending());
{
let inner = outer.get_ref().inner.lock().unwrap();
let inner = inner.as_ref().expect("the inner handle is still there");
let waiting = inner.poll_read_with(&mut cx, |mut stream| stream.read(&mut [0]));
assert!(waiting.is_pending());
}
runtime.block_on(async {
for _ in 0..3 {
runtime.sleep(Duration::from_millis(1)).await;
}
});
assert_eq!(outer.get_ref().asked.load(Ordering::SeqCst), 0);
assert!(outer.get_ref().inner.lock().unwrap().is_some());
}
}
in_both_modes! {
#[cfg(all(any(target_os = "linux", target_os = "android"), not(zruntime_poll)))]
fn a_wait_on_a_regular_file_fails<M>() {
let runtime = Runtime::<M>::new().unwrap();
let file = std::fs::File::open(concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml")).unwrap();
let file = AsyncIo::new(&runtime, file).unwrap();
let refused = runtime.block_on(file.readable()).unwrap_err();
assert_eq!(refused.kind(), io::ErrorKind::PermissionDenied);
}
}
#[test]
#[timeout(15000)]
fn a_local_async_takes_a_source_that_is_neither_send_nor_sync() {
let runtime = LocalRuntime::new().unwrap();
let (near, mut far) = tcp_pair();
let near = AsyncIo::new(&runtime, Unshareable::new(near)).unwrap();
runtime.block_on(async {
far.write_all(b"in").unwrap();
let mut received = [0; 2];
let read = near
.read_with(|source| source.read(&mut received))
.await
.unwrap();
assert_eq!(&received[..read], b"in");
let written = near
.write_with(|source| source.write(b"out"))
.await
.unwrap();
assert_eq!(written, 3);
});
let mut received = [0; 3];
far.read_exact(&mut received).unwrap();
assert_eq!(&received, b"out");
}
in_both_modes! {
#[cfg(unix)]
fn the_ends_of_a_pipe_carry_bytes_to_the_end<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (reader, writer) = std::io::pipe().unwrap();
let mut reader = AsyncIo::new(&runtime, reader).unwrap();
let mut writer = AsyncIo::new(&runtime, writer).unwrap();
let mut received = Vec::new();
let (read, written) = runtime.block_on(join(reader.read_to_end(&mut received), async {
runtime.sleep(DELAY).await;
let written = writer.write_all(b"hello").await;
drop(writer);
written
}));
assert_eq!(read.unwrap(), 5);
written.unwrap();
assert_eq!(received, b"hello");
}
}
in_both_modes! {
#[cfg(unix)]
fn a_pipe_whose_reader_closed_is_waited_on_twice<M>() {
use std::pin::pin;
use futures::future::poll_immediate;
let runtime = Runtime::<M>::new().unwrap();
let (reader, writer) = std::io::pipe().unwrap();
let writer = AsyncIo::new(&runtime, writer).unwrap();
runtime.block_on(async {
let mut writable = pin!(writer.writable());
assert!(poll_immediate(writable.as_mut()).await.is_none());
drop(reader);
writable.await.unwrap();
if let Err(refused) = writer.writable().await {
assert_eq!(refused.kind(), io::ErrorKind::BrokenPipe);
}
});
}
}
in_both_modes! {
#[cfg(unix)]
fn a_unix_stream_pair_carries_bytes_both_ways<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, far) = std::os::unix::net::UnixStream::pair().unwrap();
let mut near = AsyncIo::new(&runtime, near).unwrap();
let mut far = AsyncIo::new(&runtime, far).unwrap();
runtime.block_on(async {
let mut received = [0; 4];
let (read, written) = join(far.read_exact(&mut received), async {
runtime.sleep(DELAY).await;
near.write_all(b"ping").await
})
.await;
read.unwrap();
written.unwrap();
assert_eq!(&received, b"ping");
far.write_all(b"pong").await.unwrap();
near.read_exact(&mut received).await.unwrap();
assert_eq!(&received, b"pong");
});
}
}
in_both_modes! {
#[cfg(unix)]
fn a_child_process_output_is_read_to_the_end<M>() {
let runtime = Runtime::<M>::new().unwrap();
let mut child = Command::new("sh")
.args(["-c", "printf hello"])
.stdout(Stdio::piped())
.spawn()
.unwrap();
let stdout = child.stdout.take().unwrap();
let mut stdout = AsyncIo::new(&runtime, PipeReader::from(OwnedFd::from(stdout))).unwrap();
let mut output = Vec::new();
runtime.block_on(stdout.read_to_end(&mut output)).unwrap();
assert_eq!(output, b"hello");
assert!(child.wait().unwrap().success());
}
}
in_both_modes! {
#[cfg(unix)]
fn a_child_process_is_fed_and_read_through_its_pipes<M>() {
let runtime = Runtime::<M>::new().unwrap();
let mut child = Command::new("cat")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap();
let stdin = child.stdin.take().unwrap();
let stdout = child.stdout.take().unwrap();
let mut stdin = AsyncIo::new(&runtime, PipeWriter::from(OwnedFd::from(stdin))).unwrap();
let mut stdout = AsyncIo::new(&runtime, PipeReader::from(OwnedFd::from(stdout))).unwrap();
let mut output = Vec::new();
let (read, written) = runtime.block_on(join(stdout.read_to_end(&mut output), async {
let written = stdin.write_all(b"hello").await;
drop(stdin);
written
}));
assert_eq!(read.unwrap(), 5);
written.unwrap();
assert_eq!(output, b"hello");
assert!(child.wait().unwrap().success());
}
}
in_both_modes! {
fn an_async_prints_as_its_source_and_hands_it_out<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, _far) = tcp_pair();
let printed = format!("{near:?}");
let address = near.local_addr().unwrap();
let near = AsyncIo::new(&runtime, near).unwrap();
assert_eq!(
format!("{near:?}"),
format!("AsyncIo {{ io: {printed}, .. }}")
);
assert_eq!(near.get_ref().local_addr().unwrap(), address);
}
}
in_both_modes! {
fn an_async_names_the_descriptor_of_its_source<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (near, _far) = tcp_pair();
let address = near.local_addr().unwrap();
let raw = raw_id(&near);
let near = AsyncIo::new(&runtime, near).unwrap();
#[cfg(unix)]
{
assert_eq!(near.as_raw_fd(), raw);
assert_eq!(near.as_fd().as_raw_fd(), raw);
}
#[cfg(windows)]
{
assert_eq!(near.as_raw_socket(), raw);
assert_eq!(near.as_socket().as_raw_socket(), raw);
}
let found = SockRef::from(&near).local_addr().unwrap();
assert_eq!(found.as_socket(), Some(address));
}
}
#[test]
#[timeout(15000)]
fn shared_asyncs_are_send_sync_and_unpin() {
fn sent_and_shared<T>()
where
T: Send + Sync + Unpin,
{
}
sent_and_shared::<AsyncIo<TcpStream, Shared>>();
sent_and_shared::<Readiness<'static, Shared>>();
}
#[test]
#[timeout(15000)]
fn local_asyncs_are_unpin() {
fn unpin<T>()
where
T: Unpin,
{
}
unpin::<AsyncIo<TcpStream, Local>>();
unpin::<Readiness<'static, Local>>();
}
struct AsksToTakeBack<M>
where
M: Mode,
{
stream: TcpStream,
inner: Mutex<Option<AsyncIo<TcpStream, M>>>,
armed: AtomicBool,
asked: AtomicUsize,
}
impl<M> AsksToTakeBack<M>
where
M: Mode,
{
fn asked(&self) {
if !self.armed.load(Ordering::SeqCst) {
return;
}
self.asked.fetch_add(1, Ordering::SeqCst);
let inner = self.inner.lock().unwrap().take();
if let Some(inner) = inner {
drop(inner.into_inner());
}
}
}
#[cfg(unix)]
impl<M> AsFd for AsksToTakeBack<M>
where
M: Mode,
{
fn as_fd(&self) -> BorrowedFd<'_> {
self.asked();
self.stream.as_fd()
}
}
#[cfg(windows)]
impl<M> AsSocket for AsksToTakeBack<M>
where
M: Mode,
{
fn as_socket(&self) -> BorrowedSocket<'_> {
self.asked();
self.stream.as_socket()
}
}
struct TakesBack {
stream: TcpStream,
inner: Vec<AsyncIo<TcpStream, Shared>>,
returned: mpsc::Sender<usize>,
}
impl Drop for TakesBack {
fn drop(&mut self) {
let taken_back = self.inner.drain(..).map(AsyncIo::into_inner).count();
let _ = self.returned.send(taken_back);
}
}
#[cfg(unix)]
impl AsFd for TakesBack {
fn as_fd(&self) -> BorrowedFd<'_> {
self.stream.as_fd()
}
}
#[cfg(windows)]
impl AsSocket for TakesBack {
fn as_socket(&self) -> BorrowedSocket<'_> {
self.stream.as_socket()
}
}
const DELAY: Duration = Duration::from_millis(50);
struct Unshareable {
socket: OwnedSource,
_local: Rc<()>,
}
#[cfg(unix)]
type OwnedSource = OwnedFd;
#[cfg(windows)]
type OwnedSource = OwnedSocket;
impl Unshareable {
fn new(stream: TcpStream) -> Self {
Self {
socket: stream.into(),
_local: Rc::new(()),
}
}
fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
(&*SockRef::from(&self.socket)).read(buf)
}
fn write(&self, buf: &[u8]) -> io::Result<usize> {
(&*SockRef::from(&self.socket)).write(buf)
}
}
#[cfg(unix)]
impl AsFd for Unshareable {
fn as_fd(&self) -> BorrowedFd<'_> {
self.socket.as_fd()
}
}
#[cfg(windows)]
impl AsSocket for Unshareable {
fn as_socket(&self) -> BorrowedSocket<'_> {
self.socket.as_socket()
}
}
async fn read_exactly<M>(io: &AsyncIo<TcpStream, M>, len: usize) -> Vec<u8>
where
M: Mode,
{
let mut bytes = vec![0; len];
let mut filled = 0;
while filled < len {
let read = io
.read_with(|mut stream| stream.read(&mut bytes[filled..]))
.await
.unwrap();
assert_ne!(read, 0, "the stream ended early");
filled += read;
}
bytes
}
async fn write_everything<M>(io: &AsyncIo<TcpStream, M>, bytes: &[u8])
where
M: Mode,
{
let mut sent = 0;
while sent < bytes.len() {
sent += io
.write_with(|mut stream| stream.write(&bytes[sent..]))
.await
.unwrap();
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn fill(io: &AsyncIo<TcpStream, impl Mode>) -> usize {
let mut stream = io.get_ref();
let mut filled = 0;
loop {
match stream.write(&[0; 4096]) {
Ok(written) => filled += written,
Err(e) if e.kind() == io::ErrorKind::WouldBlock => return filled,
Err(e) => panic!("a write to a socket with room failed: {e}"),
}
}
}
fn would_block<T>(result: &io::Result<T>) -> bool {
matches!(result, Err(e) if e.kind() == io::ErrorKind::WouldBlock)
}
fn pattern(len: usize) -> Vec<u8> {
(0..len).map(|i| (i % 251) as u8).collect()
}
fn tcp_pair() -> (TcpStream, TcpStream) {
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
let far = TcpStream::connect(listener.local_addr().unwrap()).unwrap();
let near = loop {
let (accepted, _) = listener.accept().unwrap();
if accepted.peer_addr().unwrap() == far.local_addr().unwrap() {
break accepted;
}
};
near.set_nodelay(true).unwrap();
far.set_nodelay(true).unwrap();
(near, far)
}
fn udp_pair() -> (UdpSocket, UdpSocket) {
let near = UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
let far = UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
near.connect(far.local_addr().unwrap()).unwrap();
far.connect(near.local_addr().unwrap()).unwrap();
(near, far)
}
#[cfg(unix)]
type RawId = std::os::fd::RawFd;
#[cfg(windows)]
type RawId = std::os::windows::io::RawSocket;
#[cfg(unix)]
fn raw_id(socket: &TcpStream) -> RawId {
socket.as_raw_fd()
}
#[cfg(windows)]
fn raw_id(socket: &TcpStream) -> RawId {
socket.as_raw_socket()
}