Polling in a loop

To obtain the value of a Future, one obvious approach is to call the method poll in a loop until it returns Poll::Ready(T).

Example

“UDP Hello” examples

The “UDP Hello” examples, which run on UDP protocol, send data b"hello"(repeatedly) to a given echo service and receive the response(s).

The examples include “loop-poll-hello” and “loop-poll-many-hello”.

Recv

Recv, as a major part of the “UDP Hello” examples, is a Future which receives one response from a given service. Its poll implementation reads data from a given UDP socket. Recv requires and assumes that the UDP socket is in nonblocking mode.

Source: examples/src/recv.rs


use std::future::Future;
use std::io;
use std::net::UdpSocket;
use std::task::Poll;

/// Future which receives one response from a given service.
pub struct Recv {
    socket: UdpSocket,
}

impl Recv {
    /// Creates a new Recv.
    ///
    /// # Safety
    ///
    /// Caller must ensure that `socket` has been moved into nonblocking mode.
    #[must_use]
    pub unsafe fn new(socket: UdpSocket) -> Self {
        Self { socket }
    }
}

impl Future for Recv {
    type Output = Vec<u8>;

    fn poll(
        self: std::pin::Pin<&mut Self>,
        _cx: &mut std::task::Context<'_>,
    ) -> Poll<Self::Output> {
        let mut buf = [0; 1024];
        match self.socket.recv(&mut buf) {
            Ok(n) => Poll::Ready(buf[..n].to_vec()),
            Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => std::task::Poll::Pending,
            Err(e) => panic!("IO error: {e}"),
        }
    }
}

Example: “loop-poll-hello”

“loop-poll-hello” sends b"hello" to a given echo service, creates a Recv, and calls poll of the Recv until Poll::Ready(Vec<u8>) is returned.

Source: examples/src/bin/loop-poll-hello.rs

Example: “loop-poll-many-hello”

“loop-poll-many-hello” repeatedly sends b"hello" to a given echo service for 1000 times, creates a vector of 1000 Recvs, and calls poll of each Recv until all returns Poll::Ready(Vec<u8>).

Source: examples/src/bin/loop-poll-many-hello.rs

The echo service: “Lazy Echo”

“Lazy Echo” is an UDP echo service: the service sends received data back to where it comes from. The service waits one second before any sending, hence lazy.

“Lazy Echo” has two implementations: “lazy-echo-udp-smol” and “lazy-echo-udp-tokio” – same behavior but with different async runtimes.

Run the examples

  1. Start the echo service, either “lazy-echo-udp-smol” or “lazy-echo-udp-tokio”

    • In the “examples” directory, execute:

      cargo run --bin lazy-echo-udp-smol
      

      OR

      cargo run --bin lazy-echo-udp-tokio
      
    • Keep the service running

  2. Run “loop-poll-hello”. In the “examples” directory, execute:

    cargo run --bin loop-poll-hello
    
  3. Run “loop-poll-many-hello”. In the “examples” directory, execute:

    cargo run --bin loop-poll-many-hello
    

NOTE

“loop-poll-hello” and “loop-poll-many-hello” should both finish in about 1 second.


Problems

Wastes CPU time

As a Future has to be polled repeatedly and aggressively in a loop, CPU time is wasted when data isn’t ready.

Extreme example: when the data of a Future never becomes ready, the polling loop is infinite.

Doesn’t scale

As all Futures have to be “polled” one after another, when a Future becomes ready, its data may not be polled promptly.

Extreme example: when there are 1000 Futures and only the last one is ready, a program has to poll 999 times before the last one can be accessed.