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