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