1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
12 * Utilities for managing and scheduling tasks
14 * An executing Rust program consists of a collection of tasks, each with their
15 * own stack, and sole ownership of their allocated heap data. Tasks communicate
16 * with each other using channels (see `std::comm` for more info about how
17 * communication works).
19 * Failure in one task does not propagate to any others (not to parent, not to
20 * child). Failure propagation is instead handled by using the channel send()
21 * and recv() methods which will fail if the other end has hung up already.
25 * By default, every task is created with the same "flavor" as the calling task.
26 * This flavor refers to the scheduling mode, with two possibilities currently
27 * being 1:1 and M:N modes. Green (M:N) tasks are cooperatively scheduled and
28 * native (1:1) tasks are scheduled by the OS kernel.
34 * println!("Hello, World!");
40 use comm::{Sender, Receiver, channel};
41 use io::{Writer, stdio};
42 use kinds::{Send, marker};
43 use option::{None, Some, Option};
45 use result::{Result, Ok, Err};
49 use str::{Str, SendStr, IntoMaybeOwned};
51 #[cfg(test)] use any::AnyRefExt;
52 #[cfg(test)] use owned::AnyOwnExt;
53 #[cfg(test)] use result;
54 #[cfg(test)] use str::StrAllocating;
55 #[cfg(test)] use string::String;
57 /// Task configuration options
59 /// Enable lifecycle notifications on the given channel
60 pub notify_chan: Option<Sender<task::Result>>,
61 /// A name for the task-to-be, for identification in failure messages
62 pub name: Option<SendStr>,
63 /// The size of the stack for the spawned task
64 pub stack_size: Option<uint>,
66 pub stdout: Option<Box<Writer + Send>>,
68 pub stderr: Option<Box<Writer + Send>>,
72 * The task builder type.
74 * Provides detailed control over the properties and behavior of new tasks.
76 // NB: Builders are designed to be single-use because they do stateful
77 // things that get weird when reusing - e.g. if you create a result future
78 // it only applies to a single task, so then you have to maintain Some
79 // potentially tricky state to ensure that everything behaves correctly
80 // when you try to reuse the builder to spawn a new task. We'll just
81 // sidestep that whole issue by making builders uncopyable and making
82 // the run function move them in.
83 pub struct TaskBuilder {
84 /// Options to spawn the new task with
86 gen_body: Option<proc(v: proc(): Send): Send -> proc(): Send>,
87 nocopy: marker::NoCopy,
91 /// Generate the base configuration for spawning a task, off of which more
92 /// configuration methods can be chained.
93 pub fn new() -> TaskBuilder {
95 opts: TaskOpts::new(),
97 nocopy: marker::NoCopy,
101 /// Get a future representing the exit status of the task.
103 /// Taking the value of the future will block until the child task
104 /// terminates. The future result return value will be created *before* the task is
105 /// spawned; as such, do not invoke .get() on it directly;
106 /// rather, store it in an outer variable/list for later use.
109 /// Fails if a future_result was already set for this task.
110 pub fn future_result(&mut self) -> Receiver<task::Result> {
111 // FIXME (#3725): Once linked failure and notification are
112 // handled in the library, I can imagine implementing this by just
113 // registering an arbitrary number of task::on_exit handlers and
114 // sending out messages.
116 if self.opts.notify_chan.is_some() {
117 fail!("Can't set multiple future_results for one task!");
120 // Construct the future and give it to the caller.
121 let (tx, rx) = channel();
123 // Reconfigure self to use a notify channel.
124 self.opts.notify_chan = Some(tx);
129 /// Name the task-to-be. Currently the name is used for identification
130 /// only in failure messages.
131 pub fn named<S: IntoMaybeOwned<'static>>(mut self, name: S) -> TaskBuilder {
132 self.opts.name = Some(name.into_maybe_owned());
137 * Add a wrapper to the body of the spawned task.
139 * Before the task is spawned it is passed through a 'body generator'
140 * function that may perform local setup operations as well as wrap
141 * the task body in remote setup operations. With this the behavior
142 * of tasks can be extended in simple ways.
144 * This function augments the current body generator with a new body
145 * generator by applying the task body which results from the
146 * existing body generator to the new body generator.
148 pub fn with_wrapper(mut self,
149 wrapper: proc(v: proc(): Send): Send -> proc(): Send)
152 self.gen_body = match self.gen_body.take() {
153 Some(prev) => Some(proc(body) { wrapper(prev(body)) }),
154 None => Some(wrapper)
160 * Creates and executes a new child task
162 * Sets up a new task with its own call stack and schedules it to run
163 * the provided unique closure. The task has the properties and behavior
164 * specified by the task_builder.
166 pub fn spawn(mut self, f: proc(): Send) {
167 let gen_body = self.gen_body.take();
168 let f = match gen_body {
172 let t: Box<Task> = match Local::try_take() {
174 None => fail!("need a local task to spawn a new task"),
176 let TaskOpts { notify_chan, name, stack_size, stdout, stderr } = self.opts;
178 let opts = task::TaskOpts {
179 on_exit: notify_chan.map(|c| proc(r) c.send(r)),
181 stack_size: stack_size,
183 if stdout.is_some() || stderr.is_some() {
184 t.spawn_sibling(opts, proc() {
185 let _ = stdout.map(stdio::set_stdout);
186 let _ = stderr.map(stdio::set_stderr);
190 t.spawn_sibling(opts, f);
195 * Execute a function in another task and return either the return value
196 * of the function or result::err.
200 * If the function executed successfully then try returns result::ok
201 * containing the value returned by the function. If the function fails
202 * then try returns result::err containing nil.
205 * Fails if a future_result was already set for this task.
207 pub fn try<T: Send>(mut self, f: proc(): Send -> T)
208 -> Result<T, Box<Any + Send>> {
209 let (tx, rx) = channel();
211 let result = self.future_result();
217 match result.recv() {
218 Ok(()) => Ok(rx.recv()),
219 Err(cause) => Err(cause)
224 /* Task construction */
227 pub fn new() -> TaskOpts {
229 * The default task options
242 /* Spawn convenience functions */
244 /// Creates and executes a new child task
246 /// Sets up a new task with its own call stack and schedules it to run
247 /// the provided unique closure.
249 /// This function is equivalent to `TaskBuilder::new().spawn(f)`.
250 pub fn spawn(f: proc(): Send) {
251 TaskBuilder::new().spawn(f)
254 /// Execute a function in another task and return either the return value of
255 /// the function or an error if the task failed
257 /// This is equivalent to TaskBuilder::new().try
258 pub fn try<T: Send>(f: proc(): Send -> T) -> Result<T, Box<Any + Send>> {
259 TaskBuilder::new().try(f)
263 /* Lifecycle functions */
265 /// Read the name of the current task.
266 pub fn with_task_name<U>(blk: |Option<&str>| -> U) -> U {
269 let task = Local::borrow(None::<Task>);
271 Some(ref name) => blk(Some(name.as_slice())),
276 pub fn deschedule() {
277 //! Yield control to the task scheduler
279 use rt::local::Local;
281 // FIXME(#7544): Optimize this, since we know we won't block.
282 let task: Box<Task> = Local::take();
286 pub fn failing() -> bool {
287 //! True if the running task has failed
289 Local::borrow(None::<Task>).unwinder.unwinding()
292 // The following 8 tests test the following 2^3 combinations:
293 // {un,}linked {un,}supervised failure propagation {up,down}wards.
295 // !!! These tests are dangerous. If Something is buggy, they will hang, !!!
296 // !!! instead of exiting cleanly. This might wedge the buildbots. !!!
299 fn test_unnamed_task() {
301 with_task_name(|name| {
302 assert!(name.is_none());
308 fn test_owned_named_task() {
309 TaskBuilder::new().named("ada lovelace".to_string()).spawn(proc() {
310 with_task_name(|name| {
311 assert!(name.unwrap() == "ada lovelace");
317 fn test_static_named_task() {
318 TaskBuilder::new().named("ada lovelace").spawn(proc() {
319 with_task_name(|name| {
320 assert!(name.unwrap() == "ada lovelace");
326 fn test_send_named_task() {
327 TaskBuilder::new().named("ada lovelace".into_maybe_owned()).spawn(proc() {
328 with_task_name(|name| {
329 assert!(name.unwrap() == "ada lovelace");
335 fn test_run_basic() {
336 let (tx, rx) = channel();
337 TaskBuilder::new().spawn(proc() {
344 fn test_with_wrapper() {
345 let (tx, rx) = channel();
346 TaskBuilder::new().with_wrapper(proc(body) {
347 let result: proc(): Send = proc() {
352 }).spawn(proc() { });
357 fn test_future_result() {
358 let mut builder = TaskBuilder::new();
359 let result = builder.future_result();
360 builder.spawn(proc() {});
361 assert!(result.recv().is_ok());
363 let mut builder = TaskBuilder::new();
364 let result = builder.future_result();
365 builder.spawn(proc() {
368 assert!(result.recv().is_err());
371 #[test] #[should_fail]
372 fn test_back_to_the_future_result() {
373 let mut builder = TaskBuilder::new();
374 builder.future_result();
375 builder.future_result();
379 fn test_try_success() {
381 "Success!".to_string()
382 }).as_ref().map(|s| s.as_slice()) {
383 result::Ok("Success!") => (),
393 result::Err(_) => (),
394 result::Ok(()) => fail!()
399 fn test_spawn_sched() {
402 let (tx, rx) = channel();
404 fn f(i: int, tx: Sender<()>) {
420 fn test_spawn_sched_childs_on_default_sched() {
421 let (tx, rx) = channel();
433 fn avoid_copying_the_body(spawnfn: |v: proc(): Send|) {
434 let (tx, rx) = channel::<uint>();
437 let x_in_parent = (&*x) as *int as uint;
440 let x_in_child = (&*x) as *int as uint;
444 let x_in_child = rx.recv();
445 assert_eq!(x_in_parent, x_in_child);
449 fn test_avoid_copying_the_body_spawn() {
450 avoid_copying_the_body(spawn);
454 fn test_avoid_copying_the_body_task_spawn() {
455 avoid_copying_the_body(|f| {
456 let builder = TaskBuilder::new();
457 builder.spawn(proc() {
464 fn test_avoid_copying_the_body_try() {
465 avoid_copying_the_body(|f| {
473 fn test_child_doesnt_ref_parent() {
474 // If the child refcounts the parent task, this will stack overflow when
475 // climbing the task tree to dereference each ancestor. (See #1789)
476 // (well, it would if the constant were 8000+ - I lowered it to be more
477 // valgrind-friendly. try this at home, instead..!)
478 static generations: uint = 16;
479 fn child_no(x: uint) -> proc(): Send {
482 TaskBuilder::new().spawn(child_no(x+1));
486 TaskBuilder::new().spawn(child_no(0));
490 fn test_simple_newsched_spawn() {
495 fn test_try_fail_message_static_str() {
497 fail!("static string");
500 type T = &'static str;
501 assert!(e.is::<T>());
502 assert_eq!(*e.move::<T>().unwrap(), "static string");
509 fn test_try_fail_message_owned_str() {
511 fail!("owned string".to_string());
515 assert!(e.is::<T>());
516 assert_eq!(*e.move::<T>().unwrap(), "owned string".to_string());
523 fn test_try_fail_message_any() {
525 fail!(box 413u16 as Box<Any + Send>);
528 type T = Box<Any + Send>;
529 assert!(e.is::<T>());
530 let any = e.move::<T>().unwrap();
531 assert!(any.is::<u16>());
532 assert_eq!(*any.move::<u16>().unwrap(), 413u16);
539 fn test_try_fail_message_unit_struct() {
545 Err(ref e) if e.is::<Juju>() => {}
546 Err(_) | Ok(()) => fail!()