use any::AnyOwnExt;
use borrow;
+use cast;
use cleanup;
+use clone::Clone;
use io::Writer;
-use libc::{c_char, size_t};
+use iter::{Iterator, Take};
use local_data;
+use logging::Logger;
use ops::Drop;
use option::{Option, Some, None};
use prelude::drop;
use rt::borrowck;
use rt::local::Local;
use rt::local_heap::LocalHeap;
-use rt::logging::StdErrLogger;
use rt::rtio::LocalIo;
use rt::unwind::Unwinder;
use send_str::SendStr;
use sync::arc::UnsafeArc;
-use sync::atomics::{AtomicUint, SeqCst, INIT_ATOMIC_UINT};
+use sync::atomics::{AtomicUint, SeqCst};
use task::{TaskResult, TaskOpts};
use unstable::finally::Finally;
-use unstable::mutex::{Mutex, MUTEX_INIT};
-
-#[cfg(stage0)] pub use rt::unwind::begin_unwind;
-
-static mut TASK_COUNT: AtomicUint = INIT_ATOMIC_UINT;
-static mut TASK_LOCK: Mutex = MUTEX_INIT;
// The Task struct represents all state associated with a rust
// task. There are at this point two primary "subtypes" of task,
// Dynamic borrowck debugging info
borrow_list: Option<~[BorrowRecord]>,
- logger: Option<StdErrLogger>,
- stdout_handle: Option<~Writer>,
+ logger: Option<~Logger>,
+ stdout: Option<~Writer>,
+ stderr: Option<~Writer>,
priv imp: Option<~Runtime>,
}
name: None,
borrow_list: None,
logger: None,
- stdout_handle: None,
+ stdout: None,
+ stderr: None,
imp: None,
}
}
*cast::transmute::<&~Task, &*mut Task>(&self)
};
Local::put(self);
- unsafe { TASK_COUNT.fetch_add(1, SeqCst); }
// The only try/catch block in the world. Attempt to run the task's
// client-specified code and catch any failures.
// Run the task main function, then do some cleanup.
f.finally(|| {
- fn flush(w: Option<~Writer>) {
- match w {
- Some(mut w) => { w.flush(); }
- None => {}
- }
+ fn close_outputs() {
+ let mut task = Local::borrow(None::<Task>);
+ let logger = task.get().logger.take();
+ let stderr = task.get().stderr.take();
+ let stdout = task.get().stdout.take();
+ drop(task);
+ drop(logger); // loggers are responsible for flushing
+ match stdout { Some(mut w) => w.flush(), None => {} }
+ match stderr { Some(mut w) => w.flush(), None => {} }
}
+ // First, flush/destroy the user stdout/logger because these
+ // destructors can run arbitrary code.
+ close_outputs();
+
// First, destroy task-local storage. This may run user dtors.
//
// FIXME #8302: Dear diary. I'm so tired and confused.
// annihilated invoke TLS. Sadly these two operations seemed to
// be intertwined, and miraculously work for now...
let mut task = Local::borrow(None::<Task>);
- let storage = task.get().storage.take();
+ let storage_map = {
+ let task = task.get();
+ let LocalStorage(ref mut optmap) = task.storage;
+ optmap.take()
+ };
drop(task);
- drop(storage);
+ drop(storage_map);
// Destroy remaining boxes. Also may run user dtors.
unsafe { cleanup::annihilate(); }
- // Finally flush and destroy any output handles which the task
- // owns. There are no boxes here, and no user destructors should
- // run after this any more.
- let mut task = Local::borrow(None::<Task>);
- let stdout = task.get().stdout_handle.take();
- let logger = task.get().logger.take();
- drop(task);
-
- flush(stdout);
- drop(logger);
+ // Finally, just in case user dtors printed/logged during TLS
+ // cleanup and annihilation, re-destroy stdout and the logger.
+ // Note that these will have been initialized with a
+ // runtime-provided type which we have control over what the
+ // destructor does.
+ close_outputs();
})
};
// Cleanup the dynamic borrowck debugging info
borrowck::clear_task_borrow_list();
- // TODO: dox
+ // Here we must unsafely borrow the task in order to not remove it from
+ // TLS. When collecting failure, we may attempt to send on a channel (or
+ // just run aribitrary code), so we must be sure to still have a local
+ // task in TLS.
unsafe {
let me: *mut Task = Local::unsafe_borrow();
(*me).death.collect_failure((*me).unwinder.result());
- if TASK_COUNT.fetch_sub(1, SeqCst) == 1 {
- TASK_LOCK.lock();
- TASK_LOCK.signal();
- TASK_LOCK.unlock();
- }
}
let mut me: ~Task = Local::take();
me.destroyed = true;
pub fn local_io<'a>(&'a mut self) -> Option<LocalIo<'a>> {
self.imp.get_mut_ref().local_io()
}
+
+ /// Returns the stack bounds for this task in (lo, hi) format. The stack
+ /// bounds may not be known for all tasks, so the return value may be
+ /// `None`.
+ pub fn stack_bounds(&self) -> (uint, uint) {
+ self.imp.get_ref().stack_bounds()
+ }
}
impl Drop for Task {
}
}
-pub unsafe fn wait_for_completion() {
- TASK_LOCK.lock();
- while TASK_COUNT.load(SeqCst) > 0 {
- TASK_LOCK.wait();
- }
- TASK_LOCK.unlock();
-}
-
#[cfg(test)]
mod test {
use super::*;
#[test]
fn heap_cycles() {
+ use cell::RefCell;
use option::{Option, Some, None};
struct List {
- next: Option<@mut List>,
+ next: Option<@RefCell<List>>,
}
- let a = @mut List { next: None };
- let b = @mut List { next: Some(a) };
+ let a = @RefCell::new(List { next: None });
+ let b = @RefCell::new(List { next: Some(a) });
- a.next = Some(b);
+ {
+ let mut a = a.borrow_mut();
+ a.get().next = Some(b);
+ }
}
#[test]
#[should_fail]
- fn test_begin_unwind() { begin_unwind("cause", file!(), line!()) }
+ fn test_begin_unwind() {
+ use rt::unwind::begin_unwind;
+ begin_unwind("cause", file!(), line!())
+ }
// Task blocking tests