]> git.lizzy.rs Git - rust.git/blobdiff - src/librustrt/task.rs
auto merge of #15999 : Kimundi/rust/fix_folder, r=nikomatsakis
[rust.git] / src / librustrt / task.rs
index 891d0d5a8e3592053b8c4ff14c08079a71b6abbb..e3d9b7d136ec2e05b6b16816e89dbb29d35418d9 100644 (file)
@@ -16,7 +16,7 @@
 use core::prelude::*;
 
 use alloc::arc::Arc;
-use alloc::owned::{AnyOwnExt, Box};
+use alloc::boxed::{BoxAny, Box};
 use core::any::Any;
 use core::atomics::{AtomicUint, SeqCst};
 use core::iter::Take;
@@ -100,12 +100,21 @@ pub struct Task {
     pub storage: LocalStorage,
     pub unwinder: Unwinder,
     pub death: Death,
-    pub destroyed: bool,
     pub name: Option<SendStr>,
 
+    state: TaskState,
     imp: Option<Box<Runtime + Send>>,
 }
 
+// Once a task has entered the `Armed` state it must be destroyed via `drop`,
+// and no other method. This state is used to track this transition.
+#[deriving(PartialEq)]
+enum TaskState {
+    New,
+    Armed,
+    Destroyed,
+}
+
 pub struct TaskOpts {
     /// Invoke this procedure with the result of the task when it finishes.
     pub on_exit: Option<proc(Result): Send>,
@@ -159,7 +168,7 @@ pub fn new() -> Task {
             storage: LocalStorage(None),
             unwinder: Unwinder::new(),
             death: Death::new(),
-            destroyed: false,
+            state: New,
             name: None,
             imp: None,
         }
@@ -170,7 +179,7 @@ pub fn new() -> Task {
     /// This function can be used as an emulated "try/catch" to interoperate
     /// with the rust runtime at the outermost boundary. It is not possible to
     /// use this function in a nested fashion (a try/catch inside of another
-    /// try/catch). Invoking this funciton is quite cheap.
+    /// try/catch). Invoking this function is quite cheap.
     ///
     /// If the closure `f` succeeds, then the returned task can be used again
     /// for another invocation of `run`. If the closure `f` fails then `self`
@@ -203,7 +212,7 @@ pub fn new() -> Task {
     /// }).destroy();
     /// # }
     /// ```
-    pub fn run(~self, f: ||) -> Box<Task> {
+    pub fn run(mut self: Box<Task>, f: ||) -> Box<Task> {
         assert!(!self.is_destroyed(), "cannot re-use a destroyed task");
 
         // First, make sure that no one else is in TLS. This does not allow
@@ -212,6 +221,7 @@ pub fn run(~self, f: ||) -> Box<Task> {
         if Local::exists(None::<Task>) {
             fail!("cannot run a task recursively inside another");
         }
+        self.state = Armed;
         Local::put(self);
 
         // There are two primary reasons that general try/catch is unsafe. The
@@ -239,7 +249,7 @@ pub fn run(~self, f: ||) -> Box<Task> {
     ///
     /// The returned task cannot be used for running any more code, but it may
     /// be used to extract the runtime as necessary.
-    pub fn destroy(~self) -> Box<Task> {
+    pub fn destroy(self: Box<Task>) -> Box<Task> {
         if self.is_destroyed() {
             self
         } else {
@@ -252,7 +262,7 @@ pub fn destroy(~self) -> Box<Task> {
     /// This function consumes ownership of the task, deallocating it once it's
     /// done being processed. It is assumed that TLD and the local heap have
     /// already been destroyed and/or annihilated.
-    fn cleanup(~self, result: Result) -> Box<Task> {
+    fn cleanup(self: Box<Task>, result: Result) -> Box<Task> {
         // The first thing to do when cleaning up is to deallocate our local
         // resources, such as TLD and GC data.
         //
@@ -276,7 +286,7 @@ fn cleanup(~self, result: Result) -> Box<Task> {
         // 1. If TLD destruction fails, heap destruction will be attempted.
         //    There is a test for this at fail-during-tld-destroy.rs. Sadly the
         //    other way can't be tested due to point 2 above. Note that we must
-        //    immortalize the heap first becuase if any deallocations are
+        //    immortalize the heap first because if any deallocations are
         //    attempted while TLD is being dropped it will attempt to free the
         //    allocation from the wrong heap (because the current one has been
         //    replaced).
@@ -333,12 +343,12 @@ fn cleanup(~self, result: Result) -> Box<Task> {
         // Now that we're done, we remove the task from TLS and flag it for
         // destruction.
         let mut task: Box<Task> = Local::take();
-        task.destroyed = true;
+        task.state = Destroyed;
         return task;
     }
 
     /// Queries whether this can be destroyed or not.
-    pub fn is_destroyed(&self) -> bool { self.destroyed }
+    pub fn is_destroyed(&self) -> bool { self.state == Destroyed }
 
     /// Inserts a runtime object into this task, transferring ownership to the
     /// task. It is illegal to replace a previous runtime object in this task
@@ -376,7 +386,7 @@ pub fn maybe_take_runtime<T: 'static>(&mut self) -> Option<Box<T>> {
         unsafe {
             let imp = self.imp.take_unwrap();
             let vtable = mem::transmute::<_, &raw::TraitObject>(&imp).vtable;
-            match imp.wrap().move::<T>() {
+            match imp.wrap().downcast::<T>() {
                 Ok(t) => Some(t),
                 Err(t) => {
                     let data = mem::transmute::<_, raw::TraitObject>(t).data;
@@ -394,7 +404,9 @@ pub fn maybe_take_runtime<T: 'static>(&mut self) -> Option<Box<T>> {
 
     /// Spawns a sibling to this task. The newly spawned task is configured with
     /// the `opts` structure and will run `f` as the body of its code.
-    pub fn spawn_sibling(mut ~self, opts: TaskOpts, f: proc(): Send) {
+    pub fn spawn_sibling(mut self: Box<Task>,
+                         opts: TaskOpts,
+                         f: proc(): Send) {
         let ops = self.imp.take_unwrap();
         ops.spawn_sibling(self, opts, f)
     }
@@ -402,7 +414,8 @@ pub fn spawn_sibling(mut ~self, opts: TaskOpts, f: proc(): Send) {
     /// Deschedules the current task, invoking `f` `amt` times. It is not
     /// recommended to use this function directly, but rather communication
     /// primitives in `std::comm` should be used.
-    pub fn deschedule(mut ~self, amt: uint,
+    pub fn deschedule(mut self: Box<Task>,
+                      amt: uint,
                       f: |BlockedTask| -> ::core::result::Result<(), BlockedTask>) {
         let ops = self.imp.take_unwrap();
         ops.deschedule(amt, self, f)
@@ -411,7 +424,7 @@ pub fn deschedule(mut ~self, amt: uint,
     /// Wakes up a previously blocked task, optionally specifying whether the
     /// current task can accept a change in scheduling. This function can only
     /// be called on tasks that were previously blocked in `deschedule`.
-    pub fn reawaken(mut ~self) {
+    pub fn reawaken(mut self: Box<Task>) {
         let ops = self.imp.take_unwrap();
         ops.reawaken(self);
     }
@@ -419,14 +432,14 @@ pub fn reawaken(mut ~self) {
     /// Yields control of this task to another task. This function will
     /// eventually return, but possibly not immediately. This is used as an
     /// opportunity to allow other tasks a chance to run.
-    pub fn yield_now(mut ~self) {
+    pub fn yield_now(mut self: Box<Task>) {
         let ops = self.imp.take_unwrap();
         ops.yield_now(self);
     }
 
     /// Similar to `yield_now`, except that this function may immediately return
     /// without yielding (depending on what the runtime decides to do).
-    pub fn maybe_yield(mut ~self) {
+    pub fn maybe_yield(mut self: Box<Task>) {
         let ops = self.imp.take_unwrap();
         ops.maybe_yield(self);
     }
@@ -450,12 +463,20 @@ pub fn stack_bounds(&self) -> (uint, uint) {
     pub fn can_block(&self) -> bool {
         self.imp.get_ref().can_block()
     }
+
+    /// Consume this task, flagging it as a candidate for destruction.
+    ///
+    /// This function is required to be invoked to destroy a task. A task
+    /// destroyed through a normal drop will abort.
+    pub fn drop(mut self) {
+        self.state = Destroyed;
+    }
 }
 
 impl Drop for Task {
     fn drop(&mut self) {
         rtdebug!("called drop for a task: {}", self as *mut Task as uint);
-        rtassert!(self.destroyed);
+        rtassert!(self.state != Armed);
     }
 }
 
@@ -631,12 +652,17 @@ fn test_begin_unwind() {
         begin_unwind("cause", file!(), line!())
     }
 
+    #[test]
+    fn drop_new_task_ok() {
+        drop(Task::new());
+    }
+
     // Task blocking tests
 
     #[test]
     fn block_and_wake() {
         let task = box Task::new();
         let mut task = BlockedTask::block(task).wake().unwrap();
-        task.destroyed = true;
+        task.drop();
     }
 }