From 47e82c85555895c21206c84b62cac19eb351d1b8 Mon Sep 17 00:00:00 2001 From: Ben Blum Date: Fri, 2 Aug 2013 19:35:13 -0400 Subject: [PATCH 1/2] (cleanup) Uncomment an assertion that now holds. --- src/libstd/rt/kill.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libstd/rt/kill.rs b/src/libstd/rt/kill.rs index deec8dd37a600..ef2d0de5312ca 100644 --- a/src/libstd/rt/kill.rs +++ b/src/libstd/rt/kill.rs @@ -205,11 +205,10 @@ impl BlockedTask { let flag_arc = match task.death.spare_kill_flag.take() { Some(spare_flag) => spare_flag, None => { - // FIXME(#7544): Uncomment this when terminate_current_task - // stops being *terrible*. That's the only place that violates - // the assumption of "becoming unkillable will fail if the - // task was killed". - // rtassert!(task.unwinder.unwinding); + // A task that kills us won't have a spare kill flag to + // give back to us, so we restore it ourselves here. This + // situation should only arise when we're already failing. + rtassert!(task.unwinder.unwinding); (*task.death.kill_handle.get_ref().get()).killed.clone() } }; From 1f95bd768444a4a0318951c2e76f3d28bbf3763a Mon Sep 17 00:00:00 2001 From: Ben Blum Date: Mon, 5 Aug 2013 16:55:08 -0400 Subject: [PATCH 2/2] Lazily initialize 'leaf node' taskgroups for unlinked spawns, for an apparent 11% speedup. --- src/libstd/rt/kill.rs | 4 ++ src/libstd/task/spawn.rs | 104 ++++++++++++++++++++++----------------- 2 files changed, 63 insertions(+), 45 deletions(-) diff --git a/src/libstd/rt/kill.rs b/src/libstd/rt/kill.rs index ef2d0de5312ca..3372c13b87703 100644 --- a/src/libstd/rt/kill.rs +++ b/src/libstd/rt/kill.rs @@ -193,6 +193,10 @@ impl BlockedTask { /// Create a blocked task, unless the task was already killed. pub fn try_block(mut task: ~Task) -> Either<~Task, BlockedTask> { + // NB: As an optimization, we could give a free pass to being unkillable + // to tasks whose taskgroups haven't been initialized yet, but that + // introduces complications with select() and with the test cases below, + // and it's not clear the uncommon performance boost is worth it. if task.death.unkillable > 0 { Right(Unkillable(task)) } else { diff --git a/src/libstd/task/spawn.rs b/src/libstd/task/spawn.rs index 3bf2f255f5751..527b20b0e9027 100644 --- a/src/libstd/task/spawn.rs +++ b/src/libstd/task/spawn.rs @@ -568,7 +568,8 @@ impl RuntimeGlue { let me = Local::unsafe_borrow::(); blk(match (*me).taskgroup { None => { - // Main task, doing first spawn ever. Lazily initialize. + // First task in its (unlinked/unsupervised) taskgroup. + // Lazily initialize. let mut members = TaskSet::new(); let my_handle = (*me).death.kill_handle.get_ref().clone(); members.insert(NewTask(my_handle)); @@ -591,37 +592,46 @@ impl RuntimeGlue { } } +// Returns 'None' in the case where the child's TG should be lazily initialized. fn gen_child_taskgroup(linked: bool, supervised: bool) - -> (TaskGroupArc, AncestorList, bool) { - do RuntimeGlue::with_my_taskgroup |spawner_group| { - let ancestors = AncestorList(spawner_group.ancestors.map(|x| x.clone())); - if linked { - // Child is in the same group as spawner. - // Child's ancestors are spawner's ancestors. - // Propagate main-ness. - (spawner_group.tasks.clone(), ancestors, spawner_group.is_main) - } else { - // Child is in a separate group from spawner. - let g = Exclusive::new(Some(TaskGroupData { - members: TaskSet::new(), - descendants: TaskSet::new(), - })); - let a = if supervised { - let new_generation = incr_generation(&ancestors); - assert!(new_generation < uint::max_value); - // Child's ancestors start with the spawner. - // Build a new node in the ancestor list. - AncestorList(Some(Exclusive::new(AncestorNode { - generation: new_generation, - parent_group: spawner_group.tasks.clone(), - ancestors: ancestors, - }))) + -> Option<(TaskGroupArc, AncestorList, bool)> { + // FIXME(#7544): Not safe to lazily initialize in the old runtime. Remove + // this context check once 'spawn_raw_oldsched' is gone. + if context() == OldTaskContext || linked || supervised { + // with_my_taskgroup will lazily initialize the parent's taskgroup if + // it doesn't yet exist. We don't want to call it in the unlinked case. + do RuntimeGlue::with_my_taskgroup |spawner_group| { + let ancestors = AncestorList(spawner_group.ancestors.map(|x| x.clone())); + if linked { + // Child is in the same group as spawner. + // Child's ancestors are spawner's ancestors. + // Propagate main-ness. + Some((spawner_group.tasks.clone(), ancestors, spawner_group.is_main)) } else { - // Child has no ancestors. - AncestorList(None) - }; - (g, a, false) + // Child is in a separate group from spawner. + let g = Exclusive::new(Some(TaskGroupData { + members: TaskSet::new(), + descendants: TaskSet::new(), + })); + let a = if supervised { + let new_generation = incr_generation(&ancestors); + assert!(new_generation < uint::max_value); + // Child's ancestors start with the spawner. + // Build a new node in the ancestor list. + AncestorList(Some(Exclusive::new(AncestorNode { + generation: new_generation, + parent_group: spawner_group.tasks.clone(), + ancestors: ancestors, + }))) + } else { + // Child has no ancestors. + AncestorList(None) + }; + Some((g, a, false)) + } } + } else { + None } } @@ -670,20 +680,24 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) { let child_wrapper: ~fn() = || { // Child task runs this code. - let child_data = Cell::new(child_data.take()); // :( - let enlist_success = do Local::borrow:: |me| { - let (child_tg, ancestors, is_main) = child_data.take(); - let mut ancestors = ancestors; - // FIXME(#7544): Optimize out the xadd in this clone, somehow. - let handle = me.death.kill_handle.get_ref().clone(); - // Atomically try to get into all of our taskgroups. - if enlist_many(NewTask(handle), &child_tg, &mut ancestors) { - // Got in. We can run the provided child body, and can also run - // the taskgroup's exit-time-destructor afterward. - me.taskgroup = Some(Taskgroup(child_tg, ancestors, is_main, None)); - true - } else { - false + + // If child data is 'None', the enlist is vacuously successful. + let enlist_success = do child_data.take().map_consume_default(true) |child_data| { + let child_data = Cell::new(child_data); // :( + do Local::borrow:: |me| { + let (child_tg, ancestors, is_main) = child_data.take(); + let mut ancestors = ancestors; + // FIXME(#7544): Optimize out the xadd in this clone, somehow. + let handle = me.death.kill_handle.get_ref().clone(); + // Atomically try to get into all of our taskgroups. + if enlist_many(NewTask(handle), &child_tg, &mut ancestors) { + // Got in. We can run the provided child body, and can also run + // the taskgroup's exit-time-destructor afterward. + me.taskgroup = Some(Taskgroup(child_tg, ancestors, is_main, None)); + true + } else { + false + } } }; // Should be run after the local-borrowed task is returned. @@ -749,7 +763,7 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) { let join_task = join_task_cell.take(); let bootstrap_task = ~do Task::new_root(&mut new_sched.stack_pool) || { - rtdebug!("boostraping a 1:1 scheduler"); + rtdebug!("bootstrapping a 1:1 scheduler"); }; new_sched.bootstrap(bootstrap_task); @@ -793,7 +807,7 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) { fn spawn_raw_oldsched(mut opts: TaskOpts, f: ~fn()) { let (child_tg, ancestors, is_main) = - gen_child_taskgroup(opts.linked, opts.supervised); + gen_child_taskgroup(opts.linked, opts.supervised).expect("old runtime needs TG"); unsafe { let child_data = Cell::new((child_tg, ancestors, f));