Announcing Tokio 0.2 and a Roadmap to 1.0

November 26, 2019

We are very excited to announce Tokio 0.2. This is a ground up rework of Tokio based on async / await and experience gained over the past three years.

Add the following to Cargo.toml:

tokio = { version = "0.2", features = ["full"] }

This is a major update, and as such there are changes to almost all parts of the library. To highlight a few:

  • Based on async / await for superior ergonomics.

  • A brand new, much faster, scheduler.

  • Focus on making the Tokio dependency as lightweight as possible.

async / await

If you have used Tokio to date, then you are familiar with the “old” way of writing asynchronous Rust… it was not fun. Most ended up writing state machines by hand. This was cumbersome and error prone.

As of Rust 1.39, the async / await is available on the stable channel and Tokio 0.2 takes full advantage of it. Whenever possible, Tokio provides an async fn based API, usually modeled after std.

For example, accepting sockets from a TcpListener is done with the async accept function:

let (mut socket, _peer_addr) = listener.accept().await?;

I/O operations are provided as async functions:

let n = socket.read(&mut buf).await?;

let n = socket.write(&buf).await?;

The same applies to the rest of the Tokio API surface.

Starting the Tokio runtime

Setting the entry point of a Tokio application can now be done with a proc macro:

#[tokio::main]
async fn main() {
    println!("Hello from Tokio!");
}

This starts up the Tokio runtime and all necessary infrastructure to power the application.

A new scheduler

The new Tokio comes with a scheduler that was built from the ground up to take advantage of the new asynchronous task system. It is based on the experience gained from Tokio 0.1 as well as all the hard work put into other ecosystems like Go, Erlang, Java, and others.

There are still improvements to make, but initial testing using Hyper shows a 30+% speed up in macro level benchmarks between the old scheduler and the new scheduler.

You can read more about it here.

Since it landed on master, some Tokio users have been experimenting with the new scheduler and have seen some very impressive real world improvements in their applications. Hopefully they will blog about them soon!

A lightweight Tokio dependency

One of the biggest complaints users have about Tokio to date is the weight of the depenedency. Adding a dependency on Tokio has historically added a large number of transitive dependencies and added time spent compiling.

For example, a “hello world” Tokio 0.1 application on my laptop would pull in 43 crates and take 50 seconds to compile (not counting time spent downloading the dependencies).

Tokio addresses this issue on two fronts. First, most Tokio components have been collapsed into a single crate: tokio and transitive dependencies were pruned aggressively. Second, Tokio components have been made opt-in using feature flags instead of always on. Simply pulling in the tokio dependency will only get you a few traits.

To get started with Tokio 0.2, you will need to request feature flags. A full feature flag includes everything and is an easy way to get started:

tokio = { version = "0.2", features = ["full"] }

On my laptop, this reduces the total number of crates to 23. Compile time only drops to 40 seconds.

Real benefits start happening when the user of Tokio starts only requesting the components that are needed to run the application. To run the TCP echo server example, the io-util, rt-threaded, and tcp feature flags are needed. Now, Tokio pulls in 13 crates and compiling takes 13 seconds.

There is more work to be done on pruning dependencies. Mio 0.7, which prunes further dependencies, is not included with Tokio 0.2. Tokio 0.3 will include Mio 0.7.

Thanks

Of course, none of this would be possible without our amazing team and contributors who worked on this release. Many individuals submitted PRs ranging from doc fixes to migrating entire crates to std::future::Future. Some names I want to call out, in no particular order:

A pre-emptive thanks to all those working on Mio 0.7, which sadly didn’t make the cut for this release, but will happen soon!

And a big thanks to Buoyant, the makers of Linkerd (the proxy is written in Rust), who sponsored most of the work.

A Roadmap to 1.0

All that being said, we are shipping version 0.2. The question of “why not 1.0?” has come up a few times. After all, Tokio 0.1 has been stable for three years. The short answer: because it isn’t time. There is nobody who would rather ship a Tokio 1.0 than us. It also isn’t something to rush.

After all, async / await only landed in the stable Rust channel weeks ago. There has been no significant production validation yet, except maybe fuchsia and that seems like a fairly specialized use case. This release of Tokio includes significant new code and new strategies with feature flags. Also, there are still big open questions, such as the proposed changes to AsyncRead and AsyncWrite.

Tokio 1.0 will be released as soon as the APIs are proven to handle real-world production cases.

Tokio 1.0 in Q3 2020 with LTS support

The Tokio 1.0 release will be no later than Q3 2020. It will also come with “long-term support” guarantees:

  • A minimum of 5 years of maintenance.
  • A minimum of 3 years before a hypothetical 2.0 release.

When Tokio 1.0 is released in Q3 2020, on-going support, security fixes, and critical bug fixes are guaranteed until at least Q3 2025. Tokio 2.0 will not be released until at least Q3 2023 (though, ideally there will never been a Tokio 2.0 release).

How to get there

While Tokio 0.1 probably should have been a 1.0, Tokio 0.2 will be a true 0.2 release. There will breaking change releases every 2 ~ 3 months until 1.0. These changes will be much smaller than going from 0.1 -> 0.2. It is expected that the 1.0 release will look a lot like 0.2.

What is expected to change

The biggest change will be the AsyncRead and AsyncWrite traits. Based on experience gained over the past 3 years, there are a couple of issues to address:

  • Be able to safely use uninitialized memory as a read buffer.
  • Practical read vectored and write vectored APIs.

There are a few strategies to solve these problems. These strategies need to be investigated and the solution validated. You can see this comment for a detailed statement of the problem.

The other major change, which has been in the works for a while, is updating Mio. Mio 0.6 was first released almost 4 years ago and has not had a breaking change since. Mio 0.7 has been in the works for a while. It includes a full rewrite of the windows support as well as a refined API. More will be written about this shortly.

Finally, now that the API is starting to stabilize, effort will be put into documentation. Tokio 0.2 is being released before updating the website and many of the old content will no longer be relevant. In the coming weeks, expect to see updates there.

So, we have our work cut out for us. We hope you enjoy this 0.2 release and are looking forward to your feedback and help.