]> git.lizzy.rs Git - rust.git/blob - src/libstd/task/mod.rs
Disable linked failure tests
[rust.git] / src / libstd / task / mod.rs
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.
4 //
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.
10
11 /*!
12  * Task management.
13  *
14  * An executing Rust program consists of a tree of tasks, each with their own
15  * stack, and sole ownership of their allocated heap data. Tasks communicate
16  * with each other using ports and channels.
17  *
18  * When a task fails, that failure will propagate to its parent (the task
19  * that spawned it) and the parent will fail as well. The reverse is not
20  * true: when a parent task fails its children will continue executing. When
21  * the root (main) task fails, all tasks fail, and then so does the entire
22  * process.
23  *
24  * Tasks may execute in parallel and are scheduled automatically by the
25  * runtime.
26  *
27  * # Example
28  *
29  * ~~~
30  * do spawn {
31  *     log(error, "Hello, World!");
32  * }
33  * ~~~
34  */
35
36 #[allow(missing_doc)];
37
38 use prelude::*;
39
40 use cell::Cell;
41 use cmp::Eq;
42 use comm::{stream, Chan, GenericChan, GenericPort, Port};
43 use result::Result;
44 use result;
45 use rt::{context, OldTaskContext, TaskContext};
46 use rt::local::Local;
47 use unstable::finally::Finally;
48 use util;
49
50 #[cfg(test)] use cast;
51 #[cfg(test)] use comm::SharedChan;
52 #[cfg(test)] use comm;
53 #[cfg(test)] use ptr;
54 #[cfg(test)] use task;
55
56 mod local_data_priv;
57 pub mod rt;
58 pub mod spawn;
59
60 /**
61  * Indicates the manner in which a task exited.
62  *
63  * A task that completes without failing is considered to exit successfully.
64  * Supervised ancestors and linked siblings may yet fail after this task
65  * succeeds. Also note that in such a case, it may be nondeterministic whether
66  * linked failure or successful exit happen first.
67  *
68  * If you wish for this result's delivery to block until all linked and/or
69  * children tasks complete, recommend using a result future.
70  */
71 #[deriving(Eq)]
72 pub enum TaskResult {
73     Success,
74     Failure,
75 }
76
77 /// Scheduler modes
78 #[deriving(Eq)]
79 pub enum SchedMode {
80     /// Run task on the default scheduler
81     DefaultScheduler,
82     /// All tasks run in the same OS thread
83     SingleThreaded,
84 }
85
86 /**
87  * Scheduler configuration options
88  *
89  * # Fields
90  *
91  * * sched_mode - The operating mode of the scheduler
92  *
93  */
94 pub struct SchedOpts {
95     mode: SchedMode,
96 }
97
98 /**
99  * Task configuration options
100  *
101  * # Fields
102  *
103  * * linked - Propagate failure bidirectionally between child and parent.
104  *            True by default. If both this and 'supervised' are false, then
105  *            either task's failure will not affect the other ("unlinked").
106  *
107  * * supervised - Propagate failure unidirectionally from parent to child,
108  *                but not from child to parent. False by default.
109  *
110  * * watched - Make parent task collect exit status notifications from child
111  *             before reporting its own exit status. (This delays the parent
112  *             task's death and cleanup until after all transitively watched
113  *             children also exit.) True by default.
114  *
115  * * indestructible - Configures the task to ignore kill signals received from
116  *                    linked failure. This may cause process hangs during
117  *                    failure if not used carefully, but causes task blocking
118  *                    code paths (e.g. port recv() calls) to be faster by 2
119  *                    atomic operations. False by default.
120  *
121  * * notify_chan - Enable lifecycle notifications on the given channel
122  *
123  * * name - A name for the task-to-be, for identification in failure messages.
124  *
125  * * sched - Specify the configuration of a new scheduler to create the task
126  *           in
127  *
128  *     By default, every task is created in the same scheduler as its
129  *     parent, where it is scheduled cooperatively with all other tasks
130  *     in that scheduler. Some specialized applications may want more
131  *     control over their scheduling, in which case they can be spawned
132  *     into a new scheduler with the specific properties required.
133  *
134  *     This is of particular importance for libraries which want to call
135  *     into foreign code that blocks. Without doing so in a different
136  *     scheduler other tasks will be impeded or even blocked indefinitely.
137  */
138 pub struct TaskOpts {
139     linked: bool,
140     supervised: bool,
141     watched: bool,
142     indestructible: bool,
143     notify_chan: Option<Chan<TaskResult>>,
144     name: Option<~str>,
145     sched: SchedOpts,
146     stack_size: Option<uint>
147 }
148
149 /**
150  * The task builder type.
151  *
152  * Provides detailed control over the properties and behavior of new tasks.
153  */
154 // NB: Builders are designed to be single-use because they do stateful
155 // things that get weird when reusing - e.g. if you create a result future
156 // it only applies to a single task, so then you have to maintain Some
157 // potentially tricky state to ensure that everything behaves correctly
158 // when you try to reuse the builder to spawn a new task. We'll just
159 // sidestep that whole issue by making builders uncopyable and making
160 // the run function move them in.
161
162 // FIXME (#3724): Replace the 'consumed' bit with move mode on self
163 pub struct TaskBuilder {
164     opts: TaskOpts,
165     gen_body: Option<~fn(v: ~fn()) -> ~fn()>,
166     can_not_copy: Option<util::NonCopyable>,
167     consumed: bool,
168 }
169
170 /**
171  * Generate the base configuration for spawning a task, off of which more
172  * configuration methods can be chained.
173  * For example, task().unlinked().spawn is equivalent to spawn_unlinked.
174  */
175 pub fn task() -> TaskBuilder {
176     TaskBuilder {
177         opts: default_task_opts(),
178         gen_body: None,
179         can_not_copy: None,
180         consumed: false,
181     }
182 }
183
184 impl TaskBuilder {
185     fn consume(&mut self) -> TaskBuilder {
186         if self.consumed {
187             fail!("Cannot copy a task_builder"); // Fake move mode on self
188         }
189         self.consumed = true;
190         let gen_body = self.gen_body.take();
191         let notify_chan = self.opts.notify_chan.take();
192         let name = self.opts.name.take();
193         TaskBuilder {
194             opts: TaskOpts {
195                 linked: self.opts.linked,
196                 supervised: self.opts.supervised,
197                 watched: self.opts.watched,
198                 indestructible: self.opts.indestructible,
199                 notify_chan: notify_chan,
200                 name: name,
201                 sched: self.opts.sched,
202                 stack_size: self.opts.stack_size
203             },
204             gen_body: gen_body,
205             can_not_copy: None,
206             consumed: false
207         }
208     }
209
210     /// Decouple the child task's failure from the parent's. If either fails,
211     /// the other will not be killed.
212     pub fn unlinked(&mut self) {
213         self.opts.linked = false;
214         self.opts.watched = false;
215     }
216
217     /// Unidirectionally link the child task's failure with the parent's. The
218     /// child's failure will not kill the parent, but the parent's will kill
219     /// the child.
220     pub fn supervised(&mut self) {
221         self.opts.supervised = true;
222         self.opts.linked = false;
223         self.opts.watched = false;
224     }
225
226     /// Link the child task's and parent task's failures. If either fails, the
227     /// other will be killed.
228     pub fn linked(&mut self) {
229         self.opts.linked = true;
230         self.opts.supervised = false;
231         self.opts.watched = true;
232     }
233
234     /// Cause the parent task to collect the child's exit status (and that of
235     /// all transitively-watched grandchildren) before reporting its own.
236     pub fn watched(&mut self) {
237         self.opts.watched = true;
238     }
239
240     /// Allow the child task to outlive the parent task, at the possible cost
241     /// of the parent reporting success even if the child task fails later.
242     pub fn unwatched(&mut self) {
243         self.opts.watched = false;
244     }
245
246     /// Cause the child task to ignore any kill signals received from linked
247     /// failure. This optimizes context switching, at the possible expense of
248     /// process hangs in the case of unexpected failure.
249     pub fn indestructible(&mut self) {
250         self.opts.indestructible = true;
251     }
252
253     /**
254      * Get a future representing the exit status of the task.
255      *
256      * Taking the value of the future will block until the child task
257      * terminates. The future-receiving callback specified will be called
258      * *before* the task is spawned; as such, do not invoke .get() within the
259      * closure; rather, store it in an outer variable/list for later use.
260      *
261      * Note that the future returning by this function is only useful for
262      * obtaining the value of the next task to be spawning with the
263      * builder. If additional tasks are spawned with the same builder
264      * then a new result future must be obtained prior to spawning each
265      * task.
266      *
267      * # Failure
268      * Fails if a future_result was already set for this task.
269      */
270     pub fn future_result(&mut self, blk: &fn(v: Port<TaskResult>)) {
271         // FIXME (#3725): Once linked failure and notification are
272         // handled in the library, I can imagine implementing this by just
273         // registering an arbitrary number of task::on_exit handlers and
274         // sending out messages.
275
276         if self.opts.notify_chan.is_some() {
277             fail!("Can't set multiple future_results for one task!");
278         }
279
280         // Construct the future and give it to the caller.
281         let (notify_pipe_po, notify_pipe_ch) = stream::<TaskResult>();
282
283         blk(notify_pipe_po);
284
285         // Reconfigure self to use a notify channel.
286         self.opts.notify_chan = Some(notify_pipe_ch);
287     }
288
289     /// Name the task-to-be. Currently the name is used for identification
290     /// only in failure messages.
291     pub fn name(&mut self, name: ~str) {
292         self.opts.name = Some(name);
293     }
294
295     /// Configure a custom scheduler mode for the task.
296     pub fn sched_mode(&mut self, mode: SchedMode) {
297         self.opts.sched.mode = mode;
298     }
299
300     /**
301      * Add a wrapper to the body of the spawned task.
302      *
303      * Before the task is spawned it is passed through a 'body generator'
304      * function that may perform local setup operations as well as wrap
305      * the task body in remote setup operations. With this the behavior
306      * of tasks can be extended in simple ways.
307      *
308      * This function augments the current body generator with a new body
309      * generator by applying the task body which results from the
310      * existing body generator to the new body generator.
311      */
312     pub fn add_wrapper(&mut self, wrapper: ~fn(v: ~fn()) -> ~fn()) {
313         let prev_gen_body = self.gen_body.take();
314         let prev_gen_body = match prev_gen_body {
315             Some(gen) => gen,
316             None => {
317                 let f: ~fn(~fn()) -> ~fn() = |body| body;
318                 f
319             }
320         };
321         let prev_gen_body = Cell::new(prev_gen_body);
322         let next_gen_body = {
323             let f: ~fn(~fn()) -> ~fn() = |body| {
324                 let prev_gen_body = prev_gen_body.take();
325                 wrapper(prev_gen_body(body))
326             };
327             f
328         };
329         self.gen_body = Some(next_gen_body);
330     }
331
332     /**
333      * Creates and executes a new child task
334      *
335      * Sets up a new task with its own call stack and schedules it to run
336      * the provided unique closure. The task has the properties and behavior
337      * specified by the task_builder.
338      *
339      * # Failure
340      *
341      * When spawning into a new scheduler, the number of threads requested
342      * must be greater than zero.
343      */
344     pub fn spawn(&mut self, f: ~fn()) {
345         let gen_body = self.gen_body.take();
346         let notify_chan = self.opts.notify_chan.take();
347         let name = self.opts.name.take();
348         let x = self.consume();
349         let opts = TaskOpts {
350             linked: x.opts.linked,
351             supervised: x.opts.supervised,
352             watched: x.opts.watched,
353             indestructible: x.opts.indestructible,
354             notify_chan: notify_chan,
355             name: name,
356             sched: x.opts.sched,
357             stack_size: x.opts.stack_size
358         };
359         let f = match gen_body {
360             Some(gen) => {
361                 gen(f)
362             }
363             None => {
364                 f
365             }
366         };
367         spawn::spawn_raw(opts, f);
368     }
369
370     /// Runs a task, while transfering ownership of one argument to the child.
371     pub fn spawn_with<A:Send>(&mut self, arg: A, f: ~fn(v: A)) {
372         let arg = Cell::new(arg);
373         do self.spawn {
374             f(arg.take());
375         }
376     }
377
378     /**
379      * Execute a function in another task and return either the return value
380      * of the function or result::err.
381      *
382      * # Return value
383      *
384      * If the function executed successfully then try returns result::ok
385      * containing the value returned by the function. If the function fails
386      * then try returns result::err containing nil.
387      *
388      * # Failure
389      * Fails if a future_result was already set for this task.
390      */
391     pub fn try<T:Send>(&mut self, f: ~fn() -> T) -> Result<T,()> {
392         let (po, ch) = stream::<T>();
393         let mut result = None;
394
395         self.future_result(|r| { result = Some(r); });
396
397         do self.spawn {
398             ch.send(f());
399         }
400
401         match result.unwrap().recv() {
402             Success => result::Ok(po.recv()),
403             Failure => result::Err(())
404         }
405     }
406 }
407
408
409 /* Task construction */
410
411 pub fn default_task_opts() -> TaskOpts {
412     /*!
413      * The default task options
414      *
415      * By default all tasks are supervised by their parent, are spawned
416      * into the same scheduler, and do not post lifecycle notifications.
417      */
418
419     TaskOpts {
420         linked: true,
421         supervised: false,
422         watched: true,
423         indestructible: false,
424         notify_chan: None,
425         name: None,
426         sched: SchedOpts {
427             mode: DefaultScheduler,
428         },
429         stack_size: None
430     }
431 }
432
433 /* Spawn convenience functions */
434
435 /// Creates and executes a new child task
436 ///
437 /// Sets up a new task with its own call stack and schedules it to run
438 /// the provided unique closure.
439 ///
440 /// This function is equivalent to `task().spawn(f)`.
441 pub fn spawn(f: ~fn()) {
442     let mut task = task();
443     task.spawn(f)
444 }
445
446 /// Creates a child task unlinked from the current one. If either this
447 /// task or the child task fails, the other will not be killed.
448 pub fn spawn_unlinked(f: ~fn()) {
449     let mut task = task();
450     task.unlinked();
451     task.spawn(f)
452 }
453
454 pub fn spawn_supervised(f: ~fn()) {
455     /*!
456      * Creates a child task supervised by the current one. If the child
457      * task fails, the parent will not be killed, but if the parent fails,
458      * the child will be killed.
459      */
460
461     let mut task = task();
462     task.supervised();
463     task.spawn(f)
464 }
465
466 /// Creates a child task that cannot be killed by linked failure. This causes
467 /// its context-switch path to be faster by 2 atomic swap operations.
468 /// (Note that this convenience wrapper still uses linked-failure, so the
469 /// child's children will still be killable by the parent. For the fastest
470 /// possible spawn mode, use task::task().unlinked().indestructible().spawn.)
471 pub fn spawn_indestructible(f: ~fn()) {
472     let mut task = task();
473     task.indestructible();
474     task.spawn(f)
475 }
476
477 pub fn spawn_with<A:Send>(arg: A, f: ~fn(v: A)) {
478     /*!
479      * Runs a task, while transfering ownership of one argument to the
480      * child.
481      *
482      * This is useful for transfering ownership of noncopyables to
483      * another task.
484      *
485      * This function is equivalent to `task().spawn_with(arg, f)`.
486      */
487
488     let mut task = task();
489     task.spawn_with(arg, f)
490 }
491
492 pub fn spawn_sched(mode: SchedMode, f: ~fn()) {
493     /*!
494      * Creates a new task on a new or existing scheduler
495
496      * When there are no more tasks to execute the
497      * scheduler terminates.
498      *
499      * # Failure
500      *
501      * In manual threads mode the number of threads requested must be
502      * greater than zero.
503      */
504
505     let mut task = task();
506     task.sched_mode(mode);
507     task.spawn(f)
508 }
509
510 pub fn try<T:Send>(f: ~fn() -> T) -> Result<T,()> {
511     /*!
512      * Execute a function in another task and return either the return value
513      * of the function or result::err.
514      *
515      * This is equivalent to task().supervised().try.
516      */
517
518     let mut task = task();
519     task.supervised();
520     task.try(f)
521 }
522
523
524 /* Lifecycle functions */
525
526 /// Read the name of the current task.
527 pub fn with_task_name<U>(blk: &fn(Option<&str>) -> U) -> U {
528     use rt::task::Task;
529
530     match context() {
531         TaskContext => do Local::borrow::<Task, U> |task| {
532             match task.name {
533                 Some(ref name) => blk(Some(name.as_slice())),
534                 None => blk(None)
535             }
536         },
537         _ => fail!("no task name exists in %?", context()),
538     }
539 }
540
541 pub fn yield() {
542     //! Yield control to the task scheduler
543
544     use rt::{context, OldTaskContext};
545     use rt::local::Local;
546     use rt::sched::Scheduler;
547
548     unsafe {
549         match context() {
550             OldTaskContext => {
551                 let task_ = rt::rust_get_task();
552                 let killed = rt::rust_task_yield(task_);
553                 if killed && !failing() {
554                     fail!("killed");
555                 }
556             }
557             _ => {
558                 // XXX: What does yield really mean in newsched?
559                 // FIXME(#7544): Optimize this, since we know we won't block.
560                 let sched = Local::take::<Scheduler>();
561                 do sched.deschedule_running_task_and_then |sched, task| {
562                     sched.enqueue_blocked_task(task);
563                 }
564             }
565         }
566     }
567 }
568
569 pub fn failing() -> bool {
570     //! True if the running task has failed
571
572     use rt::task::Task;
573
574     match context() {
575         OldTaskContext => {
576             unsafe {
577                 rt::rust_task_is_unwinding(rt::rust_get_task())
578             }
579         }
580         _ => {
581             do Local::borrow::<Task, bool> |local| {
582                 local.unwinder.unwinding
583             }
584         }
585     }
586 }
587
588 /**
589  * Temporarily make the task unkillable
590  *
591  * # Example
592  *
593  * ~~~
594  * do task::unkillable {
595  *     // detach / yield / destroy must all be called together
596  *     rustrt::rust_port_detach(po);
597  *     // This must not result in the current task being killed
598  *     task::yield();
599  *     rustrt::rust_port_destroy(po);
600  * }
601  * ~~~
602  */
603 pub fn unkillable<U>(f: &fn() -> U) -> U {
604     use rt::task::Task;
605
606     unsafe {
607         match context() {
608             OldTaskContext => {
609                 let t = rt::rust_get_task();
610                 do (|| {
611                     rt::rust_task_inhibit_kill(t);
612                     f()
613                 }).finally {
614                     rt::rust_task_allow_kill(t);
615                 }
616             }
617             TaskContext => {
618                 // The inhibits/allows might fail and need to borrow the task.
619                 let t = Local::unsafe_borrow::<Task>();
620                 do (|| {
621                     (*t).death.inhibit_kill((*t).unwinder.unwinding);
622                     f()
623                 }).finally {
624                     (*t).death.allow_kill((*t).unwinder.unwinding);
625                 }
626             }
627             // FIXME(#3095): This should be an rtabort as soon as the scheduler
628             // no longer uses a workqueue implemented with an Exclusive.
629             _ => f()
630         }
631     }
632 }
633
634 /// The inverse of unkillable. Only ever to be used nested in unkillable().
635 pub unsafe fn rekillable<U>(f: &fn() -> U) -> U {
636     use rt::task::Task;
637
638     match context() {
639         OldTaskContext => {
640             let t = rt::rust_get_task();
641             do (|| {
642                 rt::rust_task_allow_kill(t);
643                 f()
644             }).finally {
645                 rt::rust_task_inhibit_kill(t);
646             }
647         }
648         TaskContext => {
649             let t = Local::unsafe_borrow::<Task>();
650             do (|| {
651                 (*t).death.allow_kill((*t).unwinder.unwinding);
652                 f()
653             }).finally {
654                 (*t).death.inhibit_kill((*t).unwinder.unwinding);
655             }
656         }
657         // FIXME(#3095): As in unkillable().
658         _ => f()
659     }
660 }
661
662 #[ignore(reason = "linked failure")]
663 #[test] #[ignore(cfg(windows))]
664 fn test_kill_unkillable_task() {
665     use rt::test::*;
666
667     // Attempt to test that when a kill signal is received at the start of an
668     // unkillable section, 'unkillable' unwinds correctly. This is actually
669     // quite a difficult race to expose, as the kill has to happen on a second
670     // CPU, *after* the spawner is already switched-back-to (and passes the
671     // killed check at the start of its timeslice). As far as I know, it's not
672     // possible to make this race deterministic, or even more likely to happen.
673     do run_in_newsched_task {
674         do task::try {
675             do task::spawn {
676                 fail!();
677             }
678             do task::unkillable { }
679         };
680     }
681 }
682
683 #[ignore(reason = "linked failure")]
684 #[test] #[ignore(cfg(windows))]
685 fn test_kill_rekillable_task() {
686     use rt::test::*;
687
688     // Tests that when a kill signal is received, 'rekillable' and
689     // 'unkillable' unwind correctly in conjunction with each other.
690     do run_in_newsched_task {
691         do task::try {
692             do task::unkillable {
693                 unsafe {
694                     do task::rekillable {
695                         do task::spawn {
696                             fail!();
697                         }
698                     }
699                 }
700             }
701         };
702     }
703 }
704
705 #[test] #[should_fail] #[ignore(cfg(windows))]
706 fn test_cant_dup_task_builder() {
707     let mut builder = task();
708     builder.unlinked();
709     do builder.spawn {}
710     // FIXME(#3724): For now, this is a -runtime- failure, because we haven't
711     // got move mode on self. When 3724 is fixed, this test should fail to
712     // compile instead, and should go in tests/compile-fail.
713     do builder.spawn {} // b should have been consumed by the previous call
714 }
715
716 // The following 8 tests test the following 2^3 combinations:
717 // {un,}linked {un,}supervised failure propagation {up,down}wards.
718
719 // !!! These tests are dangerous. If Something is buggy, they will hang, !!!
720 // !!! instead of exiting cleanly. This might wedge the buildbots.       !!!
721
722 #[cfg(test)]
723 fn block_forever() { let (po, _ch) = stream::<()>(); po.recv(); }
724
725 #[ignore(reason = "linked failure")]
726 #[test] #[ignore(cfg(windows))]
727 fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port
728     use rt::test::run_in_newsched_task;
729     do run_in_newsched_task {
730         let (po, ch) = stream();
731         let ch = SharedChan::new(ch);
732         do spawn_unlinked {
733             let ch = ch.clone();
734             do spawn_unlinked {
735                 // Give middle task a chance to fail-but-not-kill-us.
736                 do 16.times { task::yield(); }
737                 ch.send(()); // If killed first, grandparent hangs.
738             }
739             fail!(); // Shouldn't kill either (grand)parent or (grand)child.
740         }
741         po.recv();
742     }
743 }
744 #[ignore(reason = "linked failure")]
745 #[test] #[ignore(cfg(windows))]
746 fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails
747     use rt::test::run_in_newsched_task;
748     do run_in_newsched_task {
749         do spawn_unlinked { fail!(); }
750     }
751 }
752 #[ignore(reason = "linked failure")]
753 #[test] #[ignore(cfg(windows))]
754 fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
755     use rt::test::run_in_newsched_task;
756     do run_in_newsched_task {
757         do spawn_supervised { fail!(); }
758         // Give child a chance to fail-but-not-kill-us.
759         do 16.times { task::yield(); }
760     }
761 }
762 #[ignore(reason = "linked failure")]
763 #[test] #[ignore(cfg(windows))]
764 fn test_spawn_unlinked_sup_fail_down() {
765     use rt::test::run_in_newsched_task;
766     do run_in_newsched_task {
767         let result: Result<(),()> = do try {
768             do spawn_supervised { block_forever(); }
769             fail!(); // Shouldn't leave a child hanging around.
770         };
771         assert!(result.is_err());
772     }
773 }
774
775 #[ignore(reason = "linked failure")]
776 #[test] #[ignore(cfg(windows))]
777 fn test_spawn_linked_sup_fail_up() { // child fails; parent fails
778     use rt::test::run_in_newsched_task;
779     do run_in_newsched_task {
780         let result: Result<(),()> = do try {
781             // Unidirectional "parenting" shouldn't override bidirectional linked.
782             // We have to cheat with opts - the interface doesn't support them because
783             // they don't make sense (redundant with task().supervised()).
784             let mut b0 = task();
785             b0.opts.linked = true;
786             b0.opts.supervised = true;
787
788             do b0.spawn {
789                 fail!();
790             }
791             block_forever(); // We should get punted awake
792         };
793         assert!(result.is_err());
794     }
795 }
796 #[ignore(reason = "linked failure")]
797 #[test] #[ignore(cfg(windows))]
798 fn test_spawn_linked_sup_fail_down() { // parent fails; child fails
799     use rt::test::run_in_newsched_task;
800     do run_in_newsched_task {
801         let result: Result<(),()> = do try {
802             // We have to cheat with opts - the interface doesn't support them because
803             // they don't make sense (redundant with task().supervised()).
804             let mut b0 = task();
805             b0.opts.linked = true;
806             b0.opts.supervised = true;
807             do b0.spawn { block_forever(); }
808             fail!(); // *both* mechanisms would be wrong if this didn't kill the child
809         };
810         assert!(result.is_err());
811     }
812 }
813 #[ignore(reason = "linked failure")]
814 #[test] #[ignore(cfg(windows))]
815 fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails
816     use rt::test::run_in_newsched_task;
817     do run_in_newsched_task {
818         let result: Result<(),()> = do try {
819             // Default options are to spawn linked & unsupervised.
820             do spawn { fail!(); }
821             block_forever(); // We should get punted awake
822         };
823         assert!(result.is_err());
824     }
825 }
826 #[ignore(reason = "linked failure")]
827 #[test] #[ignore(cfg(windows))]
828 fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails
829     use rt::test::run_in_newsched_task;
830     do run_in_newsched_task {
831         let result: Result<(),()> = do try {
832             // Default options are to spawn linked & unsupervised.
833             do spawn { block_forever(); }
834             fail!();
835         };
836         assert!(result.is_err());
837     }
838 }
839 #[ignore(reason = "linked failure")]
840 #[test] #[ignore(cfg(windows))]
841 fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails
842     use rt::test::run_in_newsched_task;
843     do run_in_newsched_task {
844         let result: Result<(),()> = do try {
845             // Make sure the above test is the same as this one.
846             let mut builder = task();
847             builder.linked();
848             do builder.spawn { block_forever(); }
849             fail!();
850         };
851         assert!(result.is_err());
852     }
853 }
854
855 // A couple bonus linked failure tests - testing for failure propagation even
856 // when the middle task exits successfully early before kill signals are sent.
857
858 #[ignore(reason = "linked failure")]
859 #[test] #[ignore(cfg(windows))]
860 fn test_spawn_failure_propagate_grandchild() {
861     use rt::test::run_in_newsched_task;
862     do run_in_newsched_task {
863         let result: Result<(),()> = do try {
864             // Middle task exits; does grandparent's failure propagate across the gap?
865             do spawn_supervised {
866                 do spawn_supervised { block_forever(); }
867             }
868             do 16.times { task::yield(); }
869             fail!();
870         };
871         assert!(result.is_err());
872     }
873 }
874
875 #[ignore(reason = "linked failure")]
876 #[test] #[ignore(cfg(windows))]
877 fn test_spawn_failure_propagate_secondborn() {
878     use rt::test::run_in_newsched_task;
879     do run_in_newsched_task {
880         let result: Result<(),()> = do try {
881             // First-born child exits; does parent's failure propagate to sibling?
882             do spawn_supervised {
883                 do spawn { block_forever(); } // linked
884             }
885             do 16.times { task::yield(); }
886             fail!();
887         };
888         assert!(result.is_err());
889     }
890 }
891
892 #[ignore(reason = "linked failure")]
893 #[test] #[ignore(cfg(windows))]
894 fn test_spawn_failure_propagate_nephew_or_niece() {
895     use rt::test::run_in_newsched_task;
896     do run_in_newsched_task {
897         let result: Result<(),()> = do try {
898             // Our sibling exits; does our failure propagate to sibling's child?
899             do spawn { // linked
900                 do spawn_supervised { block_forever(); }
901             }
902             do 16.times { task::yield(); }
903             fail!();
904         };
905         assert!(result.is_err());
906     }
907 }
908
909 #[ignore(reason = "linked failure")]
910 #[test] #[ignore(cfg(windows))]
911 fn test_spawn_linked_sup_propagate_sibling() {
912     use rt::test::run_in_newsched_task;
913     do run_in_newsched_task {
914         let result: Result<(),()> = do try {
915             // Middle sibling exits - does eldest's failure propagate to youngest?
916             do spawn { // linked
917                 do spawn { block_forever(); } // linked
918             }
919             do 16.times { task::yield(); }
920             fail!();
921         };
922         assert!(result.is_err());
923     }
924 }
925
926 #[test]
927 fn test_unnamed_task() {
928     use rt::test::run_in_newsched_task;
929
930     do run_in_newsched_task {
931         do spawn {
932             do with_task_name |name| {
933                 assert!(name.is_none());
934             }
935         }
936     }
937 }
938
939 #[test]
940 fn test_named_task() {
941     use rt::test::run_in_newsched_task;
942
943     do run_in_newsched_task {
944         let mut t = task();
945         t.name(~"ada lovelace");
946         do t.spawn {
947             do with_task_name |name| {
948                 assert!(name.unwrap() == "ada lovelace");
949             }
950         }
951     }
952 }
953
954 #[test]
955 fn test_run_basic() {
956     let (po, ch) = stream::<()>();
957     let mut builder = task();
958     do builder.spawn {
959         ch.send(());
960     }
961     po.recv();
962 }
963
964 #[cfg(test)]
965 struct Wrapper {
966     f: Option<Chan<()>>
967 }
968
969 #[test]
970 fn test_add_wrapper() {
971     let (po, ch) = stream::<()>();
972     let mut b0 = task();
973     let ch = Cell::new(ch);
974     do b0.add_wrapper |body| {
975         let ch = Cell::new(ch.take());
976         let result: ~fn() = || {
977             let ch = ch.take();
978             body();
979             ch.send(());
980         };
981         result
982     };
983     do b0.spawn { }
984     po.recv();
985 }
986
987 #[test]
988 #[ignore(cfg(windows))]
989 fn test_future_result() {
990     let mut result = None;
991     let mut builder = task();
992     builder.future_result(|r| result = Some(r));
993     do builder.spawn {}
994     assert_eq!(result.unwrap().recv(), Success);
995
996     result = None;
997     let mut builder = task();
998     builder.future_result(|r| result = Some(r));
999     builder.unlinked();
1000     do builder.spawn {
1001         fail!();
1002     }
1003     assert_eq!(result.unwrap().recv(), Failure);
1004 }
1005
1006 #[test] #[should_fail] #[ignore(cfg(windows))]
1007 fn test_back_to_the_future_result() {
1008     let mut builder = task();
1009     builder.future_result(util::ignore);
1010     builder.future_result(util::ignore);
1011 }
1012
1013 #[test]
1014 fn test_try_success() {
1015     match do try {
1016         ~"Success!"
1017     } {
1018         result::Ok(~"Success!") => (),
1019         _ => fail!()
1020     }
1021 }
1022
1023 #[test]
1024 #[ignore(cfg(windows))]
1025 fn test_try_fail() {
1026     match do try {
1027         fail!()
1028     } {
1029         result::Err(()) => (),
1030         result::Ok(()) => fail!()
1031     }
1032 }
1033
1034 #[cfg(test)]
1035 fn get_sched_id() -> int {
1036     if context() == OldTaskContext {
1037         unsafe {
1038             rt::rust_get_sched_id() as int
1039         }
1040     } else {
1041         do Local::borrow::<::rt::sched::Scheduler, int> |sched| {
1042             sched.sched_id() as int
1043         }
1044     }
1045 }
1046
1047 #[test]
1048 fn test_spawn_sched() {
1049     let (po, ch) = stream::<()>();
1050     let ch = SharedChan::new(ch);
1051
1052     fn f(i: int, ch: SharedChan<()>) {
1053         let parent_sched_id = get_sched_id();
1054
1055         do spawn_sched(SingleThreaded) {
1056             let child_sched_id = get_sched_id();
1057             assert!(parent_sched_id != child_sched_id);
1058
1059             if (i == 0) {
1060                 ch.send(());
1061             } else {
1062                 f(i - 1, ch.clone());
1063             }
1064         };
1065
1066     }
1067     f(10, ch);
1068     po.recv();
1069 }
1070
1071 #[test]
1072 fn test_spawn_sched_childs_on_default_sched() {
1073     let (po, ch) = stream();
1074
1075     // Assuming tests run on the default scheduler
1076     let default_id = get_sched_id();
1077
1078     let ch = Cell::new(ch);
1079     do spawn_sched(SingleThreaded) {
1080         let parent_sched_id = get_sched_id();
1081         let ch = Cell::new(ch.take());
1082         do spawn {
1083             let ch = ch.take();
1084             let child_sched_id = get_sched_id();
1085             assert!(parent_sched_id != child_sched_id);
1086             assert_eq!(child_sched_id, default_id);
1087             ch.send(());
1088         };
1089     };
1090
1091     po.recv();
1092 }
1093
1094 #[cfg(test)]
1095 mod testrt {
1096     use libc;
1097
1098     #[nolink]
1099     extern {
1100         pub fn rust_dbg_lock_create() -> *libc::c_void;
1101         pub fn rust_dbg_lock_destroy(lock: *libc::c_void);
1102         pub fn rust_dbg_lock_lock(lock: *libc::c_void);
1103         pub fn rust_dbg_lock_unlock(lock: *libc::c_void);
1104         pub fn rust_dbg_lock_wait(lock: *libc::c_void);
1105         pub fn rust_dbg_lock_signal(lock: *libc::c_void);
1106     }
1107 }
1108
1109 #[test]
1110 fn test_spawn_sched_blocking() {
1111     unsafe {
1112
1113         // Testing that a task in one scheduler can block in foreign code
1114         // without affecting other schedulers
1115         do 20u.times {
1116             let (start_po, start_ch) = stream();
1117             let (fin_po, fin_ch) = stream();
1118
1119             let lock = testrt::rust_dbg_lock_create();
1120
1121             do spawn_sched(SingleThreaded) {
1122                 testrt::rust_dbg_lock_lock(lock);
1123
1124                 start_ch.send(());
1125
1126                 // Block the scheduler thread
1127                 testrt::rust_dbg_lock_wait(lock);
1128                 testrt::rust_dbg_lock_unlock(lock);
1129
1130                 fin_ch.send(());
1131             };
1132
1133             // Wait until the other task has its lock
1134             start_po.recv();
1135
1136             fn pingpong(po: &Port<int>, ch: &Chan<int>) {
1137                 let mut val = 20;
1138                 while val > 0 {
1139                     val = po.recv();
1140                     ch.send(val - 1);
1141                 }
1142             }
1143
1144             let (setup_po, setup_ch) = stream();
1145             let (parent_po, parent_ch) = stream();
1146             do spawn {
1147                 let (child_po, child_ch) = stream();
1148                 setup_ch.send(child_ch);
1149                 pingpong(&child_po, &parent_ch);
1150             };
1151
1152             let child_ch = setup_po.recv();
1153             child_ch.send(20);
1154             pingpong(&parent_po, &child_ch);
1155             testrt::rust_dbg_lock_lock(lock);
1156             testrt::rust_dbg_lock_signal(lock);
1157             testrt::rust_dbg_lock_unlock(lock);
1158             fin_po.recv();
1159             testrt::rust_dbg_lock_destroy(lock);
1160         }
1161     }
1162 }
1163
1164 #[cfg(test)]
1165 fn avoid_copying_the_body(spawnfn: &fn(v: ~fn())) {
1166     let (p, ch) = stream::<uint>();
1167
1168     let x = ~1;
1169     let x_in_parent = ptr::to_unsafe_ptr(&*x) as uint;
1170
1171     do spawnfn || {
1172         let x_in_child = ptr::to_unsafe_ptr(&*x) as uint;
1173         ch.send(x_in_child);
1174     }
1175
1176     let x_in_child = p.recv();
1177     assert_eq!(x_in_parent, x_in_child);
1178 }
1179
1180 #[test]
1181 fn test_avoid_copying_the_body_spawn() {
1182     avoid_copying_the_body(spawn);
1183 }
1184
1185 #[test]
1186 fn test_avoid_copying_the_body_task_spawn() {
1187     do avoid_copying_the_body |f| {
1188         let mut builder = task();
1189         do builder.spawn || {
1190             f();
1191         }
1192     }
1193 }
1194
1195 #[test]
1196 fn test_avoid_copying_the_body_try() {
1197     do avoid_copying_the_body |f| {
1198         do try || {
1199             f()
1200         };
1201     }
1202 }
1203
1204 #[test]
1205 fn test_avoid_copying_the_body_unlinked() {
1206     do avoid_copying_the_body |f| {
1207         do spawn_unlinked || {
1208             f();
1209         }
1210     }
1211 }
1212
1213 #[ignore(reason = "linked failure")]
1214 #[test]
1215 #[ignore(cfg(windows))]
1216 #[should_fail]
1217 fn test_unkillable() {
1218     let (po, ch) = stream();
1219
1220     // We want to do this after failing
1221     do spawn_unlinked {
1222         do 10.times { yield() }
1223         ch.send(());
1224     }
1225
1226     do spawn {
1227         yield();
1228         // We want to fail after the unkillable task
1229         // blocks on recv
1230         fail!();
1231     }
1232
1233     unsafe {
1234         do unkillable {
1235             let p = ~0;
1236             let pp: *uint = cast::transmute(p);
1237
1238             // If we are killed here then the box will leak
1239             po.recv();
1240
1241             let _p: ~int = cast::transmute(pp);
1242         }
1243     }
1244
1245     // Now we can be killed
1246     po.recv();
1247 }
1248
1249 #[ignore(reason = "linked failure")]
1250 #[test]
1251 #[ignore(cfg(windows))]
1252 #[should_fail]
1253 fn test_unkillable_nested() {
1254     let (po, ch) = comm::stream();
1255
1256     // We want to do this after failing
1257     do spawn_unlinked || {
1258         do 10.times { yield() }
1259         ch.send(());
1260     }
1261
1262     do spawn {
1263         yield();
1264         // We want to fail after the unkillable task
1265         // blocks on recv
1266         fail!();
1267     }
1268
1269     unsafe {
1270         do unkillable {
1271             do unkillable {} // Here's the difference from the previous test.
1272             let p = ~0;
1273             let pp: *uint = cast::transmute(p);
1274
1275             // If we are killed here then the box will leak
1276             po.recv();
1277
1278             let _p: ~int = cast::transmute(pp);
1279         }
1280     }
1281
1282     // Now we can be killed
1283     po.recv();
1284 }
1285
1286 #[test]
1287 fn test_child_doesnt_ref_parent() {
1288     // If the child refcounts the parent task, this will stack overflow when
1289     // climbing the task tree to dereference each ancestor. (See #1789)
1290     // (well, it would if the constant were 8000+ - I lowered it to be more
1291     // valgrind-friendly. try this at home, instead..!)
1292     static generations: uint = 16;
1293     fn child_no(x: uint) -> ~fn() {
1294         return || {
1295             if x < generations {
1296                 let mut t = task();
1297                 t.unwatched();
1298                 t.spawn(child_no(x+1));
1299             }
1300         }
1301     }
1302     let mut t = task();
1303     t.unwatched();
1304     t.spawn(child_no(0));
1305 }
1306
1307 #[test]
1308 fn test_simple_newsched_spawn() {
1309     use rt::test::run_in_newsched_task;
1310
1311     do run_in_newsched_task {
1312         spawn(||())
1313     }
1314 }
1315
1316 #[ignore(reason = "linked failure")]
1317 #[test] #[ignore(cfg(windows))]
1318 fn test_spawn_watched() {
1319     use rt::test::run_in_newsched_task;
1320     do run_in_newsched_task {
1321         let result = do try {
1322             let mut t = task();
1323             t.unlinked();
1324             t.watched();
1325             do t.spawn {
1326                 let mut t = task();
1327                 t.unlinked();
1328                 t.watched();
1329                 do t.spawn {
1330                     task::yield();
1331                     fail!();
1332                 }
1333             }
1334         };
1335         assert!(result.is_err());
1336     }
1337 }
1338
1339 #[ignore(reason = "linked failure")]
1340 #[test] #[ignore(cfg(windows))]
1341 fn test_indestructible() {
1342     use rt::test::run_in_newsched_task;
1343     do run_in_newsched_task {
1344         let result = do try {
1345             let mut t = task();
1346             t.watched();
1347             t.supervised();
1348             t.indestructible();
1349             do t.spawn {
1350                 let (p1, _c1) = stream::<()>();
1351                 let (p2, c2) = stream::<()>();
1352                 let (p3, c3) = stream::<()>();
1353                 let mut t = task();
1354                 t.unwatched();
1355                 do t.spawn {
1356                     do (|| {
1357                         p1.recv(); // would deadlock if not killed
1358                     }).finally {
1359                         c2.send(());
1360                     };
1361                 }
1362                 let mut t = task();
1363                 t.unwatched();
1364                 do t.spawn {
1365                     p3.recv();
1366                     task::yield();
1367                     fail!();
1368                 }
1369                 c3.send(());
1370                 p2.recv();
1371             }
1372         };
1373         assert!(result.is_ok());
1374     }
1375 }