From f7eed223873a4280c9abea937e60ef1aaedf0162 Mon Sep 17 00:00:00 2001 From: toddaaro Date: Fri, 19 Jul 2013 14:25:05 -0700 Subject: A major refactoring that changes the way the runtime uses TLS. In the old design the TLS held the scheduler struct, and the scheduler struct held the active task. This posed all sorts of weird problems due to how we wanted to use the contents of TLS. The cleaner approach is to leave the active task in TLS and have the task hold the scheduler. To make this work out the scheduler has to run inside a regular task, and then once that is the case the context switching code is massively simplified, as instead of three possible paths there is only one. The logical flow is also easier to follow, as the scheduler struct acts somewhat like a "token" indicating what is active. These changes also necessitated changing a large number of runtime tests, and rewriting most of the runtime testing helpers. Polish level is "low", as I will very soon start on more scheduler changes that will require wiping the polish off. That being said there should be sufficient comments around anything complex to make this entirely respectable as a standalone commit. --- src/libstd/task/spawn.rs | 43 +++++++++++++------------------------------ 1 file changed, 13 insertions(+), 30 deletions(-) (limited to 'src/libstd/task') diff --git a/src/libstd/task/spawn.rs b/src/libstd/task/spawn.rs index 4558f8e32c1..88f214ef4c0 100644 --- a/src/libstd/task/spawn.rs +++ b/src/libstd/task/spawn.rs @@ -653,22 +653,16 @@ fn enlist_many(child: TaskHandle, child_arc: &TaskGroupArc, pub fn spawn_raw(opts: TaskOpts, f: ~fn()) { match context() { - OldTaskContext => { - spawn_raw_oldsched(opts, f) - } - TaskContext => { - spawn_raw_newsched(opts, f) - } - SchedulerContext => { - fail!("can't spawn from scheduler context") - } - GlobalContext => { - fail!("can't spawn from global context") - } + OldTaskContext => spawn_raw_oldsched(opts, f), + TaskContext => spawn_raw_newsched(opts, f), + SchedulerContext => fail!("can't spawn from scheduler context"), + GlobalContext => fail!("can't spawn from global context"), } } fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) { + use rt::sched::*; + let child_data = Cell::new(gen_child_taskgroup(opts.linked, opts.supervised)); let indestructible = opts.indestructible; @@ -700,19 +694,11 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) { } }; - let mut task = unsafe { - let sched = Local::unsafe_borrow::(); - rtdebug!("unsafe borrowed sched"); - - if opts.watched { - let child_wrapper = Cell::new(child_wrapper); - do Local::borrow::() |running_task| { - ~running_task.new_child(&mut (*sched).stack_pool, child_wrapper.take()) - } - } else { - // An unwatched task is a new root in the exit-code propagation tree - ~Task::new_root(&mut (*sched).stack_pool, child_wrapper) - } + let mut task = if opts.watched { + Task::build_child(child_wrapper) + } else { + // An unwatched task is a new root in the exit-code propagation tree + Task::build_root(child_wrapper) }; if opts.notify_chan.is_some() { @@ -727,12 +713,9 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) { } task.name = opts.name.take(); + rtdebug!("spawn calling run_task"); + Scheduler::run_task(task); - rtdebug!("spawn about to take scheduler"); - - let sched = Local::take::(); - rtdebug!("took sched in spawn"); - sched.schedule_task(task); } fn spawn_raw_oldsched(mut opts: TaskOpts, f: ~fn()) { -- cgit 1.4.1-3-g733a5 From 37f385e44bbe31a693a665a74b6b493e11b7cf1c Mon Sep 17 00:00:00 2001 From: Ben Blum Date: Fri, 26 Jul 2013 17:20:26 -0400 Subject: Have linked failure tests run on the new scheduler instead of requiring RUST_NEWRT to test. --- src/libstd/task/mod.rs | 231 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 152 insertions(+), 79 deletions(-) (limited to 'src/libstd/task') diff --git a/src/libstd/task/mod.rs b/src/libstd/task/mod.rs index d0124407bd4..5e4d48403cc 100644 --- a/src/libstd/task/mod.rs +++ b/src/libstd/task/mod.rs @@ -677,121 +677,190 @@ fn block_forever() { let (po, _ch) = stream::<()>(); po.recv(); } #[test] #[ignore(cfg(windows))] fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port - let (po, ch) = stream(); - let ch = SharedChan::new(ch); - do spawn_unlinked { - let ch = ch.clone(); + use rt::test::run_in_newsched_task; + do run_in_newsched_task { + let (po, ch) = stream(); + let ch = SharedChan::new(ch); do spawn_unlinked { - // Give middle task a chance to fail-but-not-kill-us. - do 16.times { task::yield(); } - ch.send(()); // If killed first, grandparent hangs. + let ch = ch.clone(); + do spawn_unlinked { + // Give middle task a chance to fail-but-not-kill-us. + for 16.times { task::yield(); } + ch.send(()); // If killed first, grandparent hangs. + } + fail!(); // Shouldn't kill either (grand)parent or (grand)child. } - fail!(); // Shouldn't kill either (grand)parent or (grand)child. + po.recv(); } - po.recv(); } #[test] #[ignore(cfg(windows))] fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails - do spawn_unlinked { fail!(); } + use rt::test::run_in_newsched_task; + do run_in_newsched_task { + do spawn_unlinked { fail!(); } + } } #[test] #[ignore(cfg(windows))] fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails - do spawn_supervised { fail!(); } - // Give child a chance to fail-but-not-kill-us. - do 16.times { task::yield(); } + use rt::test::run_in_newsched_task; + do run_in_newsched_task { + do spawn_supervised { fail!(); } + // Give child a chance to fail-but-not-kill-us. + for 16.times { task::yield(); } + } } -#[test] #[should_fail] #[ignore(cfg(windows))] +#[test] #[ignore(cfg(windows))] fn test_spawn_unlinked_sup_fail_down() { - do spawn_supervised { block_forever(); } - fail!(); // Shouldn't leave a child hanging around. + use rt::test::run_in_newsched_task; + do run_in_newsched_task { + let result: Result<(),()> = do try { + do spawn_supervised { block_forever(); } + fail!(); // Shouldn't leave a child hanging around. + }; + assert!(result.is_err()); + } } -#[test] #[should_fail] #[ignore(cfg(windows))] +#[test] #[ignore(cfg(windows))] fn test_spawn_linked_sup_fail_up() { // child fails; parent fails - // Unidirectional "parenting" shouldn't override bidirectional linked. - // We have to cheat with opts - the interface doesn't support them because - // they don't make sense (redundant with task().supervised()). - let mut b0 = task(); - b0.opts.linked = true; - b0.opts.supervised = true; - - do b0.spawn { - fail!(); + use rt::test::run_in_newsched_task; + do run_in_newsched_task { + let result: Result<(),()> = do try { + // Unidirectional "parenting" shouldn't override bidirectional linked. + // We have to cheat with opts - the interface doesn't support them because + // they don't make sense (redundant with task().supervised()). + let mut b0 = task(); + b0.opts.linked = true; + b0.opts.supervised = true; + + do b0.spawn { + fail!(); + } + block_forever(); // We should get punted awake + }; + assert!(result.is_err()); } - block_forever(); // We should get punted awake } -#[test] #[should_fail] #[ignore(cfg(windows))] +#[test] #[ignore(cfg(windows))] fn test_spawn_linked_sup_fail_down() { // parent fails; child fails - // We have to cheat with opts - the interface doesn't support them because - // they don't make sense (redundant with task().supervised()). - let mut b0 = task(); - b0.opts.linked = true; - b0.opts.supervised = true; - do b0.spawn { block_forever(); } - fail!(); // *both* mechanisms would be wrong if this didn't kill the child + use rt::test::run_in_newsched_task; + do run_in_newsched_task { + let result: Result<(),()> = do try { + // We have to cheat with opts - the interface doesn't support them because + // they don't make sense (redundant with task().supervised()). + let mut b0 = task(); + b0.opts.linked = true; + b0.opts.supervised = true; + do b0.spawn { block_forever(); } + fail!(); // *both* mechanisms would be wrong if this didn't kill the child + }; + assert!(result.is_err()); + } } -#[test] #[should_fail] #[ignore(cfg(windows))] +#[test] #[ignore(cfg(windows))] fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails - // Default options are to spawn linked & unsupervised. - do spawn { fail!(); } - block_forever(); // We should get punted awake + use rt::test::run_in_newsched_task; + do run_in_newsched_task { + let result: Result<(),()> = do try { + // Default options are to spawn linked & unsupervised. + do spawn { fail!(); } + block_forever(); // We should get punted awake + }; + assert!(result.is_err()); + } } -#[test] #[should_fail] #[ignore(cfg(windows))] +#[test] #[ignore(cfg(windows))] fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails - // Default options are to spawn linked & unsupervised. - do spawn { block_forever(); } - fail!(); + use rt::test::run_in_newsched_task; + do run_in_newsched_task { + let result: Result<(),()> = do try { + // Default options are to spawn linked & unsupervised. + do spawn { block_forever(); } + fail!(); + }; + assert!(result.is_err()); + } } -#[test] #[should_fail] #[ignore(cfg(windows))] +#[test] #[ignore(cfg(windows))] fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails - // Make sure the above test is the same as this one. - let mut builder = task(); - builder.linked(); - do builder.spawn { block_forever(); } - fail!(); + use rt::test::run_in_newsched_task; + do run_in_newsched_task { + let result: Result<(),()> = do try { + // Make sure the above test is the same as this one. + let mut builder = task(); + builder.linked(); + do builder.spawn { block_forever(); } + fail!(); + }; + assert!(result.is_err()); + } } // A couple bonus linked failure tests - testing for failure propagation even // when the middle task exits successfully early before kill signals are sent. -#[test] #[should_fail] #[ignore(cfg(windows))] +#[test] #[ignore(cfg(windows))] fn test_spawn_failure_propagate_grandchild() { - // Middle task exits; does grandparent's failure propagate across the gap? - do spawn_supervised { - do spawn_supervised { block_forever(); } + use rt::test::run_in_newsched_task; + do run_in_newsched_task { + let result: Result<(),()> = do try { + // Middle task exits; does grandparent's failure propagate across the gap? + do spawn_supervised { + do spawn_supervised { block_forever(); } + } + for 16.times { task::yield(); } + fail!(); + }; + assert!(result.is_err()); } - do 16.times { task::yield(); } - fail!(); } -#[test] #[should_fail] #[ignore(cfg(windows))] +#[test] #[ignore(cfg(windows))] fn test_spawn_failure_propagate_secondborn() { - // First-born child exits; does parent's failure propagate to sibling? - do spawn_supervised { - do spawn { block_forever(); } // linked + use rt::test::run_in_newsched_task; + do run_in_newsched_task { + let result: Result<(),()> = do try { + // First-born child exits; does parent's failure propagate to sibling? + do spawn_supervised { + do spawn { block_forever(); } // linked + } + for 16.times { task::yield(); } + fail!(); + }; + assert!(result.is_err()); } - do 16.times { task::yield(); } - fail!(); } -#[test] #[should_fail] #[ignore(cfg(windows))] +#[test] #[ignore(cfg(windows))] fn test_spawn_failure_propagate_nephew_or_niece() { - // Our sibling exits; does our failure propagate to sibling's child? - do spawn { // linked - do spawn_supervised { block_forever(); } + use rt::test::run_in_newsched_task; + do run_in_newsched_task { + let result: Result<(),()> = do try { + // Our sibling exits; does our failure propagate to sibling's child? + do spawn { // linked + do spawn_supervised { block_forever(); } + } + for 16.times { task::yield(); } + fail!(); + }; + assert!(result.is_err()); } - do 16.times { task::yield(); } - fail!(); } -#[test] #[should_fail] #[ignore(cfg(windows))] +#[test] #[ignore(cfg(windows))] fn test_spawn_linked_sup_propagate_sibling() { - // Middle sibling exits - does eldest's failure propagate to youngest? - do spawn { // linked - do spawn { block_forever(); } // linked + use rt::test::run_in_newsched_task; + do run_in_newsched_task { + let result: Result<(),()> = do try { + // Middle sibling exits - does eldest's failure propagate to youngest? + do spawn { // linked + do spawn { block_forever(); } // linked + } + for 16.times { task::yield(); } + fail!(); + }; + assert!(result.is_err()); } - do 16.times { task::yield(); } - fail!(); } #[test] @@ -1149,11 +1218,15 @@ fn test_child_doesnt_ref_parent() { fn child_no(x: uint) -> ~fn() { return || { if x < generations { - task::spawn(child_no(x+1)); + let mut t = task(); + t.unwatched(); + t.spawn(child_no(x+1)); } } } - task::spawn(child_no(0)); + let mut t = task(); + t.unwatched(); + t.spawn(child_no(0)); } #[test] @@ -1167,9 +1240,9 @@ fn test_simple_newsched_spawn() { #[test] #[ignore(cfg(windows))] fn test_spawn_watched() { - use rt::test::{run_in_newsched_task, spawntask_try}; + use rt::test::run_in_newsched_task; do run_in_newsched_task { - let result = do spawntask_try { + let result = do try { let mut t = task(); t.unlinked(); t.watched(); @@ -1189,9 +1262,9 @@ fn test_spawn_watched() { #[test] #[ignore(cfg(windows))] fn test_indestructible() { - use rt::test::{run_in_newsched_task, spawntask_try}; + use rt::test::run_in_newsched_task; do run_in_newsched_task { - let result = do spawntask_try { + let result = do try { let mut t = task(); t.watched(); t.supervised(); -- cgit 1.4.1-3-g733a5 From 8e98eabce52b29d2e49ce63713ad2e1e04115bab Mon Sep 17 00:00:00 2001 From: toddaaro Date: Thu, 1 Aug 2013 15:08:51 -0700 Subject: modified local to include an implementation for try_unsafe_borrow:: so that the log methods will work --- src/libstd/rt/io/net/tcp.rs | 4 ++-- src/libstd/rt/local.rs | 10 ++++++++-- src/libstd/rt/task.rs | 1 + src/libstd/task/mod.rs | 12 ++++++------ 4 files changed, 17 insertions(+), 10 deletions(-) (limited to 'src/libstd/task') diff --git a/src/libstd/rt/io/net/tcp.rs b/src/libstd/rt/io/net/tcp.rs index 449df8cddea..2daa64e8085 100644 --- a/src/libstd/rt/io/net/tcp.rs +++ b/src/libstd/rt/io/net/tcp.rs @@ -380,7 +380,7 @@ mod test { } do spawntask { - for max.times { + do max.times { let mut stream = TcpStream::connect(addr); stream.write([99]); } @@ -405,7 +405,7 @@ mod test { } do spawntask { - for max.times { + do max.times { let mut stream = TcpStream::connect(addr); stream.write([99]); } diff --git a/src/libstd/rt/local.rs b/src/libstd/rt/local.rs index 71e60a6a923..7ab63233cff 100644 --- a/src/libstd/rt/local.rs +++ b/src/libstd/rt/local.rs @@ -44,7 +44,13 @@ impl Local for Task { } } unsafe fn unsafe_borrow() -> *mut Task { local_ptr::unsafe_borrow() } - unsafe fn try_unsafe_borrow() -> Option<*mut Task> { rtabort!("unimpl task try_unsafe_borrow") } + unsafe fn try_unsafe_borrow() -> Option<*mut Task> { + if Local::exists::() { + Some(Local::unsafe_borrow()) + } else { + None + } + } } impl Local for Scheduler { @@ -95,7 +101,7 @@ impl Local for Scheduler { } } unsafe fn try_unsafe_borrow() -> Option<*mut Scheduler> { - if Local::exists::() { + if Local::exists::() { Some(Local::unsafe_borrow()) } else { None diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index fcc6ebeada6..23a0d28e457 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -129,6 +129,7 @@ impl Task { death: Death::new(), destroyed: false, coroutine: Some(Coroutine::empty()), + name: None, sched: None, task_type: SchedTask } diff --git a/src/libstd/task/mod.rs b/src/libstd/task/mod.rs index 5e4d48403cc..7a864ecb867 100644 --- a/src/libstd/task/mod.rs +++ b/src/libstd/task/mod.rs @@ -685,7 +685,7 @@ fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port let ch = ch.clone(); do spawn_unlinked { // Give middle task a chance to fail-but-not-kill-us. - for 16.times { task::yield(); } + do 16.times { task::yield(); } ch.send(()); // If killed first, grandparent hangs. } fail!(); // Shouldn't kill either (grand)parent or (grand)child. @@ -706,7 +706,7 @@ fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails do run_in_newsched_task { do spawn_supervised { fail!(); } // Give child a chance to fail-but-not-kill-us. - for 16.times { task::yield(); } + do 16.times { task::yield(); } } } #[test] #[ignore(cfg(windows))] @@ -808,7 +808,7 @@ fn test_spawn_failure_propagate_grandchild() { do spawn_supervised { do spawn_supervised { block_forever(); } } - for 16.times { task::yield(); } + do 16.times { task::yield(); } fail!(); }; assert!(result.is_err()); @@ -824,7 +824,7 @@ fn test_spawn_failure_propagate_secondborn() { do spawn_supervised { do spawn { block_forever(); } // linked } - for 16.times { task::yield(); } + do 16.times { task::yield(); } fail!(); }; assert!(result.is_err()); @@ -840,7 +840,7 @@ fn test_spawn_failure_propagate_nephew_or_niece() { do spawn { // linked do spawn_supervised { block_forever(); } } - for 16.times { task::yield(); } + do 16.times { task::yield(); } fail!(); }; assert!(result.is_err()); @@ -856,7 +856,7 @@ fn test_spawn_linked_sup_propagate_sibling() { do spawn { // linked do spawn { block_forever(); } // linked } - for 16.times { task::yield(); } + do 16.times { task::yield(); } fail!(); }; assert!(result.is_err()); -- cgit 1.4.1-3-g733a5