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