Module tokio::reactor
[−]
[src]
Event loop that drives Tokio I/O resources.
This module contains Reactor
, which is the event loop that drives all
Tokio I/O resources. It is the reactor's job to receive events from the
operating system (epoll, kqueue, IOCP, etc...) and forward them to
waiting tasks. It is the bridge between operating system and the futures
model.
Overview
When using Tokio, all operations are asynchronous and represented by futures. These futures, representing the application logic, are scheduled by an executor (see runtime model for more details). Executors wait for notifications before scheduling the future for execution time, i.e., nothing happens until an event is received indicating that the task can make progress.
The reactor receives events from the operating system and notifies the executor.
Let's start with a basic example, establishing a TCP connection.
use tokio::prelude::*; use tokio::net::TcpStream; let addr = "93.184.216.34:9243".parse().unwrap(); let connect_future = TcpStream::connect(&addr); let task = connect_future .and_then(|socket| { println!("successfully connected"); Ok(()) }) .map_err(|e| println!("failed to connect; err={:?}", e)); tokio::run(task);
Establishing a TCP connection usually cannot be completed immediately.
TcpStream::connect
does not block the current thread. Instead, it
returns a future that resolves once the TCP connection has
been established. The connect future itself has no way of knowing when the
TCP connection has been established.
Before returning the future, TcpStream::connect
registers the socket
with a reactor. This registration process, handled by Registration
, is
what links the TcpStream
with the Reactor
instance. At this point,
the reactor starts listening for connection events from the operating system
for that socket.
Once the connect future is passed to tokio::run
, it is spawned onto a
thread pool. The thread pool waits until it is notified that the connection
has completed.
When the TCP connection is established, the reactor receives an event from
the operating system. It then notifies the thread pool, telling it that the
connect future can complete. At this point, the thread pool will schedule
the task to run on one of its worker threads. This results in the and_then
closure to get executed.
Lazy registration
Notice how the snippet above does not explicitly reference a reactor. When
TcpStream::connect
is called, it registers the socket with a reactor,
but no reactor is specified. This works because the registration process
mentioned above is actually lazy. It doesn't actually happen in the
connect
function. Instead, the registration is established the first
time that the task is polled (again, see runtime model).
A reactor instance is automatically made available when using the Tokio
runtime, which is done using tokio::run
. The Tokio runtime's executor
sets a thread-local variable referencing the associated Reactor
instance
and Handle::current
(used by Registration
) returns the reference.
Implementation
The reactor implementation uses mio
to interface with the operating
system's event queue. A call to Reactor::poll
results in in a single
call to Poll::poll
which in turn results in a single call to the
operating system's selector.
The reactor maintains state for each registered I/O resource. This tracks
the executor task to notify when events are provided by the operating
system's selector. This state is stored in a Sync
data structure and
referenced by Registration
. When the Registration
instance is
dropped, this state is cleaned up. Because the state is stored in a Sync
data structure, the Registration
instance is able to be moved to other
threads.
By default, a runtime's default reactor runs on a background thread. This ensures that application code cannot significantly impact the reactor's responsiveness.
Integrating with the reactor
Tokio comes with a number of I/O resources, like TCP and UDP sockets, that automatically integrate with the reactor. However, library authors or applications may wish to implement their own resources that are also backed by the reactor.
There are a couple of ways to do this.
If the custom I/O resource implements mio::Evented
and implements
std::Read
and / or std::Write
, then PollEvented
is the most
suited.
Otherwise, Registration
can be used directly. This provides the lowest
level primitive needed for integrating with the reactor: a stream of
readiness events.
Structs
Background |
Handle to the reactor running on a background thread. |
Handle |
A reference to a reactor. |
PollEvented2 |
Associates an I/O resource that implements the |
Reactor |
The core reactor, or event loop. |
Registration |
Associates an I/O resource with the reactor instance that drives it. |
Turn |
Return value from the |