about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorBen Blum <bblum@andrew.cmu.edu>2013-07-30 19:20:59 -0400
committerBen Blum <bblum@andrew.cmu.edu>2013-07-31 14:37:22 -0400
commit2e6dc161b6efac1ce2709ab4e1c05c55c01e3abf (patch)
tree0d1425329ceab3ed0e4062355955de3060e685fb /src/libstd
parent389aba09523f805e3b493a5089c6397749b06b67 (diff)
downloadrust-2e6dc161b6efac1ce2709ab4e1c05c55c01e3abf.tar.gz
rust-2e6dc161b6efac1ce2709ab4e1c05c55c01e3abf.zip
Give tasks useful names. #2891
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/rt/mod.rs2
-rw-r--r--src/libstd/rt/task.rs10
-rw-r--r--src/libstd/sys.rs21
-rw-r--r--src/libstd/task/mod.rs59
-rw-r--r--src/libstd/task/spawn.rs2
5 files changed, 82 insertions, 12 deletions
diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs
index dc8669b9264..c38b929a6ce 100644
--- a/src/libstd/rt/mod.rs
+++ b/src/libstd/rt/mod.rs
@@ -316,12 +316,14 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
             // Just put an unpinned task onto one of the default schedulers.
             let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool, main);
             main_task.death.on_exit = Some(on_exit);
+            main_task.name = Some(~"main");
             scheds[0].enqueue_task(main_task);
         }
         Some(ref mut main_sched) => {
             let home = Sched(main_sched.make_handle());
             let mut main_task = ~Task::new_root_homed(&mut scheds[0].stack_pool, home, main);
             main_task.death.on_exit = Some(on_exit);
+            main_task.name = Some(~"main");
             main_sched.enqueue_task(main_task);
         }
     };
diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs
index 82d4f8fcc04..c1b799796d1 100644
--- a/src/libstd/rt/task.rs
+++ b/src/libstd/rt/task.rs
@@ -40,7 +40,9 @@ pub struct Task {
     taskgroup: Option<Taskgroup>,
     death: Death,
     destroyed: bool,
-    coroutine: Option<~Coroutine>
+    coroutine: Option<~Coroutine>,
+    // FIXME(#6874/#7599) use StringRef to save on allocations
+    name: Option<~str>,
 }
 
 pub struct Coroutine {
@@ -90,7 +92,8 @@ impl Task {
             taskgroup: None,
             death: Death::new(),
             destroyed: false,
-            coroutine: Some(~Coroutine::new(stack_pool, start))
+            coroutine: Some(~Coroutine::new(stack_pool, start)),
+            name: None,
         }
     }
 
@@ -109,7 +112,8 @@ impl Task {
             // FIXME(#7544) make watching optional
             death: self.death.new_child(),
             destroyed: false,
-            coroutine: Some(~Coroutine::new(stack_pool, start))
+            coroutine: Some(~Coroutine::new(stack_pool, start)),
+            name: None,
         }
     }
 
