从通道读取并使用poll_fn和try_ready的Tokio未来永远不会完成

问题描述 投票:0回答:1

我有一个永远不会完成的东京未来(rx是一个Receiversock是一个tokio UdpSocket)。它基本上从数据包队列中读取数据包并通过套接字传输它们:

poll_fn(move || {
    match try_ready!(rx
        .poll()
        .map_err(|_e| tokio::io::Error::new(tokio::io::ErrorKind::Other, "Poll error")))
    {
        Some((packet, to)) => {
            println!(
                "Rx: Received {} bytes for {}: {:?}",
                packet.len(),
                to,
                packet.as_slice(),
            );
            try_ready!(sock.poll_send_to(packet.as_slice(), &to));
            println!("Sent");
        }
        None => println!("Rx end"),
    }
    Ok(futures::Async::Ready(()))
})
.map_err(|e: tokio::io::Error| println!("Error: {:?}", e))

它执行直到poll_send_to线(println!执行之前的poll_send_to,之后的println!没有)然后在没有发送数据包的情况下永远等待。

我用以下内容替换了上面的未来,以确保它不是套接字问题(我之前认为是一些问题是片状通知):

poll_fn(move || {
    let packet = vec![0;10];
    let to = SocketAddr::from_str("127.0.0.1:8001").expect("Parse error");
    try_ready!(sock.poll_send_to(packet.as_slice(), &to));
    Ok(futures::Async::Ready(()))
})
.map_err(|e: tokio::io::Error| println!("Error: {:?}", e))

这个未来非常有效 - 它按预期发送数据包并退出程序。

我认为问题不在于消息通道,因为rx可以成功地打造poll并打印println消息。考虑到第二个未来的作用,我认为问题不在于套接字。我正在通过Wireshark直接观察数据包,所以我认为这也不是我观察的问题。

我对Rust和Tokio很新,所以我可能忽略了一些基本事实(例如,在同一个未来两次不能try_ready,未来不会从之前停止的地方恢复,等等)。

你能帮我解决第一个未来的问题吗?

use futures::future::lazy;
use futures::stream::Stream;
use futures::try_ready;
use std::net::SocketAddr;
use std::str::FromStr;
use tokio;
use tokio::net::UdpSocket;
use tokio::prelude::future::poll_fn;
use tokio::prelude::Future;

fn main() {
    let mut sock = UdpSocket::bind(&SocketAddr::from_str("127.0.0.1:8000").expect("Parse error"))
        .expect("Bind error");

    let (mut tx, mut rx) = tokio::sync::mpsc::channel::<(Vec<u8>, SocketAddr)>(2000);

    tokio::run(lazy(move || {
        //----------------- This future works ----------------//
        // tokio::spawn(
        //     poll_fn(move || {
        //         let packet = vec![70; 10];
        //         let to = SocketAddr::from_str("127.0.0.1:8001").expect("Parse error");
        //         try_ready!(sock.poll_send_to(packet.as_slice(), &to));
        //         Ok(futures::Async::Ready(()))
        //     })
        //     .map_err(|e: tokio::io::Error| println!("Error: {:?}", e)),
        // );

        //----------------- This future doesn't ----------------//
        tokio::spawn(
            poll_fn(move || {
                match try_ready!(rx
                    .poll()
                    .map_err(|_e| tokio::io::Error::new(tokio::io::ErrorKind::Other, "Poll error")))
                {
                    Some((packet, to)) => {
                        // This is printed
                        println!(
                            "Rx: Received {} bytes for {}: {:?}",
                            packet.len(),
                            to,
                            packet.as_slice(),
                        );
                        try_ready!(sock.poll_send_to(packet.as_slice(), &to));
                        // This is never printed
                        println!("Sent");
                    }
                    None => println!("Rx end"),
                }
                Ok(futures::Async::Ready(()))
            })
            .map_err(|e: tokio::io::Error| println!("Error: {:?}", e)),
        );

        //----------------- This future queues a packet ----------------//
        tokio::spawn(
            poll_fn(move || {
                try_ready!(tx.poll_ready());
                tx.try_send((
                    vec![70; 10],
                    SocketAddr::from_str("127.0.0.1:8001").expect("Parse error"),
                ))
                .expect("Send error");
                // Wait permanently so message channel doesn't get disconnected
                // Achieved differently in production
                Ok(futures::Async::NotReady)
            })
            .map_err(|e: tokio::sync::mpsc::error::SendError| println!("Error: {:?}", e)),
        );

        Ok(())
    }));
}

