1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
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.
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
24 * Tasks may execute in parallel and are scheduled automatically by the
31 * log(error, "Hello, World!");
36 #[allow(missing_doc)];
42 use comm::{stream, Chan, GenericChan, GenericPort, Port};
45 use rt::{context, OldTaskContext, TaskContext};
47 use unstable::finally::Finally;
50 #[cfg(test)] use cast;
51 #[cfg(test)] use comm::SharedChan;
52 #[cfg(test)] use comm;
54 #[cfg(test)] use task;
61 * Indicates the manner in which a task exited.
63 * A task that completes without failing is considered to exit successfully.
64 * Supervised ancestors and linked siblings may yet fail after this task
65 * succeeds. Also note that in such a case, it may be nondeterministic whether
66 * linked failure or successful exit happen first.
68 * If you wish for this result's delivery to block until all linked and/or
69 * children tasks complete, recommend using a result future.
80 /// Run task on the default scheduler
82 /// All tasks run in the same OS thread
87 * Scheduler configuration options
91 * * sched_mode - The operating mode of the scheduler
94 pub struct SchedOpts {
99 * Task configuration options
103 * * linked - Propagate failure bidirectionally between child and parent.
104 * True by default. If both this and 'supervised' are false, then
105 * either task's failure will not affect the other ("unlinked").
107 * * supervised - Propagate failure unidirectionally from parent to child,
108 * but not from child to parent. False by default.
110 * * watched - Make parent task collect exit status notifications from child
111 * before reporting its own exit status. (This delays the parent
112 * task's death and cleanup until after all transitively watched
113 * children also exit.) True by default.
115 * * indestructible - Configures the task to ignore kill signals received from
116 * linked failure. This may cause process hangs during
117 * failure if not used carefully, but causes task blocking
118 * code paths (e.g. port recv() calls) to be faster by 2
119 * atomic operations. False by default.
121 * * notify_chan - Enable lifecycle notifications on the given channel
123 * * name - A name for the task-to-be, for identification in failure messages.
125 * * sched - Specify the configuration of a new scheduler to create the task
128 * By default, every task is created in the same scheduler as its
129 * parent, where it is scheduled cooperatively with all other tasks
130 * in that scheduler. Some specialized applications may want more
131 * control over their scheduling, in which case they can be spawned
132 * into a new scheduler with the specific properties required.
134 * This is of particular importance for libraries which want to call
135 * into foreign code that blocks. Without doing so in a different
136 * scheduler other tasks will be impeded or even blocked indefinitely.
138 pub struct TaskOpts {
142 indestructible: bool,
143 notify_chan: Option<Chan<TaskResult>>,
146 stack_size: Option<uint>
150 * The task builder type.
152 * Provides detailed control over the properties and behavior of new tasks.
154 // NB: Builders are designed to be single-use because they do stateful
155 // things that get weird when reusing - e.g. if you create a result future
156 // it only applies to a single task, so then you have to maintain Some
157 // potentially tricky state to ensure that everything behaves correctly
158 // when you try to reuse the builder to spawn a new task. We'll just
159 // sidestep that whole issue by making builders uncopyable and making
160 // the run function move them in.
162 // FIXME (#3724): Replace the 'consumed' bit with move mode on self
163 pub struct TaskBuilder {
165 gen_body: Option<~fn(v: ~fn()) -> ~fn()>,
166 can_not_copy: Option<util::NonCopyable>,
171 * Generate the base configuration for spawning a task, off of which more
172 * configuration methods can be chained.
173 * For example, task().unlinked().spawn is equivalent to spawn_unlinked.
175 pub fn task() -> TaskBuilder {
177 opts: default_task_opts(),
185 fn consume(&mut self) -> TaskBuilder {
187 fail!("Cannot copy a task_builder"); // Fake move mode on self
189 self.consumed = true;
190 let gen_body = self.gen_body.take();
191 let notify_chan = self.opts.notify_chan.take();
192 let name = self.opts.name.take();
195 linked: self.opts.linked,
196 supervised: self.opts.supervised,
197 watched: self.opts.watched,
198 indestructible: self.opts.indestructible,
199 notify_chan: notify_chan,
201 sched: self.opts.sched,
202 stack_size: self.opts.stack_size
210 /// Decouple the child task's failure from the parent's. If either fails,
211 /// the other will not be killed.
212 pub fn unlinked(&mut self) {
213 self.opts.linked = false;
214 self.opts.watched = false;
217 /// Unidirectionally link the child task's failure with the parent's. The
218 /// child's failure will not kill the parent, but the parent's will kill
220 pub fn supervised(&mut self) {
221 self.opts.supervised = true;
222 self.opts.linked = false;
223 self.opts.watched = false;
226 /// Link the child task's and parent task's failures. If either fails, the
227 /// other will be killed.
228 pub fn linked(&mut self) {
229 self.opts.linked = true;
230 self.opts.supervised = false;
231 self.opts.watched = true;
234 /// Cause the parent task to collect the child's exit status (and that of
235 /// all transitively-watched grandchildren) before reporting its own.
236 pub fn watched(&mut self) {
237 self.opts.watched = true;
240 /// Allow the child task to outlive the parent task, at the possible cost
241 /// of the parent reporting success even if the child task fails later.
242 pub fn unwatched(&mut self) {
243 self.opts.watched = false;
246 /// Cause the child task to ignore any kill signals received from linked
247 /// failure. This optimizes context switching, at the possible expense of
248 /// process hangs in the case of unexpected failure.
249 pub fn indestructible(&mut self) {
250 self.opts.indestructible = true;
254 * Get a future representing the exit status of the task.
256 * Taking the value of the future will block until the child task
257 * terminates. The future-receiving callback specified will be called
258 * *before* the task is spawned; as such, do not invoke .get() within the
259 * closure; rather, store it in an outer variable/list for later use.
261 * Note that the future returning by this function is only useful for
262 * obtaining the value of the next task to be spawning with the
263 * builder. If additional tasks are spawned with the same builder
264 * then a new result future must be obtained prior to spawning each
268 * Fails if a future_result was already set for this task.
270 pub fn future_result(&mut self, blk: &fn(v: Port<TaskResult>)) {
271 // FIXME (#3725): Once linked failure and notification are
272 // handled in the library, I can imagine implementing this by just
273 // registering an arbitrary number of task::on_exit handlers and
274 // sending out messages.
276 if self.opts.notify_chan.is_some() {
277 fail!("Can't set multiple future_results for one task!");
280 // Construct the future and give it to the caller.
281 let (notify_pipe_po, notify_pipe_ch) = stream::<TaskResult>();
285 // Reconfigure self to use a notify channel.
286 self.opts.notify_chan = Some(notify_pipe_ch);
289 /// Name the task-to-be. Currently the name is used for identification
290 /// only in failure messages.
291 pub fn name(&mut self, name: ~str) {
292 self.opts.name = Some(name);
295 /// Configure a custom scheduler mode for the task.
296 pub fn sched_mode(&mut self, mode: SchedMode) {
297 self.opts.sched.mode = mode;
301 * Add a wrapper to the body of the spawned task.
303 * Before the task is spawned it is passed through a 'body generator'
304 * function that may perform local setup operations as well as wrap
305 * the task body in remote setup operations. With this the behavior
306 * of tasks can be extended in simple ways.
308 * This function augments the current body generator with a new body
309 * generator by applying the task body which results from the
310 * existing body generator to the new body generator.
312 pub fn add_wrapper(&mut self, wrapper: ~fn(v: ~fn()) -> ~fn()) {
313 let prev_gen_body = self.gen_body.take();
314 let prev_gen_body = match prev_gen_body {
317 let f: ~fn(~fn()) -> ~fn() = |body| body;
321 let prev_gen_body = Cell::new(prev_gen_body);
322 let next_gen_body = {
323 let f: ~fn(~fn()) -> ~fn() = |body| {
324 let prev_gen_body = prev_gen_body.take();
325 wrapper(prev_gen_body(body))
329 self.gen_body = Some(next_gen_body);
333 * Creates and executes a new child task
335 * Sets up a new task with its own call stack and schedules it to run
336 * the provided unique closure. The task has the properties and behavior
337 * specified by the task_builder.
341 * When spawning into a new scheduler, the number of threads requested
342 * must be greater than zero.
344 pub fn spawn(&mut self, f: ~fn()) {
345 let gen_body = self.gen_body.take();
346 let notify_chan = self.opts.notify_chan.take();
347 let name = self.opts.name.take();
348 let x = self.consume();
349 let opts = TaskOpts {
350 linked: x.opts.linked,
351 supervised: x.opts.supervised,
352 watched: x.opts.watched,
353 indestructible: x.opts.indestructible,
354 notify_chan: notify_chan,
357 stack_size: x.opts.stack_size
359 let f = match gen_body {
367 spawn::spawn_raw(opts, f);
370 /// Runs a task, while transfering ownership of one argument to the child.
371 pub fn spawn_with<A:Send>(&mut self, arg: A, f: ~fn(v: A)) {
372 let arg = Cell::new(arg);
379 * Execute a function in another task and return either the return value
380 * of the function or result::err.
384 * If the function executed successfully then try returns result::ok
385 * containing the value returned by the function. If the function fails
386 * then try returns result::err containing nil.
389 * Fails if a future_result was already set for this task.
391 pub fn try<T:Send>(&mut self, f: ~fn() -> T) -> Result<T,()> {
392 let (po, ch) = stream::<T>();
393 let mut result = None;
395 self.future_result(|r| { result = Some(r); });
401 match result.unwrap().recv() {
402 Success => result::Ok(po.recv()),
403 Failure => result::Err(())
409 /* Task construction */
411 pub fn default_task_opts() -> TaskOpts {
413 * The default task options
415 * By default all tasks are supervised by their parent, are spawned
416 * into the same scheduler, and do not post lifecycle notifications.
423 indestructible: false,
427 mode: DefaultScheduler,
433 /* Spawn convenience functions */
435 /// Creates and executes a new child task
437 /// Sets up a new task with its own call stack and schedules it to run
438 /// the provided unique closure.
440 /// This function is equivalent to `task().spawn(f)`.
441 pub fn spawn(f: ~fn()) {
442 let mut task = task();
446 /// Creates a child task unlinked from the current one. If either this
447 /// task or the child task fails, the other will not be killed.
448 pub fn spawn_unlinked(f: ~fn()) {
449 let mut task = task();
454 pub fn spawn_supervised(f: ~fn()) {
456 * Creates a child task supervised by the current one. If the child
457 * task fails, the parent will not be killed, but if the parent fails,
458 * the child will be killed.
461 let mut task = task();
466 /// Creates a child task that cannot be killed by linked failure. This causes
467 /// its context-switch path to be faster by 2 atomic swap operations.
468 /// (Note that this convenience wrapper still uses linked-failure, so the
469 /// child's children will still be killable by the parent. For the fastest
470 /// possible spawn mode, use task::task().unlinked().indestructible().spawn.)
471 pub fn spawn_indestructible(f: ~fn()) {
472 let mut task = task();
473 task.indestructible();
477 pub fn spawn_with<A:Send>(arg: A, f: ~fn(v: A)) {
479 * Runs a task, while transfering ownership of one argument to the
482 * This is useful for transfering ownership of noncopyables to
485 * This function is equivalent to `task().spawn_with(arg, f)`.
488 let mut task = task();
489 task.spawn_with(arg, f)
492 pub fn spawn_sched(mode: SchedMode, f: ~fn()) {
494 * Creates a new task on a new or existing scheduler
496 * When there are no more tasks to execute the
497 * scheduler terminates.
501 * In manual threads mode the number of threads requested must be
505 let mut task = task();
506 task.sched_mode(mode);
510 pub fn try<T:Send>(f: ~fn() -> T) -> Result<T,()> {
512 * Execute a function in another task and return either the return value
513 * of the function or result::err.
515 * This is equivalent to task().supervised().try.
518 let mut task = task();
524 /* Lifecycle functions */
526 /// Read the name of the current task.
527 pub fn with_task_name<U>(blk: &fn(Option<&str>) -> U) -> U {
531 TaskContext => do Local::borrow::<Task, U> |task| {
533 Some(ref name) => blk(Some(name.as_slice())),
537 _ => fail!("no task name exists in %?", context()),
542 //! Yield control to the task scheduler
544 use rt::{context, OldTaskContext};
545 use rt::local::Local;
546 use rt::sched::Scheduler;
551 let task_ = rt::rust_get_task();
552 let killed = rt::rust_task_yield(task_);
553 if killed && !failing() {
558 // XXX: What does yield really mean in newsched?
559 // FIXME(#7544): Optimize this, since we know we won't block.
560 let sched = Local::take::<Scheduler>();
561 do sched.deschedule_running_task_and_then |sched, task| {
562 sched.enqueue_blocked_task(task);
569 pub fn failing() -> bool {
570 //! True if the running task has failed
577 rt::rust_task_is_unwinding(rt::rust_get_task())
581 do Local::borrow::<Task, bool> |local| {
582 local.unwinder.unwinding
589 * Temporarily make the task unkillable
594 * do task::unkillable {
595 * // detach / yield / destroy must all be called together
596 * rustrt::rust_port_detach(po);
597 * // This must not result in the current task being killed
599 * rustrt::rust_port_destroy(po);
603 pub fn unkillable<U>(f: &fn() -> U) -> U {
609 let t = rt::rust_get_task();
611 rt::rust_task_inhibit_kill(t);
614 rt::rust_task_allow_kill(t);
618 // The inhibits/allows might fail and need to borrow the task.
619 let t = Local::unsafe_borrow::<Task>();
621 (*t).death.inhibit_kill((*t).unwinder.unwinding);
624 (*t).death.allow_kill((*t).unwinder.unwinding);
627 // FIXME(#3095): This should be an rtabort as soon as the scheduler
628 // no longer uses a workqueue implemented with an Exclusive.
634 /// The inverse of unkillable. Only ever to be used nested in unkillable().
635 pub unsafe fn rekillable<U>(f: &fn() -> U) -> U {
640 let t = rt::rust_get_task();
642 rt::rust_task_allow_kill(t);
645 rt::rust_task_inhibit_kill(t);
649 let t = Local::unsafe_borrow::<Task>();
651 (*t).death.allow_kill((*t).unwinder.unwinding);
654 (*t).death.inhibit_kill((*t).unwinder.unwinding);
657 // FIXME(#3095): As in unkillable().
662 #[ignore(reason = "linked failure")]
663 #[test] #[ignore(cfg(windows))]
664 fn test_kill_unkillable_task() {
667 // Attempt to test that when a kill signal is received at the start of an
668 // unkillable section, 'unkillable' unwinds correctly. This is actually
669 // quite a difficult race to expose, as the kill has to happen on a second
670 // CPU, *after* the spawner is already switched-back-to (and passes the
671 // killed check at the start of its timeslice). As far as I know, it's not
672 // possible to make this race deterministic, or even more likely to happen.
673 do run_in_newsched_task {
678 do task::unkillable { }
683 #[ignore(reason = "linked failure")]
684 #[test] #[ignore(cfg(windows))]
685 fn test_kill_rekillable_task() {
688 // Tests that when a kill signal is received, 'rekillable' and
689 // 'unkillable' unwind correctly in conjunction with each other.
690 do run_in_newsched_task {
692 do task::unkillable {
694 do task::rekillable {
705 #[test] #[should_fail] #[ignore(cfg(windows))]
706 fn test_cant_dup_task_builder() {
707 let mut builder = task();
710 // FIXME(#3724): For now, this is a -runtime- failure, because we haven't
711 // got move mode on self. When 3724 is fixed, this test should fail to
712 // compile instead, and should go in tests/compile-fail.
713 do builder.spawn {} // b should have been consumed by the previous call
716 // The following 8 tests test the following 2^3 combinations:
717 // {un,}linked {un,}supervised failure propagation {up,down}wards.
719 // !!! These tests are dangerous. If Something is buggy, they will hang, !!!
720 // !!! instead of exiting cleanly. This might wedge the buildbots. !!!
723 fn block_forever() { let (po, _ch) = stream::<()>(); po.recv(); }
725 #[ignore(reason = "linked failure")]
726 #[test] #[ignore(cfg(windows))]
727 fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port
728 use rt::test::run_in_newsched_task;
729 do run_in_newsched_task {
730 let (po, ch) = stream();
731 let ch = SharedChan::new(ch);
735 // Give middle task a chance to fail-but-not-kill-us.
736 do 16.times { task::yield(); }
737 ch.send(()); // If killed first, grandparent hangs.
739 fail!(); // Shouldn't kill either (grand)parent or (grand)child.
744 #[ignore(reason = "linked failure")]
745 #[test] #[ignore(cfg(windows))]
746 fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails
747 use rt::test::run_in_newsched_task;
748 do run_in_newsched_task {
749 do spawn_unlinked { fail!(); }
752 #[ignore(reason = "linked failure")]
753 #[test] #[ignore(cfg(windows))]
754 fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
755 use rt::test::run_in_newsched_task;
756 do run_in_newsched_task {
757 do spawn_supervised { fail!(); }
758 // Give child a chance to fail-but-not-kill-us.
759 do 16.times { task::yield(); }
762 #[ignore(reason = "linked failure")]
763 #[test] #[ignore(cfg(windows))]
764 fn test_spawn_unlinked_sup_fail_down() {
765 use rt::test::run_in_newsched_task;
766 do run_in_newsched_task {
767 let result: Result<(),()> = do try {
768 do spawn_supervised { block_forever(); }
769 fail!(); // Shouldn't leave a child hanging around.
771 assert!(result.is_err());
775 #[ignore(reason = "linked failure")]
776 #[test] #[ignore(cfg(windows))]
777 fn test_spawn_linked_sup_fail_up() { // child fails; parent fails
778 use rt::test::run_in_newsched_task;
779 do run_in_newsched_task {
780 let result: Result<(),()> = do try {
781 // Unidirectional "parenting" shouldn't override bidirectional linked.
782 // We have to cheat with opts - the interface doesn't support them because
783 // they don't make sense (redundant with task().supervised()).
785 b0.opts.linked = true;
786 b0.opts.supervised = true;
791 block_forever(); // We should get punted awake
793 assert!(result.is_err());
796 #[ignore(reason = "linked failure")]
797 #[test] #[ignore(cfg(windows))]
798 fn test_spawn_linked_sup_fail_down() { // parent fails; child fails
799 use rt::test::run_in_newsched_task;
800 do run_in_newsched_task {
801 let result: Result<(),()> = do try {
802 // We have to cheat with opts - the interface doesn't support them because
803 // they don't make sense (redundant with task().supervised()).
805 b0.opts.linked = true;
806 b0.opts.supervised = true;
807 do b0.spawn { block_forever(); }
808 fail!(); // *both* mechanisms would be wrong if this didn't kill the child
810 assert!(result.is_err());
813 #[ignore(reason = "linked failure")]
814 #[test] #[ignore(cfg(windows))]
815 fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails
816 use rt::test::run_in_newsched_task;
817 do run_in_newsched_task {
818 let result: Result<(),()> = do try {
819 // Default options are to spawn linked & unsupervised.
820 do spawn { fail!(); }
821 block_forever(); // We should get punted awake
823 assert!(result.is_err());
826 #[ignore(reason = "linked failure")]
827 #[test] #[ignore(cfg(windows))]
828 fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails
829 use rt::test::run_in_newsched_task;
830 do run_in_newsched_task {
831 let result: Result<(),()> = do try {
832 // Default options are to spawn linked & unsupervised.
833 do spawn { block_forever(); }
836 assert!(result.is_err());
839 #[ignore(reason = "linked failure")]
840 #[test] #[ignore(cfg(windows))]
841 fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails
842 use rt::test::run_in_newsched_task;
843 do run_in_newsched_task {
844 let result: Result<(),()> = do try {
845 // Make sure the above test is the same as this one.
846 let mut builder = task();
848 do builder.spawn { block_forever(); }
851 assert!(result.is_err());
855 // A couple bonus linked failure tests - testing for failure propagation even
856 // when the middle task exits successfully early before kill signals are sent.
858 #[ignore(reason = "linked failure")]
859 #[test] #[ignore(cfg(windows))]
860 fn test_spawn_failure_propagate_grandchild() {
861 use rt::test::run_in_newsched_task;
862 do run_in_newsched_task {
863 let result: Result<(),()> = do try {
864 // Middle task exits; does grandparent's failure propagate across the gap?
865 do spawn_supervised {
866 do spawn_supervised { block_forever(); }
868 do 16.times { task::yield(); }
871 assert!(result.is_err());
875 #[ignore(reason = "linked failure")]
876 #[test] #[ignore(cfg(windows))]
877 fn test_spawn_failure_propagate_secondborn() {
878 use rt::test::run_in_newsched_task;
879 do run_in_newsched_task {
880 let result: Result<(),()> = do try {
881 // First-born child exits; does parent's failure propagate to sibling?
882 do spawn_supervised {
883 do spawn { block_forever(); } // linked
885 do 16.times { task::yield(); }
888 assert!(result.is_err());
892 #[ignore(reason = "linked failure")]
893 #[test] #[ignore(cfg(windows))]
894 fn test_spawn_failure_propagate_nephew_or_niece() {
895 use rt::test::run_in_newsched_task;
896 do run_in_newsched_task {
897 let result: Result<(),()> = do try {
898 // Our sibling exits; does our failure propagate to sibling's child?
900 do spawn_supervised { block_forever(); }
902 do 16.times { task::yield(); }
905 assert!(result.is_err());
909 #[ignore(reason = "linked failure")]
910 #[test] #[ignore(cfg(windows))]
911 fn test_spawn_linked_sup_propagate_sibling() {
912 use rt::test::run_in_newsched_task;
913 do run_in_newsched_task {
914 let result: Result<(),()> = do try {
915 // Middle sibling exits - does eldest's failure propagate to youngest?
917 do spawn { block_forever(); } // linked
919 do 16.times { task::yield(); }
922 assert!(result.is_err());
927 fn test_unnamed_task() {
928 use rt::test::run_in_newsched_task;
930 do run_in_newsched_task {
932 do with_task_name |name| {
933 assert!(name.is_none());
940 fn test_named_task() {
941 use rt::test::run_in_newsched_task;
943 do run_in_newsched_task {
945 t.name(~"ada lovelace");
947 do with_task_name |name| {
948 assert!(name.unwrap() == "ada lovelace");
955 fn test_run_basic() {
956 let (po, ch) = stream::<()>();
957 let mut builder = task();
970 fn test_add_wrapper() {
971 let (po, ch) = stream::<()>();
973 let ch = Cell::new(ch);
974 do b0.add_wrapper |body| {
975 let ch = Cell::new(ch.take());
976 let result: ~fn() = || {
988 #[ignore(cfg(windows))]
989 fn test_future_result() {
990 let mut result = None;
991 let mut builder = task();
992 builder.future_result(|r| result = Some(r));
994 assert_eq!(result.unwrap().recv(), Success);
997 let mut builder = task();
998 builder.future_result(|r| result = Some(r));
1003 assert_eq!(result.unwrap().recv(), Failure);
1006 #[test] #[should_fail] #[ignore(cfg(windows))]
1007 fn test_back_to_the_future_result() {
1008 let mut builder = task();
1009 builder.future_result(util::ignore);
1010 builder.future_result(util::ignore);
1014 fn test_try_success() {
1018 result::Ok(~"Success!") => (),
1024 #[ignore(cfg(windows))]
1025 fn test_try_fail() {
1029 result::Err(()) => (),
1030 result::Ok(()) => fail!()
1035 fn get_sched_id() -> int {
1036 if context() == OldTaskContext {
1038 rt::rust_get_sched_id() as int
1041 do Local::borrow::<::rt::sched::Scheduler, int> |sched| {
1042 sched.sched_id() as int
1048 fn test_spawn_sched() {
1049 let (po, ch) = stream::<()>();
1050 let ch = SharedChan::new(ch);
1052 fn f(i: int, ch: SharedChan<()>) {
1053 let parent_sched_id = get_sched_id();
1055 do spawn_sched(SingleThreaded) {
1056 let child_sched_id = get_sched_id();
1057 assert!(parent_sched_id != child_sched_id);
1062 f(i - 1, ch.clone());
1072 fn test_spawn_sched_childs_on_default_sched() {
1073 let (po, ch) = stream();
1075 // Assuming tests run on the default scheduler
1076 let default_id = get_sched_id();
1078 let ch = Cell::new(ch);
1079 do spawn_sched(SingleThreaded) {
1080 let parent_sched_id = get_sched_id();
1081 let ch = Cell::new(ch.take());
1084 let child_sched_id = get_sched_id();
1085 assert!(parent_sched_id != child_sched_id);
1086 assert_eq!(child_sched_id, default_id);
1100 pub fn rust_dbg_lock_create() -> *libc::c_void;
1101 pub fn rust_dbg_lock_destroy(lock: *libc::c_void);
1102 pub fn rust_dbg_lock_lock(lock: *libc::c_void);
1103 pub fn rust_dbg_lock_unlock(lock: *libc::c_void);
1104 pub fn rust_dbg_lock_wait(lock: *libc::c_void);
1105 pub fn rust_dbg_lock_signal(lock: *libc::c_void);
1110 fn test_spawn_sched_blocking() {
1113 // Testing that a task in one scheduler can block in foreign code
1114 // without affecting other schedulers
1116 let (start_po, start_ch) = stream();
1117 let (fin_po, fin_ch) = stream();
1119 let lock = testrt::rust_dbg_lock_create();
1121 do spawn_sched(SingleThreaded) {
1122 testrt::rust_dbg_lock_lock(lock);
1126 // Block the scheduler thread
1127 testrt::rust_dbg_lock_wait(lock);
1128 testrt::rust_dbg_lock_unlock(lock);
1133 // Wait until the other task has its lock
1136 fn pingpong(po: &Port<int>, ch: &Chan<int>) {
1144 let (setup_po, setup_ch) = stream();
1145 let (parent_po, parent_ch) = stream();
1147 let (child_po, child_ch) = stream();
1148 setup_ch.send(child_ch);
1149 pingpong(&child_po, &parent_ch);
1152 let child_ch = setup_po.recv();
1154 pingpong(&parent_po, &child_ch);
1155 testrt::rust_dbg_lock_lock(lock);
1156 testrt::rust_dbg_lock_signal(lock);
1157 testrt::rust_dbg_lock_unlock(lock);
1159 testrt::rust_dbg_lock_destroy(lock);
1165 fn avoid_copying_the_body(spawnfn: &fn(v: ~fn())) {
1166 let (p, ch) = stream::<uint>();
1169 let x_in_parent = ptr::to_unsafe_ptr(&*x) as uint;
1172 let x_in_child = ptr::to_unsafe_ptr(&*x) as uint;
1173 ch.send(x_in_child);
1176 let x_in_child = p.recv();
1177 assert_eq!(x_in_parent, x_in_child);
1181 fn test_avoid_copying_the_body_spawn() {
1182 avoid_copying_the_body(spawn);
1186 fn test_avoid_copying_the_body_task_spawn() {
1187 do avoid_copying_the_body |f| {
1188 let mut builder = task();
1189 do builder.spawn || {
1196 fn test_avoid_copying_the_body_try() {
1197 do avoid_copying_the_body |f| {
1205 fn test_avoid_copying_the_body_unlinked() {
1206 do avoid_copying_the_body |f| {
1207 do spawn_unlinked || {
1213 #[ignore(reason = "linked failure")]
1215 #[ignore(cfg(windows))]
1217 fn test_unkillable() {
1218 let (po, ch) = stream();
1220 // We want to do this after failing
1222 do 10.times { yield() }
1228 // We want to fail after the unkillable task
1236 let pp: *uint = cast::transmute(p);
1238 // If we are killed here then the box will leak
1241 let _p: ~int = cast::transmute(pp);
1245 // Now we can be killed
1249 #[ignore(reason = "linked failure")]
1251 #[ignore(cfg(windows))]
1253 fn test_unkillable_nested() {
1254 let (po, ch) = comm::stream();
1256 // We want to do this after failing
1257 do spawn_unlinked || {
1258 do 10.times { yield() }
1264 // We want to fail after the unkillable task
1271 do unkillable {} // Here's the difference from the previous test.
1273 let pp: *uint = cast::transmute(p);
1275 // If we are killed here then the box will leak
1278 let _p: ~int = cast::transmute(pp);
1282 // Now we can be killed
1287 fn test_child_doesnt_ref_parent() {
1288 // If the child refcounts the parent task, this will stack overflow when
1289 // climbing the task tree to dereference each ancestor. (See #1789)
1290 // (well, it would if the constant were 8000+ - I lowered it to be more
1291 // valgrind-friendly. try this at home, instead..!)
1292 static generations: uint = 16;
1293 fn child_no(x: uint) -> ~fn() {
1295 if x < generations {
1298 t.spawn(child_no(x+1));
1304 t.spawn(child_no(0));
1308 fn test_simple_newsched_spawn() {
1309 use rt::test::run_in_newsched_task;
1311 do run_in_newsched_task {
1316 #[ignore(reason = "linked failure")]
1317 #[test] #[ignore(cfg(windows))]
1318 fn test_spawn_watched() {
1319 use rt::test::run_in_newsched_task;
1320 do run_in_newsched_task {
1321 let result = do try {
1335 assert!(result.is_err());
1339 #[ignore(reason = "linked failure")]
1340 #[test] #[ignore(cfg(windows))]
1341 fn test_indestructible() {
1342 use rt::test::run_in_newsched_task;
1343 do run_in_newsched_task {
1344 let result = do try {
1350 let (p1, _c1) = stream::<()>();
1351 let (p2, c2) = stream::<()>();
1352 let (p3, c3) = stream::<()>();
1357 p1.recv(); // would deadlock if not killed
1373 assert!(result.is_ok());