diff --git a/src/libstd/sys.rs b/src/libstd/sys.rs
index 5cf77d901db..4ca5d82265f 100644
--- a/src/libstd/sys.rs
+++ b/src/libstd/sys.rs
@@ -137,12 +137,13 @@ impl FailWithCause for &'static str {
 
 // FIXME #4427: Temporary until rt::rt_fail_ goes away
 pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! {
-    use cell::Cell;
     use either::Left;
+    use option::{Some, None};
     use rt::{context, OldTaskContext, TaskContext};
     use rt::task::Task;
     use rt::local::Local;
     use rt::logging::Logger;
+    use str::Str;
 
     let context = context();
     match context {
@@ -159,20 +160,26 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! {
                 let msg = str::raw::from_c_str(msg);
                 let file = str::raw::from_c_str(file);
 
-                let outmsg = fmt!("task failed at '%s', %s:%i",
-                                  msg, file, line as int);
-
                 // XXX: Logging doesn't work correctly in non-task context because it
                 // invokes the local heap
                 if context == TaskContext {
                     // XXX: Logging doesn't work here - the check to call the log
                     // function never passes - so calling the log function directly.
-                    let outmsg = Cell::new(outmsg);
                     do Local::borrow::<Task, ()> |task| {
-                        task.logger.log(Left(outmsg.take()));
+                        let msg = match task.name {
+                            Some(ref name) =>
+                                fmt!("task '%s' failed at '%s', %s:%i",
+                                     name.as_slice(), msg, file, line as int),
+                            None =>
+                                fmt!("task <unnamed> failed at '%s', %s:%i",
+                                     msg, file, line as int)
+                        };
+
+                        task.logger.log(Left(msg));
                     }
                 } else {
-                    rterrln!("%s", outmsg);
+                    rterrln!("failed in non-task context at '%s', %s:%i",
+                             msg, file, line as int);
                 }
 
                 gc::cleanup_stack_for_failure();
diff --git a/src/libstd/task/mod.rs b/src/libstd/task/mod.rs
index 6de0c78d00b..29a0dc9e337 100644
--- a/src/libstd/task/mod.rs
+++ b/src/libstd/task/mod.rs
@@ -120,6 +120,8 @@ pub struct SchedOpts {
  *
  * * notify_chan - Enable lifecycle notifications on the given channel
  *
+ * * name - A name for the task-to-be, for identification in failure messages.
+ *
  * * sched - Specify the configuration of a new scheduler to create the task
  *           in
  *
@@ -139,6 +141,7 @@ pub struct TaskOpts {
     watched: bool,
     indestructible: bool,
     notify_chan: Option<Chan<TaskResult>>,
+    name: Option<~str>,
     sched: SchedOpts
 }
 
@@ -185,6 +188,7 @@ impl TaskBuilder {
         self.consumed = true;
         let gen_body = self.gen_body.take();
         let notify_chan = self.opts.notify_chan.take();
+        let name = self.opts.name.take();
         TaskBuilder {
             opts: TaskOpts {
                 linked: self.opts.linked,
@@ -192,6 +196,7 @@ impl TaskBuilder {
                 watched: self.opts.watched,
                 indestructible: self.opts.indestructible,
                 notify_chan: notify_chan,
+                name: name,
                 sched: self.opts.sched
             },
             gen_body: gen_body,
@@ -199,9 +204,7 @@ impl TaskBuilder {
             consumed: false
         }
     }
-}
 
-impl TaskBuilder {
     /// Decouple the child task's failure from the parent's. If either fails,
     /// the other will not be killed.
     pub fn unlinked(&mut self) {
@@ -281,6 +284,12 @@ impl TaskBuilder {
         self.opts.notify_chan = Some(notify_pipe_ch);
     }
 
+    /// Name the task-to-be. Currently the name is used for identification
+    /// only in failure messages.
+    pub fn name(&mut self, name: ~str) {
+        self.opts.name = Some(name);
+    }
+
     /// Configure a custom scheduler mode for the task.
     pub fn sched_mode(&mut self, mode: SchedMode) {
         self.opts.sched.mode = mode;
@@ -333,6 +342,7 @@ impl TaskBuilder {
     pub fn spawn(&mut self, f: ~fn()) {
         let gen_body = self.gen_body.take();
         let notify_chan = self.opts.notify_chan.take();
+        let name = self.opts.name.take();
         let x = self.consume();
         let opts = TaskOpts {
             linked: x.opts.linked,
@@ -340,6 +350,7 @@ impl TaskBuilder {
             watched: x.opts.watched,
             indestructible: x.opts.indestructible,
             notify_chan: notify_chan,
+            name: name,
             sched: x.opts.sched
         };
         let f = match gen_body {
@@ -408,6 +419,7 @@ pub fn default_task_opts() -> TaskOpts {
         watched: true,
         indestructible: false,
         notify_chan: None,
+        name: None,
         sched: SchedOpts {
             mode: DefaultScheduler,
         }
@@ -507,6 +519,21 @@ pub fn try<T:Send>(f: ~fn() -> T) -> Result<T,()> {
 
 /* Lifecycle functions */
 
+/// Read the name of the current task.
+pub fn with_task_name<U>(blk: &fn(Option<&str>) -> U) -> U {
+    use rt::task::Task;
+
+    match context() {
+        TaskContext => do Local::borrow::<Task, U> |task| {
+            match task.name {
+                Some(ref name) => blk(Some(name.as_slice())),
+                None => blk(None)
+            }
+        },
+        _ => fail!("no task name exists in %?", context()),
+    }
+}
+
 pub fn yield() {
     //! Yield control to the task scheduler
 
@@ -806,6 +833,34 @@ fn test_spawn_linked_sup_propagate_sibling() {
 }
 
 #[test]
+fn test_unnamed_task() {
+    use rt::test::run_in_newsched_task;
+
+    do run_in_newsched_task {
+        do spawn {
+            do with_task_name |name| {
+                assert!(name.is_none());
+            }
+        }
+    }
+}
+
+#[test]
+fn test_named_task() {
+    use rt::test::run_in_newsched_task;
+
+    do run_in_newsched_task {
+        let mut t = task();
+        t.name(~"ada lovelace");
+        do t.spawn {
+            do with_task_name |name| {
+                assert!(name.get() == "ada lovelace");
+            }
+        }
+    }
+}
+
+#[test]
 fn test_run_basic() {
     let (po, ch) = stream::<()>();
     let mut builder = task();
diff --git a/src/libstd/task/spawn.rs b/src/libstd/task/spawn.rs
index 54e46826976..7a42cc5c8b5 100644
--- a/src/libstd/task/spawn.rs
+++ b/src/libstd/task/spawn.rs
@@ -726,6 +726,8 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
         task.death.on_exit = Some(on_exit);
     }
 
+    task.name = opts.name.take();
+
     rtdebug!("spawn about to take scheduler");
 
     let sched = Local::take::<Scheduler>();