Repo

rust future rust-tokio
1个回答
1
投票

使用此版本的未来会显示问题:

tokio::spawn(
    future::poll_fn(move || {
        eprintln!("Starting poll_fn");

        let from_channel = rx
            .poll()
            .map_err(|_e| tokio::io::Error::new(tokio::io::ErrorKind::Other, "Poll error"));

        if let Some((packet, to)) = futures::try_ready!(dbg!(from_channel)) {
            futures::try_ready!(dbg!(sock.poll_send_to(packet.as_slice(), &to)));
        }
        Ok(futures::Async::Ready(()))
    })
    .map_err(|e: tokio::io::Error| println!("Error: {:?}", e)),
);

这是略微清理的输出:

Starting poll_fn
[src/main.rs:21] from_channel = Ok(NotReady)

Starting poll_fn
[src/main.rs:21] from_channel = Ok(Ready(Some(/* ... */)))
[src/main.rs:22] sock.poll_send_to(packet.as_slice(), &to) = Ok(NotReady)

Starting poll_fn
[src/main.rs:21] from_channel = Ok(NotReady)

用语言:

  1. 未来开始。
  2. 通道没有任何准备;频道注册通知。
  3. 未来的回归。
  4. 通道获取值并通知任务。
  5. 未来又开始了。
  6. 通道上有一个值可用。
  7. 插座上的发送尚未就绪;套接字注册通知。
  8. 未来的回归。
  9. 套接字被清除并通知任务。
  10. 未来又开始了。
  11. 通道没有任何准备;频道注册通知。
  12. 未来的回归。
  13. 没有其他任何东西被添加到频道。

简而言之,您未能在未来正确维护状态机。你需要知道你最后一次运行到底有多远,并在下次运行时从那一点开始。

有一个原因是async / await语法备受期待:它将为您编写这些状态机。

我不知道为什么你选择使用基于poll的低级接口。我会使用基于Future的更高级别的:

tokio::spawn({
    rx.fold(sock, |sock, (packet, to)| {
        sock.send_dgram(packet, &to)
            .inspect(|_| println!("Sent it!"))
            .map(|(sock, _)| sock)
            .map_err(|e| panic!("Error: {:?}", e))
    })
    .map(drop)
    .map_err(|e| panic!("Error: {:?}", e))
});

基于Future的接口[...]在出错时销毁套接字(和缓冲区)

这是使用基于poll的界面的一个很好的理由,但我仍然只需要花费足够长的时间来实现自己的未来。像这样的东西:

struct X(UdpSocket);
struct XSendGram<D> {
    sock: Option<UdpSocket>,
    data: D,
    addr: SocketAddr,
}

impl X {
    fn send_dgram<D>(self, data: D, addr: SocketAddr) -> XSendGram<D> {
        XSendGram {
            sock: Some(self.0),
            data,
            addr,
        }
    }
}

impl<D> Future for XSendGram<D>
where
    D: AsRef<[u8]>,
{
    type Item = (X, usize);
    type Error = (X, std::io::Error);

    fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> {
        let mut sock = self.sock.take().expect("Future called after success or failure");

        match sock.poll_send_to(self.data.as_ref(), &self.addr) {
            Ok(Async::Ready(bytes)) => Ok(Async::Ready((X(sock), bytes))),
            Ok(Async::NotReady) => {
                self.sock = Some(sock); // Restore it for the next call
                Ok(Async::NotReady)
            }
            Err(e) => Err((X(sock), e)),
        }
    }
}
tokio::spawn({
    rx.fold(X(sock), |sock, (packet, to)| {
        sock.send_dgram(packet, to)
            .inspect(|(_, n)| println!("Sent {} bytes", n))
            .then(|r| match r {
                Ok((sock, _)) | Err((sock, _)) => future::ok(sock),
            })
    })
    .map(drop)
    .map_err(|e| panic!("Error: {:?}", e))
});
© www.soinside.com 2019 - 2024. All rights reserved.