about summary refs log tree commit diff
path: root/src/libstd/rt/task.rs
diff options
context:
space:
mode:
authortoddaaro <github@opprobrio.us>2013-06-26 16:41:00 -0700
committertoddaaro <github@opprobrio.us>2013-07-01 16:14:56 -0700
commit5cfad4b6de3a9ab749c975338c23fc2e20b0beec (patch)
tree76891a17d34f8418a466429b7e121c3ebb4cd400 /src/libstd/rt/task.rs
parente65d0cbabebc73f2c9733a7ed158576c9702e71e (diff)
downloadrust-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.rs176
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());
         }
     }