rust, smol Async I/O
smol 实现同步 I/O 包装为异步操作,从而可以与 epoll 之类的操作系统异步模型集成。
值得注意的是,Async IO 仅支持网络类型的操作,也就是 TCP/UDP/Unix socket。不支持普通文件类的操作。事实上,文件操作在 smol 中是通过 Blocking Executor 开独立线程池的方式实现的。
Aync<T>
Source 是沟通 Async I/O 与 Reactor 的桥梁。
pub struct Async<T> {
/// A source registered in the reactor.
source: Arc<Source>,
/// The inner I/O handle.
io: Option<Box<T>>,
}
pub(crate) struct Source {
/// Raw file descriptor on Unix platforms.
#[cfg(unix)]
pub(crate) raw: RawFd,
/// Raw socket handle on Windows.
#[cfg(windows)]
pub(crate) raw: RawSocket,
/// The key of this source obtained during registration.
key: usize,
/// Tasks interested in events on this source.
wakers: Mutex<Wakers>,
}
不同操作系统关于Socket I/O 的实现有所差异。在 windows 下,为 RawSocket,Unix 为 RawFd。以 windows 为例:
#[cfg(windows)]
impl<T: AsRawSocket> Async<T> {
pub fn new(io: T) -> io::Result<Async<T>> {
Ok(Async {
source: Reactor::get().insert_io(io.as_raw_socket())?,
io: Some(Box::new(io)),
})
}
}
构建一个 Async<T>
,并注册 source 至 Reactor。
read_with
执行异步读。通过future::poll_fn() 执行一个包装为 Future的函数,函数内部执行 op,根据 op 返回的结果决定是否完成 Future。
future::poll_fn() 将一个函数包装为一个 Future。注意这样的函数可以返回 Poll::Ready 或 Poll::Pending,这一点与 async {} (只能返回Ready) 不同。
pub async fn read_with<R>(&self, op: impl FnMut(&T) -> io::Result<R>) -> io::Result<R> {
let mut op = op;
future::poll_fn(|cx| {
match op(self.get_ref()) {
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
res => return Poll::Ready(res),
}
futures_util::ready!(poll_future(cx, self.readable()))?;
Poll::Pending
})
.await
}
一个 read_with() 的典型用法,调用 socket.accept:
let listener = Async::<TcpListener>::bind("127.0.0.1:0")?;
// Accept a new client asynchronously.
let (stream, addr) = listener.read_with(|l| l.accept()).await?;
write_with
异步写操作。与 read_with 类似。
pub async fn write_with<R>(&self, op: impl FnMut(&T) -> io::Result<R>) -> io::Result<R> {
let mut op = op;
future::poll_fn(|cx| {
match op(self.get_ref()) {
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
res => return Poll::Ready(res),
}
futures_util::ready!(poll_future(cx, self.writable()))?;
Poll::Pending
})
.await
}
示例代码:
let len = socket.write_with(|s| s.send(msg)).await?;
read_with/write_with 分别还有 mut 版本,允许 op 改变 Async<T>
drop
将 socket 从 Reactor 中移除。
fn drop(&mut self) {
if self.io.is_some() {
// Deregister and ignore errors because destructors should not panic.
let _ = Reactor::get().remove_io(&self.source);
// Drop the I/O handle to close it.
self.io.take();
}
}
AsyncRead/AsyncWrite
Async<T>
实现了 futures-io 中定义的 AsyncRead/AsyncWrite,因此可以使用类似于 std::io Read/Write 的读写方式。很明显,Async<T>
的 AsyncRead/AsyncWrite 一定是基于 read_with/write_with 实现的。另外注意,泛型参数 T 必须实现 Read/Write。代码如下:
impl<T: Read> AsyncRead for Async<T> {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
poll_future(cx, self.read_with_mut(|io| io.read(buf)))
}
fn poll_read_vectored(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
) -> Poll<io::Result<usize>> {
poll_future(cx, self.read_with_mut(|io| io.read_vectored(bufs)))
}
}
impl<T> AsyncRead for &Async<T>
where
for<'a> &'a T: Read,
{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
poll_future(cx, self.read_with(|io| (&*io).read(buf)))
}
fn poll_read_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
) -> Poll<io::Result<usize>> {
poll_future(cx, self.read_with(|io| (&*io).read_vectored(bufs)))
}
}
impl<T: Write> AsyncWrite for Async<T> {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
poll_future(cx, self.write_with_mut(|io| io.write(buf)))
}
...
}
impl<T> AsyncWrite for &Async<T>
where
for<'a> &'a T: Write,
{
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
poll_future(cx, self.write_with(|io| (&*io).write(buf)))
}
...
}
AsyncReadExt/AsyncWriteExt
futures-io 中,实际上通过 AsyncReadExt/AsyncWriteExt 来实现常用的 read/write 操作。AsyncReadExt 代码如下。可以看到实现了 AsyncRead 的类型自然也实现了AsyncReadExt。
/// An extension trait which adds utility methods to `AsyncRead` types.
pub trait AsyncReadExt: AsyncRead {
fn chain<R>(self, next: R) -> Chain<Self, R>
where
Self: Sized,
R: AsyncRead,
{
Chain::new(self, next)
}
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self>
where Self: Unpin,
{
Read::new(self, buf)
}
fn read_vectored<'a>(&'a mut self, bufs: &'a mut [IoSliceMut<'a>]) -> ReadVectored<'a, Self>
where Self: Unpin,
{
ReadVectored::new(self, bufs)
}
fn read_exact<'a>(
&'a mut self,
buf: &'a mut [u8],
) -> ReadExact<'a, Self>
where Self: Unpin,
{
ReadExact::new(self, buf)
}
fn read_to_end<'a>(
&'a mut self,
buf: &'a mut Vec<u8>,
) -> ReadToEnd<'a, Self>
where Self: Unpin,
{
ReadToEnd::new(self, buf)
}
fn read_to_string<'a>(
&'a mut self,
buf: &'a mut String,
) -> ReadToString<'a, Self>
where Self: Unpin,
{
ReadToString::new(self, buf)
}
...
}
impl<R: AsyncRead + ?Sized> AsyncReadExt for R {}
read 实现
因此任意实现了 AsyncReadExt 的类型都可以 调用其 read 方法。如下:
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self>
where Self: Unpin,
{
Read::new(self, buf)
}
返回一个实现了 Future 的 Read 数据结构:
pub struct Read<'a, R: ?Sized> {
reader: &'a mut R, // reader 是传入的 Async<T>
buf: &'a mut [u8],
}
impl<R: ?Sized + Unpin> Unpin for Read<'_, R> {}
impl<'a, R: AsyncRead + ?Sized + Unpin> Read<'a, R> {
pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self {
Read { reader, buf }
}
}
impl<R: AsyncRead + ?Sized + Unpin> Future for Read<'_, R> {
type Output = io::Result<usize>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = &mut *self;
Pin::new(&mut this.reader).poll_read(cx, this.buf)
}
}
这里 Pin::new(&mut this.reader).poll_read(cx, this.buf) 就是 Async<T>
的 AsyncRead::poll_read()。
Async<TcpListener>
Async<TcpStream>
以及其他…
此类特定 Async<T>
实现特定 socket 类型的特定操作。如 TcpListener 实现了 accept。具体参见代码,不赘述。