1 unstable release
0.1.0 | Jan 14, 2024 |
---|
#1886 in Asynchronous
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:
- Create a
Poll
instance (and learn what it is). - Register an [event source].
- 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 Event
s 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