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:
https://github.com/mozilla/rust/blob/master/src/test/run-pass/rt-start-main-thread.rs
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.
Regards,
Brian
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev