What is Tokio?

Tokio allows developers to write asynchronous programs in the Rust programminglanguage. Instead of synchronously waiting for long-running operations (like readinga file or waiting for a timer to complete) before moving on to the next thing,Tokio allows developers to write programs where execution continues while thelong-running operations are in progress.

More specifically, Tokio is an event-driven, non-blocking I/O platformfor writing asynchronous applications with Rust. At a high level, itprovides a few major components:

  • A multithreaded, work-stealing based task scheduler.
  • A reactor backed by the operating system’s event queue (epoll, kqueue,IOCP, etc…).
  • Asynchronous TCP and UDP sockets.
    These components provide the runtime components necessary for buildingan asynchronous application.

Fast

Tokio is built on the Rust programming language, which is itself veryfast. Applications built with Tokio will get those same benefits. Tokio’s designis also geared towards enabling applications to be as fast as possible.

Zero-cost abstractions

Tokio is built around futures. Futures aren’t a new idea, but the way Tokiouses them is unique. Unlike futures from other languages, Tokio’sfutures compile down to a state machine. There is no added overhead fromsynchronization, allocation, or other costs common with future implementations.

Note that providing zero-cost abstractions does not mean that Tokio itself hasno cost. It means that using Tokio results in an end product with equivalentoverhead to not using Tokio.

Concurrency

Out of the box, Tokio provides a multi-threaded, work-stealing, scheduler. So,when you start the Tokio runtime, you are already using all of your computer’sCPU cores.

Modern computers increase their performance by adding cores, so being able toutilize many cores is critical for writing fast applications.

Non-blocking I/O

When hitting the network, Tokio will use the most efficient system available tothe operating system. On Linux this means epoll, bsd platforms provide kqueue,and Windows has I/O completion ports.

This allows multiplexing many sockets on a single thread and receivingoperating system notifications in batches, thus reducing system calls. All thisleads to less overhead for the application.

Reliable

While Tokio cannot prevent all bugs, it is designed to minimize them. It doesthis by providing APIs that are hard to misuse. At the end of the day, you canship applications to production with confidence.

Ownership and type system

Rust’s ownership model and type system enables implementing system levelapplications without the fear of memory unsafety. It prevents classic bugssuch as accessing uninitialized memory and use after free. It does this withoutadding any run-time overhead.

Further, APIs are able to leverage the type system to provide hard to misuseAPIs. For example, Mutex does not require the user to explicitly unlock.

Backpressure

In push based systems, when a producer produces data faster than the consumercan process, data will start backing up. Pending data is stored in memory.Unless the producer stops producing, the system will eventually run out ofmemory and crash. The ability for a consumer to inform the producer to slow downis backpressure.

Because Tokio uses a poll based model, the problem mostly just goes away.Producers are lazy by default. They will not produce any data unless theconsumer asks them to. This is built into Tokio’s foundation.

Cancellation

Because of Tokio’s poll based model, computations do no work unless they arepolled. Dependents of that computation hold a future representing theresult of that computation. If the result is no longer needed, the future isdropped. At this point, the computation will no longer be polled and thusperform no more work.

Thanks to Rust’s ownership model, the computation is able to implement drophandles to detect the future being dropped. This allows it to perform anynecessary cleanup work.

Lightweight

Tokio scales well without adding overhead to the application, allowing it tothrive in resource constrained environments.

No garbage collector

Because Tokio is built on Rust, the compiled executable includes minimallanguage run-time. The end product is similar to what C++ would produce. Thismeans, no garbage collector, no virtual machine, no JIT compilation, and nostack manipulation. Write your server applications without fear ofstop-the-world#Disadvantages) pauses.

It is possible to use Tokio without incurring any runtime allocations, making ita good fit for real-time use cases.

Modular

While Tokio provides a lot out of the box, it is all organized very modularly.Each component lives in a separate library. If needed, applications may opt topick and choose the needed components and avoid pulling in the rest.

Tokio leverages mio for the system event queue and futures for definingtasks. Tokio implements async syntax to improve readability of futures.Many libraries are implemented using Tokio, including hyper and actix.

Example

A basic TCP echo server with Tokio:

  1. extern crate tokio;
  2. use tokio::prelude::*;
  3. use tokio::io::copy;
  4. use tokio::net::TcpListener;
  5. fn main() {
  6. # }
  7. # fn hax() {
  8. // Bind the server's socket.
  9. let addr = "127.0.0.1:12345".parse().unwrap();
  10. let listener = TcpListener::bind(&addr)
  11. .expect("unable to bind TCP listener");
  12. // Pull out a stream of sockets for incoming connections
  13. let server = listener.incoming()
  14. .map_err(|e| eprintln!("accept failed = {:?}", e))
  15. .for_each(|sock| {
  16. // Split up the reading and writing parts of the
  17. // socket.
  18. let (reader, writer) = sock.split();
  19. // A future that echos the data and returns how
  20. // many bytes were copied...
  21. let bytes_copied = copy(reader, writer);
  22. // ... after which we'll print what happened.
  23. let handle_conn = bytes_copied.map(|amt| {
  24. println!("wrote {:} bytes", amt.0)
  25. }).map_err(|err| {
  26. eprintln!("IO error {:?}", err)
  27. });
  28. // Spawn the future as a concurrent task.
  29. tokio::spawn(handle_conn)
  30. });
  31. // Start the Tokio runtime
  32. tokio::run(server);
  33. }

More examples can be found here.

Next up: Hello World!