1 unstable release

0.1.0 Jan 14, 2024

#1886 in Asynchronous

MIT license

400KB
7K SLoC

verypoll - just poll the IO, please.


lib.rs:

verypoll is a fast, low-level I/O library for Rust focusing on non-blocking APIs and event notification for building high performance I/O apps with as little overhead as possible over the OS abstractions.

Usage

Using verypoll starts by creating a Poll, which reads events from the OS and puts them into Events. You can handle I/O events from the OS with it.

For more detail, see Poll.

Guide

A getting started guide is available in the guide module.

Available features

The available features are described in the features module. Unix only extensions. Unix pipe.

See the [new] function for documentation. Windows only extensions.

verypoll's optional features.

This document describes the available features in verypoll.

verypoll by default provides only a shell implementation that panic!s the moment it is actually run. To run it requires OS support, this is enabled by activating the os-poll feature.

This makes Poll, Registry and Waker functional.

os-ext enables additional OS specific facilities. These facilities can be found in the unix and windows module.

The net feature enables networking primitives in the net module.

Getting started guide.

In this guide we'll do the following:

  1. Create a Poll instance (and learn what it is).
  2. Register an [event source].
  3. Create an event loop.

At the end you'll have a very small (but quick) TCP server that accepts connections and then drops (disconnects) them.

1. Creating a Poll instance

Using verypoll starts by creating a Poll instance, which monitors events from the OS and puts them into Events. This allows us to execute I/O operations based on what operations are ready.

use verypoll::{Poll, Events};

fn main() -> std::io::Result<()> {

// Poll allows for polling of readiness events. let poll = Poll::new()?; // Events is collection of readiness Events and can be filled by // calling Poll::poll. let events = Events::with_capacity(128);

drop((poll, events));

Ok(())

}


For example if we're using a [`TcpListener`],  we'll only want to
attempt to accept an incoming connection *iff* any connections are
queued and ready to be accepted. We don't want to waste our time if no
connections are ready.

[`TcpListener`]: ../net/struct.TcpListener.html

## 2. Registering event source

After we've created a [`Poll`] instance that monitors events from the OS
for us, we need to provide it with a source of events. This is done by
registering an [event source]. As the name “event source” suggests it is
a source of events which can be polled using a `Poll` instance. On Unix
systems this is usually a file descriptor, or a socket/handle on
Windows.

In the example below we'll use a [`TcpListener`] for which we'll receive
an event (from [`Poll`]) once a connection is ready to be accepted.

[event source]: ../event/trait.Source.html

// Create a `TcpListener`, binding it to `address`.
let mut listener = TcpListener::bind(address)?;

// Next we register it with `Poll` to receive events for it. The `SERVER`
// `Token` is used to determine that we received an event for the listener
// later on.
const SERVER: Token = Token(0);
poll.registry().register(&mut listener, SERVER, Interest::READABLE)?;

Multiple event sources can be registered (concurrently), so we can monitor multiple sources at a time.

3. Creating the event loop

After we've created a Poll instance and registered one or more event sources with it, we can poll it for events. Polling for events is simple, we need a container to store the events: Events and need to do something based on the polled events (this part is up to you, we can't do it all!). If we do this in a loop we've got ourselves an event loop.

The example below shows the event loop in action, completing our small TCP server.

use std::io;

use std::time::Duration;

use verypoll::net::TcpListener;

use verypoll::{Poll, Token, Interest, Events};

fn main() -> io::Result<()> {

let mut poll = Poll::new()?;

let mut events = Events::with_capacity(128);

let address = "127.0.0.1:0".parse().unwrap();

let mut listener = TcpListener::bind(address)?;

const SERVER: Token = Token(0);

poll.registry().register(&mut listener, SERVER, Interest::READABLE)?;

// Start our event loop. loop { // Poll the OS for events, waiting at most 100 milliseconds. poll.poll(&mut events, Some(Duration::from_millis(100)))?;

 // Process each event.
 for event in events.iter() {
     // We can use the token we previously provided to `register` to
     // determine for which type the event is.
     match event.token() {
         SERVER => loop {
             // One or more connections are ready, so we'll attempt to
             // accept them (in a loop).
             match listener.accept() {
                 Ok((connection, address)) => {
                     println!("Got a connection from: {}", address);

drop(connection);

                 },
                 // A "would block error" is returned if the operation
                 // is not ready, so we'll stop trying to accept
                 // connections.
                 Err(ref err) if would_block(err) => break,
                 Err(err) => return Err(err),
             }
         }

_ => unreachable!(),

     }
 }

return Ok(());

}

fn would_block(err: &io::Error) -> bool { err.kind() == io::ErrorKind::WouldBlock }

}

Dependencies

~0–8.5MB
~63K SLoC