Description
There is currently a gentleperson's agreement about the use of the higher-order context switchers in the new runtime. Usually, they are to be used like this:
let mut environment = ...;
do sched.deschedule_running_task_and_then |sched, task| {
... use environment ...
sched.schedule_blocked_task(task);
}
... use environment ...
However, because the closure ("cleanup job") runs in scheduler context, on SMP the task can resume on a different core before the closure finishes running. Hence, if you did:
let mut environment = ...;
do sched.deschedule_running_task_and_then |sched, task| {
sched.schedule_blocked_task(task);
... use environment ... // A
}
... use environment ... // B
...then A and B will race and the world will explode.
There are two possible solutions. One is to leave this issue open forever, and audit all uses of the context-switchers before doing each major release. The second is to use the type system (which once again proves expressive enough to deal with this sort of thing). It would look something like this:
impl Scheduler {
// Passes the environment explicitly as a noncopyable Env token.
fn deschedule_running_task_and_then<E>(~self, E, extern fn(&mut Scheduler, BlockedTask, Env<E>))
// Wakes a blocked task, also proving that the caller won't access its environment anymore.
fn schedule_blocked_task<E>(&mut self, BlockedTask, Env<E>)
}
impl <E> Env<E> {
// Can only access environment as long as opaque Env permission token is held.
fn get<'a>(&'a mut self) -> &'a mut E
}
There is just one problem with this, which is that it doesn't save you if you're already manipulating unsafe pointers as part of the environment (such as we do in comm::PortOne
). Those use sites will have to be audited nonetheless.