[rust-dev] New Rust runtime turned on. What next?

Brian Anderson banderson at mozilla.com
Thu Aug 8 11:54:26 PDT 2013

Hey there.

Today we've turned on the new runtime, written in Rust, for all Rust 
programs. This completely replaces the old task scheduler written in 
C++. This is an important milestone in the ongoing [I/O rewrite], and 
there are a lot of feature and perf changes in the air, so now's a good 
time to update everybody on the state of things and what's going to be 
happening for the remainder of the year.

As a reminder, the reason we are doing this is to integrate an I/O event 
loop (libuv) directly into the task scheduler. We expect this strategy 
to give is us reasonably fast and scalable I/O with a traditional 
synchronous interface - a task that blocks on I/O will be descheduled, 
not blocking the progress of other tasks.

[I/O rewrite]: https://github.com/mozilla/rust/issues/4419

Instead of continuing to develop the task scheduler in C++ we decided to 
rewrite it in Rust. This should make it much easier to maintain, 
primarily because there's no FFI boundary to design around, so the 
interface between the standard library and the runtime is richer, more 
efficient and more idiomatic. It's also a good test of Rust's 
suitability for systemsy things.

The next weeks and months are undoubtedly going to be a turbulent period 
in Rust's evolution, so I ask for your patience and understanding as we 
work through the kinks. The rest of this email explains the current 
state of the scheduler and I/O, important regressions, API changes and 
future work.

## The current status

It's not even close to done yet, sadly, but I'm confident we've laid a 
solid groundwork for the future, and things will improve quickly from 
here. There aren't a lot of immediate benefits, though tasks are now 
migrated across threads by the scheduler, whereas in the old scheduler a 
single task was always run in the same thread.

At the moment performance is not what you might expect: the current 
scheduling algorithm is very naive, using a single work queue shared 
between scheduler threads; there are two shared data structures that are 
implemented with locks that will be heavily contested; there are a lot 
of wasted allocations and work. Basically, the focus for the last two 
months has been on transitioning to the new scheduler and not on 
performance. I advise against drawing any conclusion from benchmarks at 
this time. Aaron Todd is going to be pushing on performance for the next 
few weeks. He's already got a patch to convert to work stealing and his 
preliminary measurements are promising.

The I/O subsystem, in `rt::io` is still immature and will change a lot 
yet, but it does implement TCP and UDP on both IPv4 and IPv6. Chris 
Morgan has been working on an [HTTP server] built on `rt::io` so it does 
work somewhat. A word of caution though: I/O is not yet threadsafe so 
will fail if you don't set `RUST_THREADS=1`.

[HTTP server]: http://hg.chrismorgan.info/rusthttpserver

The new runtime does come with one very major regression: segmented 
stacks are not implemented. For now all tasks run on 2MB stacks, which 
can be overridden by setting `stack_size` in the `TaskOpts` structure on 
`TaskBuilder`. Overflowing the stack will cause havok. Reimplementing 
segmented stacks is a major effort, and I don't have an estimate for 
when it will be done (there are some higher priority work items yet).

Also, linked failure has a race condition that causes segfaults. That 
will be fixed soon.

Despite all these caveats I have a very strong sense that writing the 
runtime in Rust will go a long way to validate Rust in the domains it's 
aiming for: concurrent and systems programming. Even in the task 
scheduler, where there's quite a bit of unsafe code, the shared-nothing 
nature of unique types forces you to consciously break the type system 
to share memory, and that seems to go a long way to making parallel 
programming easier to reason about. The structure of this scheduler is 
very different from the old, reflecting the preferences of the Rust type 
system, and anecdotally at least it's been much easier to get working 
reliably. I hope to write more on this topic in the future.

## Feature changes

For the most part the new runtime is a drop-in replacement. The new task 
scheduler though is structured much differently than the former, so a 
number of the previously-available scheduler options in `std::task` have 
been removed. For the most part this should go unnoticed, since a lot of 
those options were unused or unimplemented. The `CurrentScheduler`, 
`DefaultScheduler`, `ExistingScheduler`, `ThreadPerTask` and 
`ManualThreads` scheduler modes are gone. The only remaining mode is 
`SingleThreaded`, which is often used for putting blocking tasks into 
their own threads.

The big disruptive change is that the `PlatformThread` spawn mode is 
gone. This was a way to tell a new task to run on the *actual main 
thread* of the process, which is required for many windowing systems 
(generally all tasks run on other threads, leaving the main thread 
empty). It always felt like a bit of a hack, and required setting up an 
extra task scheduler just in case the process wanted to use it. The new 
runtime does not provide this capability by default, but there is a way 
to opt into putting a scheduler on the main thread.

Because the new runtime is written in Rust, it is available for 
arbitrary Rust code to call, so applications that want to use the main 
thread are now required to set up the runtime themselves by overriding 
the application entry point using `#[start]` (a lower-level entry point 
than the default `main`) and then starting the runtime with 
`std::rt::start_on_main_thread`. See the following test case for an example:


As far as interfaces go, these `start` functions are a little ugly, 
passing around a bunch of unsafe pointers that shouldn't be touched, so 
this will probably be refined over time. For now though, this should get 
you by.

On the I/O side, most people by now have noticed that the `extra::net`, 
`extra::timer`, and related modules have disappeared. These mostly have 
replacements in `std::rt::io`, though as I mentioned before this code is 
not yet threadsafe. It will be soon though.

One other minor regression is that the debugging code for tracking 
dynamic borrows is currently broken. I suspect that this won't be missed 
since it appears to require a recompile of std anyway to turn on.

## What's next?

I told you last time I wanted to get this merge done before July, so I'm 
about a month behind the schedule I set at the beginning of summer.

In the immediate future I'll be working on a few things:

* Removing the C++ runtime
* Implementing a new HTTP client on top of `rt::io`, possibly using 
Chris Morgan's HTTP code, for use in Servo
* Porting Servo to the latest Rust
* Fixing any major problems that turn up as a result of this transition

Once these things are out of the way my next major task will be to 
replace uses of legacy `std::io` with `std::rt::io`, then once that's 
done to move `std::rt::io` to `std::io`. In the process I'll be 
implementing whatever features are missing to make that happen.
Additionally Eric Reed is going to be spending a number of weeks here 
going full speed to implement I/O features and making sure it's fast, 
and Jeff Olson and others are also starting to ramp up on the new I/O 
system. The focus for the rest of the year will be on making sure that 
I/O is cleanly-designed, stable and fast.

I'm not sure when we'll be able to restore segmented stacks. I imagine 
it will take about a month of effort, so it's difficult to know where to 
prioritize that.

This progress owes a lot to three of our interns: Aaron Todd, Eric Reed 
and Ben Blum, who have been doing most of the feature work on the 
runtime this summer.


More information about the Rust-dev mailing list