diff options
| author | toddaaro <github@opprobrio.us> | 2013-06-26 16:41:00 -0700 |
|---|---|---|
| committer | toddaaro <github@opprobrio.us> | 2013-07-01 16:14:56 -0700 |
| commit | 5cfad4b6de3a9ab749c975338c23fc2e20b0beec (patch) | |
| tree | 76891a17d34f8418a466429b7e121c3ebb4cd400 /src/libstd/rt/task.rs | |
| parent | e65d0cbabebc73f2c9733a7ed158576c9702e71e (diff) | |
| download | rust-5cfad4b6de3a9ab749c975338c23fc2e20b0beec.tar.gz rust-5cfad4b6de3a9ab749c975338c23fc2e20b0beec.zip | |
Refactored the runtime to view coroutines as a component of tasks, instead of tasks as a component of coroutines.
Diffstat (limited to 'src/libstd/rt/task.rs')
| -rw-r--r-- | src/libstd/rt/task.rs | 176 |
1 files changed, 166 insertions, 10 deletions
diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index 97c3b6a749b..333eaa53912 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -23,8 +23,11 @@ use option::{Option, Some, None}; use rt::local::Local; use rt::logging::StdErrLogger; use super::local_heap::LocalHeap; -use rt::sched::{SchedHome, AnySched}; +use rt::sched::{Scheduler, SchedHandle}; use rt::join_latch::JoinLatch; +use rt::stack::{StackSegment, StackPool}; +use rt::context::Context; +use cell::Cell; pub struct Task { heap: LocalHeap, @@ -35,7 +38,22 @@ pub struct Task { home: Option<SchedHome>, join_latch: Option<~JoinLatch>, on_exit: Option<~fn(bool)>, - destroyed: bool + destroyed: bool, + coroutine: Option<~Coroutine> +} + +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 + /// execution. + priv current_stack_segment: StackSegment, + /// Always valid if the task is alive and not running. + saved_context: Context +} + +pub enum SchedHome { + AnySched, + Sched(SchedHandle) } pub struct GarbageCollector; @@ -46,31 +64,50 @@ pub struct Unwinder { } impl Task { - pub fn new_root() -> Task { + + pub fn new_root(stack_pool: &mut StackPool, + start: ~fn()) -> Task { + Task::new_root_homed(stack_pool, AnySched, start) + } + + pub fn new_child(&mut self, + stack_pool: &mut StackPool, + start: ~fn()) -> Task { + self.new_child_homed(stack_pool, AnySched, start) + } + + pub fn new_root_homed(stack_pool: &mut StackPool, + home: SchedHome, + start: ~fn()) -> Task { Task { heap: LocalHeap::new(), gc: GarbageCollector, storage: LocalStorage(ptr::null(), None), logger: StdErrLogger, unwinder: Unwinder { unwinding: false }, - home: Some(AnySched), + home: Some(home), join_latch: Some(JoinLatch::new_root()), on_exit: None, - destroyed: false + destroyed: false, + coroutine: Some(~Coroutine::new(stack_pool, start)) } } - pub fn new_child(&mut self) -> Task { + pub fn new_child_homed(&mut self, + stack_pool: &mut StackPool, + home: SchedHome, + start: ~fn()) -> Task { Task { heap: LocalHeap::new(), gc: GarbageCollector, storage: LocalStorage(ptr::null(), None), logger: StdErrLogger, - home: Some(AnySched), + home: Some(home), unwinder: Unwinder { unwinding: false }, join_latch: Some(self.join_latch.get_mut_ref().new_child()), on_exit: None, - destroyed: false + destroyed: false, + coroutine: Some(~Coroutine::new(stack_pool, start)) } } @@ -108,11 +145,11 @@ impl Task { /// called unsafely, without removing Task from /// thread-local-storage. fn destroy(&mut self) { - // This is just an assertion that `destroy` was called unsafely - // and this instance of Task is still accessible. + do Local::borrow::<Task, ()> |task| { assert!(borrow::ref_eq(task, self)); } + match self.storage { LocalStorage(ptr, Some(ref dtor)) => { (*dtor)(ptr) @@ -125,12 +162,129 @@ 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") } + } + } + } + + pub fn is_home_no_tls(&self, sched: &~Scheduler) -> bool { + match self.home { + Some(AnySched) => { false } + 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") } + } + } + } + + /// 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") + } + } + } + + /// On a special scheduler? + pub fn on_special() -> bool { + do Local::borrow::<Scheduler,bool> |sched| { + sched.run_anything + } + } + } impl Drop for Task { fn finalize(&self) { assert!(self.destroyed) } } +// Coroutines represent nothing more than a context and a stack +// segment. + +impl Coroutine { + + pub fn new(stack_pool: &mut StackPool, start: ~fn()) -> Coroutine { + static MIN_STACK_SIZE: uint = 100000; // XXX: Too much stack + + let start = Coroutine::build_start_wrapper(start); + let mut stack = stack_pool.take_segment(MIN_STACK_SIZE); + let initial_context = Context::new(start, &mut stack); + Coroutine { + current_stack_segment: stack, + saved_context: initial_context + } + } + + 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(); + + 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 + // closure used to initialize the first call + // frame, is destroyed in the scheduler context, + // not task context. So any captured closures must + // not contain user-definable dtors that expect to + // be in task context. By moving `start` out of + // the closure, all the user code goes our of + // scope while the task is still running. + let start = start_cell.take(); + start(); + }; + } + + let sched = Local::take::<Scheduler>(); + sched.terminate_current_task(); + }; + return wrapper; + } + + /// Destroy coroutine and try to reuse stack segment. + pub fn recycle(~self, stack_pool: &mut StackPool) { + match self { + ~Coroutine { current_stack_segment, _ } => { + stack_pool.give_segment(current_stack_segment); + } + } + } + +} + + // Just a sanity check to make sure we are catching a Rust-thrown exception static UNWIND_TOKEN: uintptr_t = 839147; @@ -209,8 +363,10 @@ mod test { fn unwind() { do run_in_newsched_task() { let result = spawntask_try(||()); + rtdebug!("trying first assert"); assert!(result.is_ok()); let result = spawntask_try(|| fail!()); + rtdebug!("trying second assert"); assert!(result.is_err()); } } |
