1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
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!");
39 use comm::{stream, Chan, GenericChan, GenericPort, Port};
42 use task::rt::{task_id, sched_id, rust_task};
45 use unstable::finally::Finally;
47 #[cfg(test)] use comm::SharedChan;
54 /// A handle to a scheduler
57 SchedulerHandle(sched_id)
60 /// A handle to a task
67 * Indicates the manner in which a task exited.
69 * A task that completes without failing is considered to exit successfully.
70 * Supervised ancestors and linked siblings may yet fail after this task
71 * succeeds. Also note that in such a case, it may be nondeterministic whether
72 * linked failure or successful exit happen first.
74 * If you wish for this result's delivery to block until all linked and/or
75 * children tasks complete, recommend using a result future.
86 /// Run task on the default scheduler
88 /// Run task on the current scheduler
90 /// Run task on a specific scheduler
91 ExistingScheduler(Scheduler),
93 * Tasks are scheduled on the main OS thread
95 * The main OS thread is the thread used to launch the runtime which,
96 * in most cases, is the process's initial thread as created by the OS.
99 /// All tasks run in the same OS thread
101 /// Tasks are distributed among available CPUs
103 /// Each task runs in its own OS thread
105 /// Tasks are distributed among a fixed number of OS threads
110 * Scheduler configuration options
114 * * sched_mode - The operating mode of the scheduler
116 * * foreign_stack_size - The size of the foreign stack, in bytes
118 * Rust code runs on Rust-specific stacks. When Rust code calls foreign
119 * code (via functions in foreign modules) it switches to a typical, large
120 * stack appropriate for running code written in languages like C. By
121 * default these foreign stacks have unspecified size, but with this
122 * option their size can be precisely specified.
124 pub struct SchedOpts {
126 foreign_stack_size: Option<uint>,
130 * Task configuration options
134 * * linked - Propagate failure bidirectionally between child and parent.
135 * True by default. If both this and 'supervised' are false, then
136 * either task's failure will not affect the other ("unlinked").
138 * * supervised - Propagate failure unidirectionally from parent to child,
139 * but not from child to parent. False by default.
141 * * notify_chan - Enable lifecycle notifications on the given channel
143 * * sched - Specify the configuration of a new scheduler to create the task
146 * By default, every task is created in the same scheduler as its
147 * parent, where it is scheduled cooperatively with all other tasks
148 * in that scheduler. Some specialized applications may want more
149 * control over their scheduling, in which case they can be spawned
150 * into a new scheduler with the specific properties required.
152 * This is of particular importance for libraries which want to call
153 * into foreign code that blocks. Without doing so in a different
154 * scheduler other tasks will be impeded or even blocked indefinitely.
156 pub struct TaskOpts {
159 mut notify_chan: Option<Chan<TaskResult>>,
164 * The task builder type.
166 * Provides detailed control over the properties and behavior of new tasks.
168 // NB: Builders are designed to be single-use because they do stateful
169 // things that get weird when reusing - e.g. if you create a result future
170 // it only applies to a single task, so then you have to maintain Some
171 // potentially tricky state to ensure that everything behaves correctly
172 // when you try to reuse the builder to spawn a new task. We'll just
173 // sidestep that whole issue by making builders uncopyable and making
174 // the run function move them in.
176 // FIXME (#3724): Replace the 'consumed' bit with move mode on self
177 pub struct TaskBuilder {
179 mut gen_body: Option<~fn(v: ~fn()) -> ~fn()>,
180 can_not_copy: Option<util::NonCopyable>,
185 * Generate the base configuration for spawning a task, off of which more
186 * configuration methods can be chained.
187 * For example, task().unlinked().spawn is equivalent to spawn_unlinked.
189 pub fn task() -> TaskBuilder {
191 opts: default_task_opts(),
198 #[doc(hidden)] // FIXME #3538
199 priv impl TaskBuilder {
200 fn consume(&self) -> TaskBuilder {
202 fail!(~"Cannot copy a task_builder"); // Fake move mode on self
204 self.consumed = true;
205 let gen_body = replace(&mut self.gen_body, None);
206 let notify_chan = replace(&mut self.opts.notify_chan, None);
209 linked: self.opts.linked,
210 supervised: self.opts.supervised,
211 notify_chan: notify_chan,
212 sched: self.opts.sched
221 pub impl TaskBuilder {
223 * Decouple the child task's failure from the parent's. If either fails,
224 * the other will not be killed.
226 fn unlinked(&self) -> TaskBuilder {
227 let notify_chan = replace(&mut self.opts.notify_chan, None);
231 supervised: self.opts.supervised,
232 notify_chan: notify_chan,
233 sched: self.opts.sched
240 * Unidirectionally link the child task's failure with the parent's. The
241 * child's failure will not kill the parent, but the parent's will kill
244 fn supervised(&self) -> TaskBuilder {
245 let notify_chan = replace(&mut self.opts.notify_chan, None);
250 notify_chan: notify_chan,
251 sched: self.opts.sched
258 * Link the child task's and parent task's failures. If either fails, the
259 * other will be killed.
261 fn linked(&self) -> TaskBuilder {
262 let notify_chan = replace(&mut self.opts.notify_chan, None);
267 notify_chan: notify_chan,
268 sched: self.opts.sched
276 * Get a future representing the exit status of the task.
278 * Taking the value of the future will block until the child task
279 * terminates. The future-receiving callback specified will be called
280 * *before* the task is spawned; as such, do not invoke .get() within the
281 * closure; rather, store it in an outer variable/list for later use.
283 * Note that the future returning by this function is only useful for
284 * obtaining the value of the next task to be spawning with the
285 * builder. If additional tasks are spawned with the same builder
286 * then a new result future must be obtained prior to spawning each
290 * Fails if a future_result was already set for this task.
292 fn future_result(&self, blk: &fn(v: Port<TaskResult>)) -> TaskBuilder {
293 // FIXME (#3725): Once linked failure and notification are
294 // handled in the library, I can imagine implementing this by just
295 // registering an arbitrary number of task::on_exit handlers and
296 // sending out messages.
298 if self.opts.notify_chan.is_some() {
299 fail!(~"Can't set multiple future_results for one task!");
302 // Construct the future and give it to the caller.
303 let (notify_pipe_po, notify_pipe_ch) = stream::<TaskResult>();
307 // Reconfigure self to use a notify channel.
310 linked: self.opts.linked,
311 supervised: self.opts.supervised,
312 notify_chan: Some(notify_pipe_ch),
313 sched: self.opts.sched
319 /// Configure a custom scheduler mode for the task.
320 fn sched_mode(&self, mode: SchedMode) -> TaskBuilder {
321 let notify_chan = replace(&mut self.opts.notify_chan, None);
324 linked: self.opts.linked,
325 supervised: self.opts.supervised,
326 notify_chan: notify_chan,
327 sched: SchedOpts { mode: mode, foreign_stack_size: None}
335 * Add a wrapper to the body of the spawned task.
337 * Before the task is spawned it is passed through a 'body generator'
338 * function that may perform local setup operations as well as wrap
339 * the task body in remote setup operations. With this the behavior
340 * of tasks can be extended in simple ways.
342 * This function augments the current body generator with a new body
343 * generator by applying the task body which results from the
344 * existing body generator to the new body generator.
346 fn add_wrapper(&self, wrapper: ~fn(v: ~fn()) -> ~fn()) -> TaskBuilder {
347 let prev_gen_body = replace(&mut self.gen_body, None);
348 let prev_gen_body = match prev_gen_body {
351 let f: ~fn(~fn()) -> ~fn() = |body| body;
355 let prev_gen_body = Cell(prev_gen_body);
356 let next_gen_body = {
357 let f: ~fn(~fn()) -> ~fn() = |body| {
358 let prev_gen_body = prev_gen_body.take();
359 wrapper(prev_gen_body(body))
363 let notify_chan = replace(&mut self.opts.notify_chan, None);
366 linked: self.opts.linked,
367 supervised: self.opts.supervised,
368 notify_chan: notify_chan,
369 sched: self.opts.sched
371 gen_body: Some(next_gen_body),
378 * Creates and executes a new child task
380 * Sets up a new task with its own call stack and schedules it to run
381 * the provided unique closure. The task has the properties and behavior
382 * specified by the task_builder.
386 * When spawning into a new scheduler, the number of threads requested
387 * must be greater than zero.
389 fn spawn(&self, f: ~fn()) {
390 let gen_body = replace(&mut self.gen_body, None);
391 let notify_chan = replace(&mut self.opts.notify_chan, None);
392 let x = self.consume();
393 let opts = TaskOpts {
394 linked: x.opts.linked,
395 supervised: x.opts.supervised,
396 notify_chan: notify_chan,
399 let f = match gen_body {
407 spawn::spawn_raw(opts, f);
409 /// Runs a task, while transfering ownership of one argument to the child.
410 fn spawn_with<A:Owned>(&self, arg: A, f: ~fn(v: A)) {
418 * Execute a function in another task and return either the return value
419 * of the function or result::err.
423 * If the function executed successfully then try returns result::ok
424 * containing the value returned by the function. If the function fails
425 * then try returns result::err containing nil.
428 * Fails if a future_result was already set for this task.
430 fn try<T:Owned>(&self, f: ~fn() -> T) -> Result<T,()> {
431 let (po, ch) = stream::<T>();
432 let mut result = None;
434 let fr_task_builder = self.future_result(|+r| {
437 do fr_task_builder.spawn || {
440 match result.unwrap().recv() {
441 Success => result::Ok(po.recv()),
442 Failure => result::Err(())
448 /* Task construction */
450 pub fn default_task_opts() -> TaskOpts {
452 * The default task options
454 * By default all tasks are supervised by their parent, are spawned
455 * into the same scheduler, and do not post lifecycle notifications.
463 mode: DefaultScheduler,
464 foreign_stack_size: None
469 /* Spawn convenience functions */
471 pub fn spawn(f: ~fn()) {
473 * Creates and executes a new child task
475 * Sets up a new task with its own call stack and schedules it to run
476 * the provided unique closure.
478 * This function is equivalent to `task().spawn(f)`.
484 pub fn spawn_unlinked(f: ~fn()) {
486 * Creates a child task unlinked from the current one. If either this
487 * task or the child task fails, the other will not be killed.
490 task().unlinked().spawn(f)
493 pub fn spawn_supervised(f: ~fn()) {
495 * Creates a child task supervised by the current one. If the child
496 * task fails, the parent will not be killed, but if the parent fails,
497 * the child will be killed.
500 task().supervised().spawn(f)
503 pub fn spawn_with<A:Owned>(arg: A, f: ~fn(v: A)) {
505 * Runs a task, while transfering ownership of one argument to the
508 * This is useful for transfering ownership of noncopyables to
511 * This function is equivalent to `task().spawn_with(arg, f)`.
514 task().spawn_with(arg, f)
517 pub fn spawn_sched(mode: SchedMode, f: ~fn()) {
519 * Creates a new task on a new or existing scheduler
521 * When there are no more tasks to execute the
522 * scheduler terminates.
526 * In manual threads mode the number of threads requested must be
530 task().sched_mode(mode).spawn(f)
533 pub fn try<T:Owned>(f: ~fn() -> T) -> Result<T,()> {
535 * Execute a function in another task and return either the return value
536 * of the function or result::err.
538 * This is equivalent to task().supervised().try.
541 task().supervised().try(f)
545 /* Lifecycle functions */
548 //! Yield control to the task scheduler
551 let task_ = rt::rust_get_task();
552 let killed = rt::rust_task_yield(task_);
553 if killed && !failing() {
559 pub fn failing() -> bool {
560 //! True if the running task has failed
563 rt::rust_task_is_unwinding(rt::rust_get_task())
567 pub fn get_task() -> Task {
568 //! Get a handle to the running task
571 TaskHandle(rt::get_task_id())
575 pub fn get_scheduler() -> Scheduler {
576 SchedulerHandle(unsafe { rt::rust_get_sched_id() })
580 * Temporarily make the task unkillable
585 * do task::unkillable {
586 * // detach / yield / destroy must all be called together
587 * rustrt::rust_port_detach(po);
588 * // This must not result in the current task being killed
590 * rustrt::rust_port_destroy(po);
594 pub unsafe fn unkillable<U>(f: &fn() -> U) -> U {
595 let t = rt::rust_get_task();
597 rt::rust_task_inhibit_kill(t);
600 rt::rust_task_allow_kill(t);
604 /// The inverse of unkillable. Only ever to be used nested in unkillable().
605 pub unsafe fn rekillable<U>(f: &fn() -> U) -> U {
606 let t = rt::rust_get_task();
608 rt::rust_task_allow_kill(t);
611 rt::rust_task_inhibit_kill(t);
616 * A stronger version of unkillable that also inhibits scheduling operations.
617 * For use with exclusive ARCs, which use pthread mutexes directly.
619 pub unsafe fn atomically<U>(f: &fn() -> U) -> U {
620 let t = rt::rust_get_task();
622 rt::rust_task_inhibit_kill(t);
623 rt::rust_task_inhibit_yield(t);
626 rt::rust_task_allow_yield(t);
627 rt::rust_task_allow_kill(t);
631 #[test] #[should_fail] #[ignore(cfg(windows))]
632 fn test_cant_dup_task_builder() {
633 let b = task().unlinked();
635 // FIXME(#3724): For now, this is a -runtime- failure, because we haven't
636 // got move mode on self. When 3724 is fixed, this test should fail to
637 // compile instead, and should go in tests/compile-fail.
638 do b.spawn { } // b should have been consumed by the previous call
641 // The following 8 tests test the following 2^3 combinations:
642 // {un,}linked {un,}supervised failure propagation {up,down}wards.
644 // !!! These tests are dangerous. If Something is buggy, they will hang, !!!
645 // !!! instead of exiting cleanly. This might wedge the buildbots. !!!
647 #[test] #[ignore(cfg(windows))]
648 fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port
649 let (po, ch) = stream();
650 let ch = SharedChan::new(ch);
654 // Give middle task a chance to fail-but-not-kill-us.
655 for old_iter::repeat(16) { task::yield(); }
656 ch.send(()); // If killed first, grandparent hangs.
658 fail!(); // Shouldn't kill either (grand)parent or (grand)child.
662 #[test] #[ignore(cfg(windows))]
663 fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails
664 do spawn_unlinked { fail!(); }
666 #[test] #[ignore(cfg(windows))]
667 fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
668 do spawn_supervised { fail!(); }
669 // Give child a chance to fail-but-not-kill-us.
670 for old_iter::repeat(16) { task::yield(); }
672 #[test] #[should_fail] #[ignore(cfg(windows))]
673 fn test_spawn_unlinked_sup_fail_down() {
674 do spawn_supervised { loop { task::yield(); } }
675 fail!(); // Shouldn't leave a child hanging around.
678 #[test] #[should_fail] #[ignore(cfg(windows))]
679 fn test_spawn_linked_sup_fail_up() { // child fails; parent fails
680 let (po, _ch) = stream::<()>();
681 // Unidirectional "parenting" shouldn't override bidirectional linked.
682 // We have to cheat with opts - the interface doesn't support them because
683 // they don't make sense (redundant with task().supervised()).
685 let mut opts = default_task_opts();
687 opts.supervised = true;
692 let b1 = TaskBuilder {
697 do b1.spawn { fail!(); }
698 po.recv(); // We should get punted awake
700 #[test] #[should_fail] #[ignore(cfg(windows))]
701 fn test_spawn_linked_sup_fail_down() { // parent fails; child fails
702 // We have to cheat with opts - the interface doesn't support them because
703 // they don't make sense (redundant with task().supervised()).
705 let mut opts = default_task_opts();
707 opts.supervised = true;
712 let b1 = TaskBuilder {
717 do b1.spawn { loop { task::yield(); } }
718 fail!(); // *both* mechanisms would be wrong if this didn't kill the child
720 #[test] #[should_fail] #[ignore(cfg(windows))]
721 fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails
722 let (po, _ch) = stream::<()>();
723 // Default options are to spawn linked & unsupervised.
724 do spawn { fail!(); }
725 po.recv(); // We should get punted awake
727 #[test] #[should_fail] #[ignore(cfg(windows))]
728 fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails
729 // Default options are to spawn linked & unsupervised.
730 do spawn { loop { task::yield(); } }
733 #[test] #[should_fail] #[ignore(cfg(windows))]
734 fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails
735 // Make sure the above test is the same as this one.
736 do task().linked().spawn { loop { task::yield(); } }
740 // A couple bonus linked failure tests - testing for failure propagation even
741 // when the middle task exits successfully early before kill signals are sent.
743 #[test] #[should_fail] #[ignore(cfg(windows))]
744 fn test_spawn_failure_propagate_grandchild() {
745 // Middle task exits; does grandparent's failure propagate across the gap?
746 do spawn_supervised {
747 do spawn_supervised {
748 loop { task::yield(); }
751 for old_iter::repeat(16) { task::yield(); }
755 #[test] #[should_fail] #[ignore(cfg(windows))]
756 fn test_spawn_failure_propagate_secondborn() {
757 // First-born child exits; does parent's failure propagate to sibling?
758 do spawn_supervised {
760 loop { task::yield(); }
763 for old_iter::repeat(16) { task::yield(); }
767 #[test] #[should_fail] #[ignore(cfg(windows))]
768 fn test_spawn_failure_propagate_nephew_or_niece() {
769 // Our sibling exits; does our failure propagate to sibling's child?
771 do spawn_supervised {
772 loop { task::yield(); }
775 for old_iter::repeat(16) { task::yield(); }
779 #[test] #[should_fail] #[ignore(cfg(windows))]
780 fn test_spawn_linked_sup_propagate_sibling() {
781 // Middle sibling exits - does eldest's failure propagate to youngest?
784 loop { task::yield(); }
787 for old_iter::repeat(16) { task::yield(); }
792 fn test_run_basic() {
793 let (po, ch) = stream::<()>();
802 mut f: Option<Chan<()>>
806 fn test_add_wrapper() {
807 let (po, ch) = stream::<()>();
809 let ch = Wrapper { f: Some(ch) };
810 let b1 = do b0.add_wrapper |body| {
811 let ch = Wrapper { f: Some(ch.f.swap_unwrap()) };
812 let result: ~fn() = || {
813 let ch = ch.f.swap_unwrap();
824 #[ignore(cfg(windows))]
825 fn test_future_result() {
826 let mut result = None;
827 do task().future_result(|+r| { result = Some(r); }).spawn { }
828 assert!(result.unwrap().recv() == Success);
831 do task().future_result(|+r|
832 { result = Some(r); }).unlinked().spawn {
835 assert!(result.unwrap().recv() == Failure);
838 #[test] #[should_fail] #[ignore(cfg(windows))]
839 fn test_back_to_the_future_result() {
840 let _ = task().future_result(util::ignore).future_result(util::ignore);
844 fn test_try_success() {
848 result::Ok(~"Success!") => (),
854 #[ignore(cfg(windows))]
859 result::Err(()) => (),
860 result::Ok(()) => fail!()
866 #[ignore(cfg(windows))]
867 fn test_spawn_sched_no_threads() {
868 do spawn_sched(ManualThreads(0u)) { }
872 fn test_spawn_sched() {
873 let (po, ch) = stream::<()>();
874 let ch = SharedChan::new(ch);
876 fn f(i: int, ch: SharedChan<()>) {
877 let parent_sched_id = unsafe { rt::rust_get_sched_id() };
879 do spawn_sched(SingleThreaded) {
880 let child_sched_id = unsafe { rt::rust_get_sched_id() };
881 assert!(parent_sched_id != child_sched_id);
886 f(i - 1, ch.clone());
896 fn test_spawn_sched_childs_on_default_sched() {
897 let (po, ch) = stream();
899 // Assuming tests run on the default scheduler
900 let default_id = unsafe { rt::rust_get_sched_id() };
902 let ch = Wrapper { f: Some(ch) };
903 do spawn_sched(SingleThreaded) {
904 let parent_sched_id = unsafe { rt::rust_get_sched_id() };
905 let ch = Wrapper { f: Some(ch.f.swap_unwrap()) };
907 let ch = ch.f.swap_unwrap();
908 let child_sched_id = unsafe { rt::rust_get_sched_id() };
909 assert!(parent_sched_id != child_sched_id);
910 assert!(child_sched_id == default_id);
924 unsafe fn rust_dbg_lock_create() -> *libc::c_void;
925 unsafe fn rust_dbg_lock_destroy(lock: *libc::c_void);
926 unsafe fn rust_dbg_lock_lock(lock: *libc::c_void);
927 unsafe fn rust_dbg_lock_unlock(lock: *libc::c_void);
928 unsafe fn rust_dbg_lock_wait(lock: *libc::c_void);
929 unsafe fn rust_dbg_lock_signal(lock: *libc::c_void);
934 fn test_spawn_sched_blocking() {
937 // Testing that a task in one scheduler can block in foreign code
938 // without affecting other schedulers
939 for old_iter::repeat(20u) {
941 let (start_po, start_ch) = stream();
942 let (fin_po, fin_ch) = stream();
944 let lock = testrt::rust_dbg_lock_create();
946 do spawn_sched(SingleThreaded) {
948 testrt::rust_dbg_lock_lock(lock);
952 // Block the scheduler thread
953 testrt::rust_dbg_lock_wait(lock);
954 testrt::rust_dbg_lock_unlock(lock);
960 // Wait until the other task has its lock
963 fn pingpong(po: &Port<int>, ch: &Chan<int>) {
971 let (setup_po, setup_ch) = stream();
972 let (parent_po, parent_ch) = stream();
974 let (child_po, child_ch) = stream();
975 setup_ch.send(child_ch);
976 pingpong(&child_po, &parent_ch);
979 let child_ch = setup_po.recv();
981 pingpong(&parent_po, &child_ch);
982 testrt::rust_dbg_lock_lock(lock);
983 testrt::rust_dbg_lock_signal(lock);
984 testrt::rust_dbg_lock_unlock(lock);
986 testrt::rust_dbg_lock_destroy(lock);
992 fn avoid_copying_the_body(spawnfn: &fn(v: ~fn())) {
993 let (p, ch) = stream::<uint>();
996 let x_in_parent = ptr::to_unsafe_ptr(&*x) as uint;
999 let x_in_child = ptr::to_unsafe_ptr(&*x) as uint;
1000 ch.send(x_in_child);
1003 let x_in_child = p.recv();
1004 assert!(x_in_parent == x_in_child);
1008 fn test_avoid_copying_the_body_spawn() {
1009 avoid_copying_the_body(spawn);
1013 fn test_avoid_copying_the_body_task_spawn() {
1014 do avoid_copying_the_body |f| {
1015 do task().spawn || {
1022 fn test_avoid_copying_the_body_try() {
1023 do avoid_copying_the_body |f| {
1031 fn test_avoid_copying_the_body_unlinked() {
1032 do avoid_copying_the_body |f| {
1033 do spawn_unlinked || {
1040 fn test_platform_thread() {
1041 let (po, ch) = stream();
1042 do task().sched_mode(PlatformThread).spawn {
1049 #[ignore(cfg(windows))]
1051 fn test_unkillable() {
1052 let (po, ch) = stream();
1054 // We want to do this after failing
1056 for old_iter::repeat(10) { yield() }
1062 // We want to fail after the unkillable task
1070 let pp: *uint = cast::transmute(p);
1072 // If we are killed here then the box will leak
1075 let _p: ~int = cast::transmute(pp);
1079 // Now we can be killed
1084 #[ignore(cfg(windows))]
1086 fn test_unkillable_nested() {
1087 let (po, ch) = comm::stream();
1089 // We want to do this after failing
1090 do spawn_unlinked || {
1091 for old_iter::repeat(10) { yield() }
1097 // We want to fail after the unkillable task
1104 do unkillable {} // Here's the difference from the previous test.
1106 let pp: *uint = cast::transmute(p);
1108 // If we are killed here then the box will leak
1111 let _p: ~int = cast::transmute(pp);
1115 // Now we can be killed
1119 #[test] #[should_fail] #[ignore(cfg(windows))]
1120 fn test_atomically() {
1121 unsafe { do atomically { yield(); } }
1125 fn test_atomically2() {
1126 unsafe { do atomically { } } yield(); // shouldn't fail
1129 #[test] #[should_fail] #[ignore(cfg(windows))]
1130 fn test_atomically_nested() {
1131 unsafe { do atomically { do atomically { } yield(); } }
1135 fn test_child_doesnt_ref_parent() {
1136 // If the child refcounts the parent task, this will stack overflow when
1137 // climbing the task tree to dereference each ancestor. (See #1789)
1138 // (well, it would if the constant were 8000+ - I lowered it to be more
1139 // valgrind-friendly. try this at home, instead..!)
1140 static generations: uint = 16;
1141 fn child_no(x: uint) -> ~fn() {
1143 if x < generations {
1144 task::spawn(child_no(x+1));
1148 task::spawn(child_no(0));
1152 fn test_sched_thread_per_core() {
1153 let (port, chan) = comm::stream();
1155 do spawn_sched(ThreadPerCore) || {
1157 let cores = rt::rust_num_threads();
1158 let reported_threads = rt::rust_sched_threads();
1159 assert!((cores as uint == reported_threads as uint));
1168 fn test_spawn_thread_on_demand() {
1169 let (port, chan) = comm::stream();
1171 do spawn_sched(ManualThreads(2)) || {
1173 let max_threads = rt::rust_sched_threads();
1174 assert!((max_threads as int == 2));
1175 let running_threads = rt::rust_sched_current_nonlazy_threads();
1176 assert!((running_threads as int == 1));
1178 let (port2, chan2) = comm::stream();
1180 do spawn_sched(CurrentScheduler) || {
1184 let running_threads2 = rt::rust_sched_current_nonlazy_threads();
1185 assert!((running_threads2 as int == 2));
1196 fn test_simple_newsched_spawn() {
1197 use rt::run_in_newsched_task;
1199 do run_in_newsched_task {