diff options
| author | toddaaro <github@opprobrio.us> | 2013-07-19 14:25:05 -0700 |
|---|---|---|
| committer | toddaaro <github@opprobrio.us> | 2013-08-01 15:14:00 -0700 |
| commit | f7eed223873a4280c9abea937e60ef1aaedf0162 (patch) | |
| tree | d1dea92a84b12741e7796cf91938edbae906fd8e /src/libstd/rt/task.rs | |
| parent | 82b24559e6aa0914f8a49e0a9dbfb3cf35372515 (diff) | |
| download | rust-f7eed223873a4280c9abea937e60ef1aaedf0162.tar.gz rust-f7eed223873a4280c9abea937e60ef1aaedf0162.zip | |
A major refactoring that changes the way the runtime uses TLS. In the
old design the TLS held the scheduler struct, and the scheduler struct held the active task. This posed all sorts of weird problems due to how we wanted to use the contents of TLS. The cleaner approach is to leave the active task in TLS and have the task hold the scheduler. To make this work out the scheduler has to run inside a regular task, and then once that is the case the context switching code is massively simplified, as instead of three possible paths there is only one. The logical flow is also easier to follow, as the scheduler struct acts somewhat like a "token" indicating what is active. These changes also necessitated changing a large number of runtime tests, and rewriting most of the runtime testing helpers. Polish level is "low", as I will very soon start on more scheduler changes that will require wiping the polish off. That being said there should be sufficient comments around anything complex to make this entirely respectable as a standalone commit.
Diffstat (limited to 'src/libstd/rt/task.rs')
| -rw-r--r-- | src/libstd/rt/task.rs | 215 |
1 files changed, 161 insertions, 54 deletions
diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index c1b799796d1..bc603bede97 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -30,21 +30,34 @@ use rt::context::Context; use task::spawn::Taskgroup; use cell::Cell; +// The Task struct represents all state associated with a rust +// task. There are at this point two primary "subtypes" of task, +// however instead of using a subtype we just have a "task_type" field +// in the struct. This contains a pointer to another struct that holds +// the type-specific state. + pub struct Task { heap: LocalHeap, gc: GarbageCollector, storage: LocalStorage, logger: StdErrLogger, unwinder: Unwinder, - home: Option<SchedHome>, taskgroup: Option<Taskgroup>, death: Death, destroyed: bool, coroutine: Option<~Coroutine>, // FIXME(#6874/#7599) use StringRef to save on allocations name: Option<~str>, + sched: Option<~Scheduler>, + task_type: TaskType +} + +pub enum TaskType { + GreenTask(Option<~SchedHome>), + SchedTask } +/// A coroutine is nothing more than a (register context, stack) pair. pub struct Coroutine { /// The segment of stack on which the task is currently running or /// if the task is blocked, on which the task will resume @@ -54,6 +67,7 @@ pub struct Coroutine { saved_context: Context } +/// Some tasks have a deciated home scheduler that they must run on. pub enum SchedHome { AnySched, Sched(SchedHandle) @@ -68,6 +82,58 @@ pub struct Unwinder { impl Task { + // A helper to build a new task using the dynamically found + // scheduler and task. Only works in GreenTask context. + pub fn build_homed_child(f: ~fn(), home: SchedHome) -> ~Task { + let f = Cell::new(f); + let home = Cell::new(home); + do Local::borrow::<Task, ~Task> |running_task| { + let mut sched = running_task.sched.take_unwrap(); + let new_task = ~running_task.new_child_homed(&mut sched.stack_pool, + home.take(), + f.take()); + running_task.sched = Some(sched); + new_task + } + } + + pub fn build_child(f: ~fn()) -> ~Task { + Task::build_homed_child(f, AnySched) + } + + pub fn build_homed_root(f: ~fn(), home: SchedHome) -> ~Task { + let f = Cell::new(f); + let home = Cell::new(home); + do Local::borrow::<Task, ~Task> |running_task| { + let mut sched = running_task.sched.take_unwrap(); + let new_task = ~Task::new_root_homed(&mut sched.stack_pool, + home.take(), + f.take()); + running_task.sched = Some(sched); + new_task + } + } + + pub fn build_root(f: ~fn()) -> ~Task { + Task::build_homed_root(f, AnySched) + } + + pub fn new_sched_task() -> Task { + Task { + heap: LocalHeap::new(), + gc: GarbageCollector, + storage: LocalStorage(ptr::null(), None), + logger: StdErrLogger, + unwinder: Unwinder { unwinding: false }, + taskgroup: None, + death: Death::new(), + destroyed: false, + coroutine: Some(~Coroutine::empty()), + sched: None, + task_type: SchedTask + } + } + pub fn new_root(stack_pool: &mut StackPool, start: ~fn()) -> Task { Task::new_root_homed(stack_pool, AnySched, start) @@ -88,12 +154,13 @@ impl Task { storage: LocalStorage(ptr::null(), None), logger: StdErrLogger, unwinder: Unwinder { unwinding: false }, - home: Some(home), taskgroup: None, death: Death::new(), destroyed: false, coroutine: Some(~Coroutine::new(stack_pool, start)), name: None, + sched: None, + task_type: GreenTask(Some(~home)) } } @@ -106,7 +173,6 @@ impl Task { gc: GarbageCollector, storage: LocalStorage(ptr::null(), None), logger: StdErrLogger, - home: Some(home), unwinder: Unwinder { unwinding: false }, taskgroup: None, // FIXME(#7544) make watching optional @@ -114,19 +180,35 @@ impl Task { destroyed: false, coroutine: Some(~Coroutine::new(stack_pool, start)), name: None, + sched: None, + task_type: GreenTask(Some(~home)) } } pub fn give_home(&mut self, new_home: SchedHome) { - self.home = Some(new_home); + match self.task_type { + GreenTask(ref mut home) => { + *home = Some(~new_home); + } + SchedTask => { + rtabort!("type error: used SchedTask as GreenTask"); + } + } } - pub fn run(&mut self, f: &fn()) { - // This is just an assertion that `run` was called unsafely - // and this instance of Task is still accessible. - do Local::borrow::<Task, ()> |task| { - assert!(borrow::ref_eq(task, self)); + pub fn take_unwrap_home(&mut self) -> SchedHome { + match self.task_type { + GreenTask(ref mut home) => { + let out = home.take_unwrap(); + return *out; + } + SchedTask => { + rtabort!("type error: used SchedTask as GreenTask"); + } } + } + + pub fn run(&mut self, f: &fn()) { self.unwinder.try(f); { let _ = self.taskgroup.take(); } @@ -141,6 +223,8 @@ impl Task { /// thread-local-storage. fn destroy(&mut self) { + rtdebug!("DESTROYING TASK: %u", borrow::to_uint(self)); + do Local::borrow::<Task, ()> |task| { assert!(borrow::ref_eq(task, self)); } @@ -158,63 +242,68 @@ impl Task { self.destroyed = true; } - /// Check if *task* is currently home. - pub fn is_home(&self) -> bool { - do Local::borrow::<Scheduler,bool> |sched| { - match self.home { - Some(AnySched) => { false } - Some(Sched(SchedHandle { sched_id: ref id, _ })) => { - *id == sched.sched_id() - } - None => { rtabort!("task home of None") } - } - } - } + // New utility functions for homes. pub fn is_home_no_tls(&self, sched: &~Scheduler) -> bool { - match self.home { - Some(AnySched) => { false } - Some(Sched(SchedHandle { sched_id: ref id, _ })) => { + match self.task_type { + GreenTask(Some(~AnySched)) => { false } + GreenTask(Some(~Sched(SchedHandle { sched_id: ref id, _}))) => { *id == sched.sched_id() } - None => {rtabort!("task home of None") } - } - } - - pub fn is_home_using_id(sched_id: uint) -> bool { - do Local::borrow::<Task,bool> |task| { - match task.home { - Some(Sched(SchedHandle { sched_id: ref id, _ })) => { - *id == sched_id - } - Some(AnySched) => { false } - None => { rtabort!("task home of None") } + GreenTask(None) => { + rtabort!("task without home"); + } + SchedTask => { + // Awe yea + rtabort!("type error: expected: GreenTask, found: SchedTask"); } } } - /// Check if this *task* has a home. pub fn homed(&self) -> bool { - match self.home { - Some(AnySched) => { false } - Some(Sched(_)) => { true } - None => { - rtabort!("task home of None") + match self.task_type { + GreenTask(Some(~AnySched)) => { false } + GreenTask(Some(~Sched(SchedHandle { _ }))) => { true } + GreenTask(None) => { + rtabort!("task without home"); + } + SchedTask => { + rtabort!("type error: expected: GreenTask, found: SchedTask"); } } } - /// On a special scheduler? - pub fn on_special() -> bool { - do Local::borrow::<Scheduler,bool> |sched| { - !sched.run_anything + // Grab both the scheduler and the task from TLS and check if the + // task is executing on an appropriate scheduler. + pub fn on_appropriate_sched() -> bool { + do Local::borrow::<Task,bool> |task| { + let sched_id = task.sched.get_ref().sched_id(); + let sched_run_anything = task.sched.get_ref().run_anything; + match task.task_type { + GreenTask(Some(~AnySched)) => { + rtdebug!("anysched task in sched check ****"); + sched_run_anything + } + GreenTask(Some(~Sched(SchedHandle { sched_id: ref id, _ }))) => { + rtdebug!("homed task in sched check ****"); + *id == sched_id + } + GreenTask(None) => { + rtabort!("task without home"); + } + SchedTask => { + rtabort!("type error: expected: GreenTask, found: SchedTask"); + } + } } } - } impl Drop for Task { - fn drop(&self) { assert!(self.destroyed) } + fn drop(&self) { + rtdebug!("called drop for a task"); + assert!(self.destroyed) + } } // Coroutines represent nothing more than a context and a stack @@ -234,19 +323,33 @@ impl Coroutine { } } + pub fn empty() -> Coroutine { + Coroutine { + current_stack_segment: StackSegment::new(0), + saved_context: Context::empty() + } + } + fn build_start_wrapper(start: ~fn()) -> ~fn() { let start_cell = Cell::new(start); let wrapper: ~fn() = || { // First code after swap to this new context. Run our // cleanup job. unsafe { - let sched = Local::unsafe_borrow::<Scheduler>(); - (*sched).run_cleanup_job(); - let sched = Local::unsafe_borrow::<Scheduler>(); - let task = (*sched).current_task.get_mut_ref(); + // Again - might work while safe, or it might not. + do Local::borrow::<Scheduler,()> |sched| { + (sched).run_cleanup_job(); + } + + // To call the run method on a task we need a direct + // reference to it. The task is in TLS, so we can + // simply unsafe_borrow it to get this reference. We + // need to still have the task in TLS though, so we + // need to unsafe_borrow. + let task = Local::unsafe_borrow::<Task>(); - do task.run { + do (*task).run { // N.B. Removing `start` from the start wrapper // closure by emptying a cell is critical for // correctness. The ~Task pointer, and in turn the @@ -262,8 +365,11 @@ impl Coroutine { }; } + // We remove the sched from the Task in TLS right now. let sched = Local::take::<Scheduler>(); - sched.terminate_current_task(); + // ... allowing us to give it away when performing a + // scheduling operation. + sched.terminate_current_task() }; return wrapper; } @@ -465,3 +571,4 @@ mod test { } } } + |
