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;
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>,
storage: LocalStorage(None),
unwinder: Unwinder::new(),
death: Death::new(),
- destroyed: false,
+ state: New,
name: None,
imp: None,
}
/// 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`
/// }).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
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
///
/// 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 {
/// 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.
//
// 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).
// 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
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;
/// 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)
}
/// 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)
/// 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);
}
/// 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);
}
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);
}
}
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();
}
}