Будущее Tokio, которое читает канал и использует poll_fn и try_ready, никогда не завершается - PullRequest
0 голосов
/ 24 февраля 2019

У меня есть будущее Токио, которое никогда не завершается (rx - это Receiver, а sock - это Токио 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(())
    }));
}

Репо

1 Ответ

0 голосов
/ 25 февраля 2019

Использование этой версии вашего будущего показывает проблему:

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))
});
...