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。具体参见代码,不赘述。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Rustasync/await是一种异步编程模式,可以在代码中使用关键字async和await来创建和等待异步任务。 以下是使用async/await的基本步骤: 1. 在函数签名中添加async关键字,表示该函数是一个异步函数。 2. 在函数体内使用await关键字等待异步任务的结果。await关键字会暂停当前函数的执行,直到异步任务完成并返回结果。 3. 在异步函数内部,可以使用异步API来创建和启动异步任务。 例如,下面是一个简单的使用async/await的例子: ```rust async fn do_something_async() -> String { // 创建一个异步任务,等待1秒后返回结果 let result = tokio::time::delay_for(Duration::from_secs(1)).await; // 返回一个字符串 return "Hello, World!".to_string(); } fn main() { // 创建一个异步运行时 let rt = tokio::runtime::Runtime::new().unwrap(); // 在异步运行时中执行异步函数 let result = rt.block_on(async { // 等待异步任务完成并返回结果 let result = do_something_async().await; // 返回异步任务的结果 return result; }); // 输出结果 println!("{}", result); } ``` 在这个例子中,我们首先定义了一个异步函数do_something_async(),它创建了一个异步任务,等待1秒后返回一个字符串。然后,在main函数中,我们创建了一个异步运行时,并使用block_on函数在异步运行时中执行异步函数do_something_async(),并等待它的结果。最后,我们输出异步任务的结果。 需要注意的是,Rustasync/await需要配合异步运行时一起使用,例如上述例子中使用的tokio运行时。异步运行时提供了异步任务的执行环境和调度器,负责管理异步任务的执行和调度。因此,在使用async/await时需要选择一个合适的异步运行时,并将异步函数放入异步运行时中执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值