-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Scheduler rewrite with I/O event loop #4419
Comments
This will probably do work stealing from the beginning: #3095 |
Some preliminary performance numbers https://mail.mozilla.org/pipermail/rust-dev/2013-May/004127.html |
r? This is all of my scheduler work on #4419 from the last 3 weeks or so. I've had a few failed pull requests so far but I think the problems are ironed out. * TCP * The beginnings of runtime embedding APIs * Porting various corners of core to be compatible with both schedulers * libuv timer bindings * Further refinement of I/O error handling, including a new, incomplete, `read_error` condition * Incomplete refactoring to make tasks work without coroutines and user-space scheduling * Implementations of Reader/Writer extension methods * Implementations of the most important part of core::comm I'm particularly happy with how easy the [comm types on top of the scheduler](https://github.com/brson/rust/blob/io-upstream/src/libcore/rt/comm.rs). Note that these implementations do not use pipes. If anything here needs careful review though it's this code. This branch passes 95% of the run-pass tests (with `TESTARGS=--newrt`) In the next week I'll probably spend some time adding preliminary multithreading and seeing how close we are to removing the old runtime.
r? Mostly refactoring, and adding some of the remaining types described in #4419. The [`Local`](https://github.com/brson/rust/blob/3b4ff41511cfaa5e311b03d16b47bf40c117fa2f/src/libcore/rt/local.rs#L17) trait collects some common, often unsafe patterns around task-local and thread-local values. Making all these types safe is largely the aim of #6210.
Haskell I/O event manager: http://research.google.com/pubs/author39484.html |
As with #3095, the major work items of this issue have all been completed now that the scheduler has seen some weathered use. The scheduler has been pretty stable for some time now, and the remaining items on this TODO list are all becoming satellite "this would be nice to have" issues, but none of them really block 1.0. As always, if others disagree, I'm more than willing to reopen. Closing for now. |
seems like |
In order to better integrate async I/O, we are going to make it responsible for driving the scheduler event loop. I think we should take this opportunity to rewrite the scheduler in Rust, which takes us a long way toward rewriting the entire runtime in Rust and enabling freestanding Rust.
Links
rt
module: https://github.com/brson/rust/blob/io/src/libcore/rt/mod.rsio
module: https://github.com/brson/rust/blob/io/src/libcore/rt/io/mod.rssched
module: https://github.com/brson/rust/blob/io/src/libcore/rt/sched.rsOption
: http://www.reddit.com/r/rust/comments/1fod5c/io_and_condition_handlers/Current work items
Scheduler
I/O related
Runtime porting
unkillable
etc.Constraints
Concepts
Task
- The current state of a task. An owned type that is passed between schedulers and other runtime objects. It mostly provides task-local runtime services that are exposed through the language and core API's, e.g. local heap, logging , GC, unwinding, TLS. Unlike the previous scheduler, Task's do not need to be scheduled as coroutines.Scheduler
- Schedulers provide the mechanism for scheduling and descheduling Tasks within a single thread. The runtime may consist of multiple threads, each one with a Scheduler. A Scheduler instance is (almost) always accessible via thread-local storage.Coroutine
- The stored CPU context and the stack of a single task, executed by Schedulers. Not sure this is the best name yet. Would also like to use this for task-local coroutines that have no task state of their own.SchedHandle
- A handle for communicating to remote Schedulers. For task forwarding, probably also to wake up sleeping schedulers when new work becomes available.Scheduler multithreading
Most of the thread synchronization in Rust should be performed in Rust tasks, using pipes. The Scheduler itself should do as little as it can.
Tasks are owned types. When they are running their
~Task
pointer is stored in thread local storage. When they are not running they are owned by whatever object they are blocked on. Scheduling a task means acquiring ownership of a~Task
, then passing ownership to aScheduler
. Blocking a task means taking ownership of a~Task
from thread-local storage, stuffing it somewhere else and context switching.There are several places where schedulers need to synchronize with the outside world.
WorkQueue
- work stealing dequeMessageQueue
- message passing between schedulersWorkList
- The shared list ofWorkQueue
s of active schedulers andSchedHandle
s of sleeping schedulers.SleeperList
- The shared collection (currently a stack) of sleeping schedulers.The first is the
WorkQueue
, which is a work-stealing deque. Tasks can be pushed onto or popped from the front of the queue by a single thread. Tasks can be popped from the back of the queue (stolen) by an arbitrary number of threads. The WorkQueue is the primary mechanism for distributing tasks across threads. Importantly, under the work stealing strategy, most of the burden of distributing tasks to threads is put on those threads that otherwise are not working. The local scheduler just drops work into the (lock-free) queue and goes about its business.The second is the
MessageQueue
through which tasks are forwarded from other schedulers. Tasks can be popped from the front of the queue by a single thread (the local Scheduler). Tasks can be pushed onto the back of the queue by an arbitrary number of threads. Sends to the MessageQueue are performed with the SchedHandle and are accompanied by a signal to wake up the Scheduler if it is asleep.The MessageQueue is higher priority than the WorkQueue, as the former contains tasks that must specifically be acted on by the associated Scheduler, while the later contains work that can be stolen by other threads. An example of where messages should probably be checked is before spawning - some other thread can create the task, but no other thread can handle the message.
The SchedHandle also serves as a reference count keeping schedulers running. Schedulers exit when there are no remaining I/O events, no available tasks to run, and no outstanding SchedHandles.
Work stealing
See #3095 (comment)
TODO
How do sleeping schedulers become aware that there is new work to be stolen? This needs to impose minimum synchronization on the non-sleeping schedulers. Papers seem to imply busy-waiting.
Risks
select
-like behavior.Scheduler policy and operations
TODO
Alternatives
The text was updated successfully, but these errors were encountered: