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)];
41 use comm::{stream, Chan, GenericChan, GenericPort, Port};
44 use rt::in_green_task_context;
46 use unstable::finally::Finally;
49 #[cfg(test)] use cast;
50 #[cfg(test)] use comm::SharedChan;
51 #[cfg(test)] use comm;
53 #[cfg(test)] use task;
59 * Indicates the manner in which a task exited.
61 * A task that completes without failing is considered to exit successfully.
62 * Supervised ancestors and linked siblings may yet fail after this task
63 * succeeds. Also note that in such a case, it may be nondeterministic whether
64 * linked failure or successful exit happen first.
66 * If you wish for this result's delivery to block until all linked and/or
67 * children tasks complete, recommend using a result future.
78 /// Run task on the default scheduler
80 /// All tasks run in the same OS thread
85 * Scheduler configuration options
89 * * sched_mode - The operating mode of the scheduler
92 pub struct SchedOpts {
97 * Task configuration options
101 * * linked - Propagate failure bidirectionally between child and parent.
102 * True by default. If both this and 'supervised' are false, then
103 * either task's failure will not affect the other ("unlinked").
105 * * supervised - Propagate failure unidirectionally from parent to child,
106 * but not from child to parent. False by default.
108 * * watched - Make parent task collect exit status notifications from child
109 * before reporting its own exit status. (This delays the parent
110 * task's death and cleanup until after all transitively watched
111 * children also exit.) True by default.
113 * * indestructible - Configures the task to ignore kill signals received from
114 * linked failure. This may cause process hangs during
115 * failure if not used carefully, but causes task blocking
116 * code paths (e.g. port recv() calls) to be faster by 2
117 * atomic operations. False by default.
119 * * notify_chan - Enable lifecycle notifications on the given channel
121 * * name - A name for the task-to-be, for identification in failure messages.
123 * * sched - Specify the configuration of a new scheduler to create the task
126 * By default, every task is created in the same scheduler as its
127 * parent, where it is scheduled cooperatively with all other tasks
128 * in that scheduler. Some specialized applications may want more
129 * control over their scheduling, in which case they can be spawned
130 * into a new scheduler with the specific properties required.
132 * This is of particular importance for libraries which want to call
133 * into foreign code that blocks. Without doing so in a different
134 * scheduler other tasks will be impeded or even blocked indefinitely.
136 pub struct TaskOpts {
140 indestructible: bool,
141 notify_chan: Option<Chan<TaskResult>>,
144 stack_size: Option<uint>
148 * The task builder type.
150 * Provides detailed control over the properties and behavior of new tasks.
152 // NB: Builders are designed to be single-use because they do stateful
153 // things that get weird when reusing - e.g. if you create a result future
154 // it only applies to a single task, so then you have to maintain Some
155 // potentially tricky state to ensure that everything behaves correctly
156 // when you try to reuse the builder to spawn a new task. We'll just
157 // sidestep that whole issue by making builders uncopyable and making
158 // the run function move them in.
160 // FIXME (#3724): Replace the 'consumed' bit with move mode on self
161 pub struct TaskBuilder {
163 gen_body: Option<~fn(v: ~fn()) -> ~fn()>,
164 can_not_copy: Option<util::NonCopyable>,
169 * Generate the base configuration for spawning a task, off of which more
170 * configuration methods can be chained.
171 * For example, task().unlinked().spawn is equivalent to spawn_unlinked.
173 pub fn task() -> TaskBuilder {
175 opts: default_task_opts(),
183 fn consume(&mut self) -> TaskBuilder {
185 fail!("Cannot copy a task_builder"); // Fake move mode on self
187 self.consumed = true;
188 let gen_body = self.gen_body.take();
189 let notify_chan = self.opts.notify_chan.take();
190 let name = self.opts.name.take();
193 linked: self.opts.linked,
194 supervised: self.opts.supervised,
195 watched: self.opts.watched,
196 indestructible: self.opts.indestructible,
197 notify_chan: notify_chan,
199 sched: self.opts.sched,
200 stack_size: self.opts.stack_size
208 /// Decouple the child task's failure from the parent's. If either fails,
209 /// the other will not be killed.
210 pub fn unlinked(&mut self) {
211 self.opts.linked = false;
212 self.opts.watched = false;
215 /// Unidirectionally link the child task's failure with the parent's. The
216 /// child's failure will not kill the parent, but the parent's will kill
218 pub fn supervised(&mut self) {
219 self.opts.supervised = true;
220 self.opts.linked = false;
221 self.opts.watched = false;
224 /// Link the child task's and parent task's failures. If either fails, the
225 /// other will be killed.
226 pub fn linked(&mut self) {
227 self.opts.linked = true;
228 self.opts.supervised = false;
229 self.opts.watched = true;
232 /// Cause the parent task to collect the child's exit status (and that of
233 /// all transitively-watched grandchildren) before reporting its own.
234 pub fn watched(&mut self) {
235 self.opts.watched = true;
238 /// Allow the child task to outlive the parent task, at the possible cost
239 /// of the parent reporting success even if the child task fails later.
240 pub fn unwatched(&mut self) {
241 self.opts.watched = false;
244 /// Cause the child task to ignore any kill signals received from linked
245 /// failure. This optimizes context switching, at the possible expense of
246 /// process hangs in the case of unexpected failure.
247 pub fn indestructible(&mut self) {
248 self.opts.indestructible = true;
252 * Get a future representing the exit status of the task.
254 * Taking the value of the future will block until the child task
255 * terminates. The future-receiving callback specified will be called
256 * *before* the task is spawned; as such, do not invoke .get() within the
257 * closure; rather, store it in an outer variable/list for later use.
259 * Note that the future returning by this function is only useful for
260 * obtaining the value of the next task to be spawning with the
261 * builder. If additional tasks are spawned with the same builder
262 * then a new result future must be obtained prior to spawning each
266 * Fails if a future_result was already set for this task.
268 pub fn future_result(&mut self, blk: &fn(v: Port<TaskResult>)) {
269 // FIXME (#3725): Once linked failure and notification are
270 // handled in the library, I can imagine implementing this by just
271 // registering an arbitrary number of task::on_exit handlers and
272 // sending out messages.
274 if self.opts.notify_chan.is_some() {
275 fail!("Can't set multiple future_results for one task!");
278 // Construct the future and give it to the caller.
279 let (notify_pipe_po, notify_pipe_ch) = stream::<TaskResult>();
283 // Reconfigure self to use a notify channel.
284 self.opts.notify_chan = Some(notify_pipe_ch);
287 /// Name the task-to-be. Currently the name is used for identification
288 /// only in failure messages.
289 pub fn name(&mut self, name: ~str) {
290 self.opts.name = Some(name);
293 /// Configure a custom scheduler mode for the task.
294 pub fn sched_mode(&mut self, mode: SchedMode) {
295 self.opts.sched.mode = mode;
299 * Add a wrapper to the body of the spawned task.
301 * Before the task is spawned it is passed through a 'body generator'
302 * function that may perform local setup operations as well as wrap
303 * the task body in remote setup operations. With this the behavior
304 * of tasks can be extended in simple ways.
306 * This function augments the current body generator with a new body
307 * generator by applying the task body which results from the
308 * existing body generator to the new body generator.
310 pub fn add_wrapper(&mut self, wrapper: ~fn(v: ~fn()) -> ~fn()) {
311 let prev_gen_body = self.gen_body.take();
312 let prev_gen_body = match prev_gen_body {
315 let f: ~fn(~fn()) -> ~fn() = |body| body;
319 let prev_gen_body = Cell::new(prev_gen_body);
320 let next_gen_body = {
321 let f: ~fn(~fn()) -> ~fn() = |body| {
322 let prev_gen_body = prev_gen_body.take();
323 wrapper(prev_gen_body(body))
327 self.gen_body = Some(next_gen_body);
331 * Creates and executes a new child task
333 * Sets up a new task with its own call stack and schedules it to run
334 * the provided unique closure. The task has the properties and behavior
335 * specified by the task_builder.
339 * When spawning into a new scheduler, the number of threads requested
340 * must be greater than zero.
342 pub fn spawn(&mut self, f: ~fn()) {
343 let gen_body = self.gen_body.take();
344 let notify_chan = self.opts.notify_chan.take();
345 let name = self.opts.name.take();
346 let x = self.consume();
347 let opts = TaskOpts {
348 linked: x.opts.linked,
349 supervised: x.opts.supervised,
350 watched: x.opts.watched,
351 indestructible: x.opts.indestructible,
352 notify_chan: notify_chan,
355 stack_size: x.opts.stack_size
357 let f = match gen_body {
365 spawn::spawn_raw(opts, f);
368 /// Runs a task, while transferring ownership of one argument to the child.
369 pub fn spawn_with<A:Send>(&mut self, arg: A, f: ~fn(v: A)) {
370 let arg = Cell::new(arg);
377 * Execute a function in another task and return either the return value
378 * of the function or result::err.
382 * If the function executed successfully then try returns result::ok
383 * containing the value returned by the function. If the function fails
384 * then try returns result::err containing nil.
387 * Fails if a future_result was already set for this task.
389 pub fn try<T:Send>(&mut self, f: ~fn() -> T) -> Result<T,()> {
390 let (po, ch) = stream::<T>();
391 let mut result = None;
393 self.future_result(|r| { result = Some(r); });
399 match result.unwrap().recv() {
400 Success => result::Ok(po.recv()),
401 Failure => result::Err(())
407 /* Task construction */
409 pub fn default_task_opts() -> TaskOpts {
411 * The default task options
413 * By default all tasks are supervised by their parent, are spawned
414 * into the same scheduler, and do not post lifecycle notifications.
421 indestructible: false,
425 mode: DefaultScheduler,
431 /* Spawn convenience functions */
433 /// Creates and executes a new child task
435 /// Sets up a new task with its own call stack and schedules it to run
436 /// the provided unique closure.
438 /// This function is equivalent to `task().spawn(f)`.
439 pub fn spawn(f: ~fn()) {
440 let mut task = task();
444 /// Creates a child task unlinked from the current one. If either this
445 /// task or the child task fails, the other will not be killed.
446 pub fn spawn_unlinked(f: ~fn()) {
447 let mut task = task();
452 pub fn spawn_supervised(f: ~fn()) {
454 * Creates a child task supervised by the current one. If the child
455 * task fails, the parent will not be killed, but if the parent fails,
456 * the child will be killed.
459 let mut task = task();
464 /// Creates a child task that cannot be killed by linked failure. This causes
465 /// its context-switch path to be faster by 2 atomic swap operations.
466 /// (Note that this convenience wrapper still uses linked-failure, so the
467 /// child's children will still be killable by the parent. For the fastest
468 /// possible spawn mode, use task::task().unlinked().indestructible().spawn.)
469 pub fn spawn_indestructible(f: ~fn()) {
470 let mut task = task();
471 task.indestructible();
475 pub fn spawn_with<A:Send>(arg: A, f: ~fn(v: A)) {
477 * Runs a task, while transferring ownership of one argument to the
480 * This is useful for transferring ownership of noncopyables to
483 * This function is equivalent to `task().spawn_with(arg, f)`.
486 let mut task = task();
487 task.spawn_with(arg, f)
490 pub fn spawn_sched(mode: SchedMode, f: ~fn()) {
492 * Creates a new task on a new or existing scheduler
494 * When there are no more tasks to execute the
495 * scheduler terminates.
499 * In manual threads mode the number of threads requested must be
503 let mut task = task();
504 task.sched_mode(mode);
508 pub fn try<T:Send>(f: ~fn() -> T) -> Result<T,()> {
510 * Execute a function in another task and return either the return value
511 * of the function or result::err.
513 * This is equivalent to task().supervised().try.
516 let mut task = task();
522 /* Lifecycle functions */
524 /// Read the name of the current task.
525 pub fn with_task_name<U>(blk: &fn(Option<&str>) -> U) -> U {
528 if in_green_task_context() {
529 do Local::borrow::<Task, U> |task| {
531 Some(ref name) => blk(Some(name.as_slice())),
536 fail!("no task name exists in non-green task context")
540 pub fn deschedule() {
541 //! Yield control to the task scheduler
543 use rt::local::Local;
544 use rt::sched::Scheduler;
546 // XXX: What does yield really mean in newsched?
547 // FIXME(#7544): Optimize this, since we know we won't block.
548 let sched = Local::take::<Scheduler>();
549 do sched.deschedule_running_task_and_then |sched, task| {
550 sched.enqueue_blocked_task(task);
554 pub fn failing() -> bool {
555 //! True if the running task has failed
559 do Local::borrow::<Task, bool> |local| {
560 local.unwinder.unwinding
565 * Temporarily make the task unkillable
570 * do task::unkillable {
571 * // detach / deschedule / destroy must all be called together
572 * rustrt::rust_port_detach(po);
573 * // This must not result in the current task being killed
574 * task::deschedule();
575 * rustrt::rust_port_destroy(po);
579 pub fn unkillable<U>(f: &fn() -> U) -> U {
583 if in_green_task_context() {
584 // The inhibits/allows might fail and need to borrow the task.
585 let t = Local::unsafe_borrow::<Task>();
587 (*t).death.inhibit_kill((*t).unwinder.unwinding);
590 (*t).death.allow_kill((*t).unwinder.unwinding);
593 // FIXME(#3095): This should be an rtabort as soon as the scheduler
594 // no longer uses a workqueue implemented with an Exclusive.
600 /// The inverse of unkillable. Only ever to be used nested in unkillable().
601 pub unsafe fn rekillable<U>(f: &fn() -> U) -> U {
604 if in_green_task_context() {
605 let t = Local::unsafe_borrow::<Task>();
607 (*t).death.allow_kill((*t).unwinder.unwinding);
610 (*t).death.inhibit_kill((*t).unwinder.unwinding);
613 // FIXME(#3095): As in unkillable().
618 #[ignore(reason = "linked failure")]
619 #[test] #[ignore(cfg(windows))]
620 fn test_kill_unkillable_task() {
623 // Attempt to test that when a kill signal is received at the start of an
624 // unkillable section, 'unkillable' unwinds correctly. This is actually
625 // quite a difficult race to expose, as the kill has to happen on a second
626 // CPU, *after* the spawner is already switched-back-to (and passes the
627 // killed check at the start of its timeslice). As far as I know, it's not
628 // possible to make this race deterministic, or even more likely to happen.
629 do run_in_newsched_task {
634 do task::unkillable { }
639 #[ignore(reason = "linked failure")]
640 #[test] #[ignore(cfg(windows))]
641 fn test_kill_rekillable_task() {
644 // Tests that when a kill signal is received, 'rekillable' and
645 // 'unkillable' unwind correctly in conjunction with each other.
646 do run_in_newsched_task {
648 do task::unkillable {
650 do task::rekillable {
661 #[test] #[should_fail] #[ignore(cfg(windows))]
662 fn test_cant_dup_task_builder() {
663 let mut builder = task();
666 // FIXME(#3724): For now, this is a -runtime- failure, because we haven't
667 // got move mode on self. When 3724 is fixed, this test should fail to
668 // compile instead, and should go in tests/compile-fail.
669 do builder.spawn {} // b should have been consumed by the previous call
672 // The following 8 tests test the following 2^3 combinations:
673 // {un,}linked {un,}supervised failure propagation {up,down}wards.
675 // !!! These tests are dangerous. If Something is buggy, they will hang, !!!
676 // !!! instead of exiting cleanly. This might wedge the buildbots. !!!
679 fn block_forever() { let (po, _ch) = stream::<()>(); po.recv(); }
681 #[ignore(reason = "linked failure")]
682 #[test] #[ignore(cfg(windows))]
683 fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port
684 use rt::test::run_in_newsched_task;
685 do run_in_newsched_task {
686 let (po, ch) = stream();
687 let ch = SharedChan::new(ch);
691 // Give middle task a chance to fail-but-not-kill-us.
692 do 16.times { task::deschedule(); }
693 ch.send(()); // If killed first, grandparent hangs.
695 fail!(); // Shouldn't kill either (grand)parent or (grand)child.
700 #[ignore(reason = "linked failure")]
701 #[test] #[ignore(cfg(windows))]
702 fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails
703 use rt::test::run_in_newsched_task;
704 do run_in_newsched_task {
705 do spawn_unlinked { fail!(); }
708 #[ignore(reason = "linked failure")]
709 #[test] #[ignore(cfg(windows))]
710 fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
711 use rt::test::run_in_newsched_task;
712 do run_in_newsched_task {
713 do spawn_supervised { fail!(); }
714 // Give child a chance to fail-but-not-kill-us.
715 do 16.times { task::deschedule(); }
718 #[ignore(reason = "linked failure")]
719 #[test] #[ignore(cfg(windows))]
720 fn test_spawn_unlinked_sup_fail_down() {
721 use rt::test::run_in_newsched_task;
722 do run_in_newsched_task {
723 let result: Result<(),()> = do try {
724 do spawn_supervised { block_forever(); }
725 fail!(); // Shouldn't leave a child hanging around.
727 assert!(result.is_err());
731 #[ignore(reason = "linked failure")]
732 #[test] #[ignore(cfg(windows))]
733 fn test_spawn_linked_sup_fail_up() { // child fails; parent fails
734 use rt::test::run_in_newsched_task;
735 do run_in_newsched_task {
736 let result: Result<(),()> = do try {
737 // Unidirectional "parenting" shouldn't override bidirectional linked.
738 // We have to cheat with opts - the interface doesn't support them because
739 // they don't make sense (redundant with task().supervised()).
741 b0.opts.linked = true;
742 b0.opts.supervised = true;
747 block_forever(); // We should get punted awake
749 assert!(result.is_err());
752 #[ignore(reason = "linked failure")]
753 #[test] #[ignore(cfg(windows))]
754 fn test_spawn_linked_sup_fail_down() { // parent fails; child fails
755 use rt::test::run_in_newsched_task;
756 do run_in_newsched_task {
757 let result: Result<(),()> = do try {
758 // We have to cheat with opts - the interface doesn't support them because
759 // they don't make sense (redundant with task().supervised()).
761 b0.opts.linked = true;
762 b0.opts.supervised = true;
763 do b0.spawn { block_forever(); }
764 fail!(); // *both* mechanisms would be wrong if this didn't kill the child
766 assert!(result.is_err());
769 #[ignore(reason = "linked failure")]
770 #[test] #[ignore(cfg(windows))]
771 fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails
772 use rt::test::run_in_newsched_task;
773 do run_in_newsched_task {
774 let result: Result<(),()> = do try {
775 // Default options are to spawn linked & unsupervised.
776 do spawn { fail!(); }
777 block_forever(); // We should get punted awake
779 assert!(result.is_err());
782 #[ignore(reason = "linked failure")]
783 #[test] #[ignore(cfg(windows))]
784 fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails
785 use rt::test::run_in_newsched_task;
786 do run_in_newsched_task {
787 let result: Result<(),()> = do try {
788 // Default options are to spawn linked & unsupervised.
789 do spawn { block_forever(); }
792 assert!(result.is_err());
795 #[ignore(reason = "linked failure")]
796 #[test] #[ignore(cfg(windows))]
797 fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails
798 use rt::test::run_in_newsched_task;
799 do run_in_newsched_task {
800 let result: Result<(),()> = do try {
801 // Make sure the above test is the same as this one.
802 let mut builder = task();
804 do builder.spawn { block_forever(); }
807 assert!(result.is_err());
811 // A couple bonus linked failure tests - testing for failure propagation even
812 // when the middle task exits successfully early before kill signals are sent.
814 #[ignore(reason = "linked failure")]
815 #[test] #[ignore(cfg(windows))]
816 fn test_spawn_failure_propagate_grandchild() {
817 use rt::test::run_in_newsched_task;
818 do run_in_newsched_task {
819 let result: Result<(),()> = do try {
820 // Middle task exits; does grandparent's failure propagate across the gap?
821 do spawn_supervised {
822 do spawn_supervised { block_forever(); }
824 do 16.times { task::deschedule(); }
827 assert!(result.is_err());
831 #[ignore(reason = "linked failure")]
832 #[test] #[ignore(cfg(windows))]
833 fn test_spawn_failure_propagate_secondborn() {
834 use rt::test::run_in_newsched_task;
835 do run_in_newsched_task {
836 let result: Result<(),()> = do try {
837 // First-born child exits; does parent's failure propagate to sibling?
838 do spawn_supervised {
839 do spawn { block_forever(); } // linked
841 do 16.times { task::deschedule(); }
844 assert!(result.is_err());
848 #[ignore(reason = "linked failure")]
849 #[test] #[ignore(cfg(windows))]
850 fn test_spawn_failure_propagate_nephew_or_niece() {
851 use rt::test::run_in_newsched_task;
852 do run_in_newsched_task {
853 let result: Result<(),()> = do try {
854 // Our sibling exits; does our failure propagate to sibling's child?
856 do spawn_supervised { block_forever(); }
858 do 16.times { task::deschedule(); }
861 assert!(result.is_err());
865 #[ignore(reason = "linked failure")]
866 #[test] #[ignore(cfg(windows))]
867 fn test_spawn_linked_sup_propagate_sibling() {
868 use rt::test::run_in_newsched_task;
869 do run_in_newsched_task {
870 let result: Result<(),()> = do try {
871 // Middle sibling exits - does eldest's failure propagate to youngest?
873 do spawn { block_forever(); } // linked
875 do 16.times { task::deschedule(); }
878 assert!(result.is_err());
883 fn test_unnamed_task() {
884 use rt::test::run_in_newsched_task;
886 do run_in_newsched_task {
888 do with_task_name |name| {
889 assert!(name.is_none());
896 fn test_named_task() {
897 use rt::test::run_in_newsched_task;
899 do run_in_newsched_task {
901 t.name(~"ada lovelace");
903 do with_task_name |name| {
904 assert!(name.unwrap() == "ada lovelace");
911 fn test_run_basic() {
912 let (po, ch) = stream::<()>();
913 let mut builder = task();
926 fn test_add_wrapper() {
927 let (po, ch) = stream::<()>();
929 let ch = Cell::new(ch);
930 do b0.add_wrapper |body| {
931 let ch = Cell::new(ch.take());
932 let result: ~fn() = || {
944 #[ignore(cfg(windows))]
945 fn test_future_result() {
946 let mut result = None;
947 let mut builder = task();
948 builder.future_result(|r| result = Some(r));
950 assert_eq!(result.unwrap().recv(), Success);
953 let mut builder = task();
954 builder.future_result(|r| result = Some(r));
959 assert_eq!(result.unwrap().recv(), Failure);
962 #[test] #[should_fail] #[ignore(cfg(windows))]
963 fn test_back_to_the_future_result() {
964 let mut builder = task();
965 builder.future_result(util::ignore);
966 builder.future_result(util::ignore);
970 fn test_try_success() {
974 result::Ok(~"Success!") => (),
980 #[ignore(cfg(windows))]
985 result::Err(()) => (),
986 result::Ok(()) => fail!()
991 fn get_sched_id() -> int {
992 do Local::borrow::<::rt::sched::Scheduler, int> |sched| {
993 sched.sched_id() as int
998 fn test_spawn_sched() {
999 let (po, ch) = stream::<()>();
1000 let ch = SharedChan::new(ch);
1002 fn f(i: int, ch: SharedChan<()>) {
1003 let parent_sched_id = get_sched_id();
1005 do spawn_sched(SingleThreaded) {
1006 let child_sched_id = get_sched_id();
1007 assert!(parent_sched_id != child_sched_id);
1012 f(i - 1, ch.clone());
1022 fn test_spawn_sched_childs_on_default_sched() {
1023 let (po, ch) = stream();
1025 // Assuming tests run on the default scheduler
1026 let default_id = get_sched_id();
1028 let ch = Cell::new(ch);
1029 do spawn_sched(SingleThreaded) {
1030 let parent_sched_id = get_sched_id();
1031 let ch = Cell::new(ch.take());
1034 let child_sched_id = get_sched_id();
1035 assert!(parent_sched_id != child_sched_id);
1036 assert_eq!(child_sched_id, default_id);
1048 externfn!(fn rust_dbg_lock_create() -> *libc::c_void)
1049 externfn!(fn rust_dbg_lock_destroy(lock: *libc::c_void))
1050 externfn!(fn rust_dbg_lock_lock(lock: *libc::c_void))
1051 externfn!(fn rust_dbg_lock_unlock(lock: *libc::c_void))
1052 externfn!(fn rust_dbg_lock_wait(lock: *libc::c_void))
1053 externfn!(fn rust_dbg_lock_signal(lock: *libc::c_void))
1057 fn test_spawn_sched_blocking() {
1060 // Testing that a task in one scheduler can block in foreign code
1061 // without affecting other schedulers
1063 let (start_po, start_ch) = stream();
1064 let (fin_po, fin_ch) = stream();
1066 let lock = testrt::rust_dbg_lock_create();
1068 do spawn_sched(SingleThreaded) {
1069 testrt::rust_dbg_lock_lock(lock);
1073 // Block the scheduler thread
1074 testrt::rust_dbg_lock_wait(lock);
1075 testrt::rust_dbg_lock_unlock(lock);
1080 // Wait until the other task has its lock
1083 fn pingpong(po: &Port<int>, ch: &Chan<int>) {
1091 let (setup_po, setup_ch) = stream();
1092 let (parent_po, parent_ch) = stream();
1094 let (child_po, child_ch) = stream();
1095 setup_ch.send(child_ch);
1096 pingpong(&child_po, &parent_ch);
1099 let child_ch = setup_po.recv();
1101 pingpong(&parent_po, &child_ch);
1102 testrt::rust_dbg_lock_lock(lock);
1103 testrt::rust_dbg_lock_signal(lock);
1104 testrt::rust_dbg_lock_unlock(lock);
1106 testrt::rust_dbg_lock_destroy(lock);
1112 fn avoid_copying_the_body(spawnfn: &fn(v: ~fn())) {
1113 let (p, ch) = stream::<uint>();
1116 let x_in_parent = ptr::to_unsafe_ptr(&*x) as uint;
1119 let x_in_child = ptr::to_unsafe_ptr(&*x) as uint;
1120 ch.send(x_in_child);
1123 let x_in_child = p.recv();
1124 assert_eq!(x_in_parent, x_in_child);
1128 fn test_avoid_copying_the_body_spawn() {
1129 avoid_copying_the_body(spawn);
1133 fn test_avoid_copying_the_body_task_spawn() {
1134 do avoid_copying_the_body |f| {
1135 let mut builder = task();
1136 do builder.spawn || {
1143 fn test_avoid_copying_the_body_try() {
1144 do avoid_copying_the_body |f| {
1152 fn test_avoid_copying_the_body_unlinked() {
1153 do avoid_copying_the_body |f| {
1154 do spawn_unlinked || {
1160 #[ignore(reason = "linked failure")]
1162 #[ignore(cfg(windows))]
1164 fn test_unkillable() {
1165 let (po, ch) = stream();
1167 // We want to do this after failing
1169 do 10.times { deschedule() }
1175 // We want to fail after the unkillable task
1183 let pp: *uint = cast::transmute(p);
1185 // If we are killed here then the box will leak
1188 let _p: ~int = cast::transmute(pp);
1192 // Now we can be killed
1196 #[ignore(reason = "linked failure")]
1198 #[ignore(cfg(windows))]
1200 fn test_unkillable_nested() {
1201 let (po, ch) = comm::stream();
1203 // We want to do this after failing
1204 do spawn_unlinked || {
1205 do 10.times { deschedule() }
1211 // We want to fail after the unkillable task
1218 do unkillable {} // Here's the difference from the previous test.
1220 let pp: *uint = cast::transmute(p);
1222 // If we are killed here then the box will leak
1225 let _p: ~int = cast::transmute(pp);
1229 // Now we can be killed
1234 fn test_child_doesnt_ref_parent() {
1235 // If the child refcounts the parent task, this will stack overflow when
1236 // climbing the task tree to dereference each ancestor. (See #1789)
1237 // (well, it would if the constant were 8000+ - I lowered it to be more
1238 // valgrind-friendly. try this at home, instead..!)
1239 static generations: uint = 16;
1240 fn child_no(x: uint) -> ~fn() {
1242 if x < generations {
1245 t.spawn(child_no(x+1));
1251 t.spawn(child_no(0));
1255 fn test_simple_newsched_spawn() {
1256 use rt::test::run_in_newsched_task;
1258 do run_in_newsched_task {
1263 #[ignore(reason = "linked failure")]
1264 #[test] #[ignore(cfg(windows))]
1265 fn test_spawn_watched() {
1266 use rt::test::run_in_newsched_task;
1267 do run_in_newsched_task {
1268 let result = do try {
1282 assert!(result.is_err());
1286 #[ignore(reason = "linked failure")]
1287 #[test] #[ignore(cfg(windows))]
1288 fn test_indestructible() {
1289 use rt::test::run_in_newsched_task;
1290 do run_in_newsched_task {
1291 let result = do try {
1297 let (p1, _c1) = stream::<()>();
1298 let (p2, c2) = stream::<()>();
1299 let (p3, c3) = stream::<()>();
1304 p1.recv(); // would deadlock if not killed
1320 assert!(result.is_ok());