]> git.lizzy.rs Git - rust.git/blobdiff - src/libstd/rt/task.rs
auto merge of #11386 : rcatolino/rust/ice-10955, r=pcwalton
[rust.git] / src / libstd / rt / task.rs
index 765f0b427cd54632ca8b3f4ca8d4f41e1ecc57f4..6c94f23778931d358593752f85b6df7e13815f59 100644 (file)
 
 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;
-
-// These two statics are used as bookeeping to keep track of the rust runtime's
-// count of threads. In 1:1 contexts, this is used to know when to return from
-// the main function, and in M:N contexts this is used to know when to shut down
-// the pool of schedulers.
-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,
@@ -65,8 +56,9 @@ pub struct 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>,
 }
@@ -104,7 +96,8 @@ pub fn new() -> Task {
             name: None,
             borrow_list: None,
             logger: None,
-            stdout_handle: None,
+            stdout: None,
+            stderr: None,
             imp: None,
         }
     }
@@ -126,7 +119,6 @@ pub fn run(~self, f: ||) -> ~Task {
             *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.
@@ -134,13 +126,21 @@ pub fn run(~self, f: ||) -> ~Task {
 
             // 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.
@@ -161,23 +161,23 @@ fn flush(w: Option<~Writer>) {
                 // 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();
             })
         };
 
@@ -193,13 +193,6 @@ fn flush(w: Option<~Writer>) {
         unsafe {
             let me: *mut Task = Local::unsafe_borrow();
             (*me).death.collect_failure((*me).unwinder.result());
-
-            // see comments on these statics for why they're used
-            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;
@@ -293,19 +286,11 @@ pub fn local_io<'a>(&'a mut self) -> Option<LocalIo<'a>> {
         self.imp.get_mut_ref().local_io()
     }
 
-    /// The main function of all rust executables will by default use this
-    /// function. This function will *block* the OS thread (hence the `unsafe`)
-    /// waiting for all known tasks to complete. Once this function has
-    /// returned, it is guaranteed that no more user-defined code is still
-    /// running.
-    pub unsafe fn wait_for_other_tasks(&mut self) {
-        TASK_COUNT.fetch_sub(1, SeqCst); // don't count ourselves
-        TASK_LOCK.lock();
-        while TASK_COUNT.load(SeqCst) > 0 {
-            TASK_LOCK.wait();
-        }
-        TASK_LOCK.unlock();
-        TASK_COUNT.fetch_add(1, SeqCst); // add ourselves back in
+    /// 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) -> Option<(uint, uint)> {
+        self.imp.get_ref().stack_bounds()
     }
 }
 
@@ -474,21 +459,28 @@ fn comm_shared_chan() {
 
     #[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