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

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