Skip to content

Commit 7d9c61a

Browse files
committed
Linked failure touchups (#1868)
1 parent ac0c8b0 commit 7d9c61a

File tree

1 file changed

+41
-28
lines changed

1 file changed

+41
-28
lines changed

src/libcore/task.rs

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,12 @@ type task_opts = {
185185
// when you try to reuse the builder to spawn a new task. We'll just
186186
// sidestep that whole issue by making builders uncopyable and making
187187
// the run function move them in.
188-
class dummy { let x: (); new() { self.x = (); } drop { } }
189188

190189
// FIXME (#2585): Replace the 'consumed' bit with move mode on self
191190
enum task_builder = {
192191
opts: task_opts,
193192
gen_body: fn@(+fn~()) -> fn~(),
194-
can_not_copy: option<dummy>,
193+
can_not_copy: option<util::noncopyable>,
195194
mut consumed: bool,
196195
};
197196

@@ -598,8 +597,8 @@ unsafe fn atomically<U>(f: fn() -> U) -> U {
598597
*
599598
* A new one of these is created each spawn_linked or spawn_supervised.
600599
*
601-
* (2) The "taskgroup" is a per-task control structure that tracks a task's
602-
* spawn configuration. It contains a reference to its taskgroup_arc,
600+
* (2) The "tcb" is a per-task control structure that tracks a task's spawn
601+
* configuration. It contains a reference to its taskgroup_arc, a
603602
* a reference to its node in the ancestor list (below), a flag for
604603
* whether it's part of the 'main'/'root' taskgroup, and an optionally
605604
* configured notification port. These are stored in TLS.
@@ -641,8 +640,8 @@ unsafe fn atomically<U>(f: fn() -> U) -> U {
641640
* ( E )- - - - - - - > | {E} {} |
642641
* \___/ |_________|
643642
*
644-
* "taskgroup" "taskgroup_arc"
645-
* "ancestor_list"
643+
* "tcb" "taskgroup_arc"
644+
* "ancestor_list"
646645
*
647646
****************************************************************************/
648647

@@ -694,6 +693,7 @@ type taskgroup_arc = arc::exclusive<option<taskgroup_data>>;
694693

695694
type taskgroup_inner = &mut option<taskgroup_data>;
696695

696+
// A taskgroup is 'dead' when nothing can cause it to fail; only members can.
697697
fn taskgroup_is_dead(tg: taskgroup_data) -> bool {
698698
(&mut tg.members).is_empty()
699699
}
@@ -705,7 +705,7 @@ fn taskgroup_is_dead(tg: taskgroup_data) -> bool {
705705
// taskgroup which was spawned-unlinked. Tasks from intermediate generations
706706
// have references to the middle of the list; when intermediate generations
707707
// die, their node in the list will be collected at a descendant's spawn-time.
708-
enum ancestor_list = option<arc::exclusive<{
708+
type ancestor_node = {
709709
// Since the ancestor list is recursive, we end up with references to
710710
// exclusives within other exclusives. This is dangerous business (if
711711
// circular references arise, deadlock and memory leaks are imminent).
@@ -717,7 +717,19 @@ enum ancestor_list = option<arc::exclusive<{
717717
mut parent_group: option<taskgroup_arc>,
718718
// Recursive rest of the list.
719719
mut ancestors: ancestor_list,
720-
}>>;
720+
};
721+
enum ancestor_list = option<arc::exclusive<ancestor_node>>;
722+
723+
// Accessors for taskgroup arcs and ancestor arcs that wrap the unsafety.
724+
#[inline(always)]
725+
fn access_group<U>(x: taskgroup_arc, blk: fn(taskgroup_inner) -> U) -> U {
726+
unsafe { x.with(|_c, tg| blk(tg)) }
727+
}
728+
#[inline(always)]
729+
fn access_ancestors<U>(x: arc::exclusive<ancestor_node>,
730+
blk: fn(x: &mut ancestor_node) -> U) -> U {
731+
unsafe { x.with(|_c, nobe| blk(nobe)) }
732+
}
721733

722734
// Iterates over an ancestor list.
723735
// (1) Runs forward_blk on each ancestral taskgroup in the list
@@ -782,7 +794,7 @@ fn each_ancestor(list: &mut ancestor_list,
782794
// the end of the list, which doesn't make sense to coalesce.
783795
return do (*ancestors).map_default((none,false)) |ancestor_arc| {
784796
// NB: Takes a lock! (this ancestor node)
785-
do ancestor_arc.with |_c, nobe| {
797+
do access_ancestors(ancestor_arc) |nobe| {
786798
// Check monotonicity
787799
assert last_generation > nobe.generation;
788800
/*##########################################################*
@@ -847,15 +859,15 @@ fn each_ancestor(list: &mut ancestor_list,
847859
// If this trips, more likely the problem is 'blk' failed inside.
848860
assert tmp.is_some();
849861
let tmp_arc = option::unwrap(tmp);
850-
let result = do tmp_arc.with |_c, tg_opt| { blk(tg_opt) };
862+
let result = do access_group(tmp_arc) |tg_opt| { blk(tg_opt) };
851863
*parent_group <- some(tmp_arc);
852864
result
853865
}
854866
}
855867
}
856868

857869
// One of these per task.
858-
class taskgroup {
870+
class tcb {
859871
let me: *rust_task;
860872
// List of tasks with whose fates this one's is intertwined.
861873
let tasks: taskgroup_arc; // 'none' means the group has failed.
@@ -878,12 +890,12 @@ class taskgroup {
878890
if rustrt::rust_task_is_unwinding(self.me) {
879891
self.notifier.iter(|x| { x.failed = true; });
880892
// Take everybody down with us.
881-
do self.tasks.with |_c, tg| {
893+
do access_group(self.tasks) |tg| {
882894
kill_taskgroup(tg, self.me, self.is_main);
883895
}
884896
} else {
885897
// Remove ourselves from the group(s).
886-
do self.tasks.with |_c, tg| {
898+
do access_group(self.tasks) |tg| {
887899
leave_taskgroup(tg, self.me, true);
888900
}
889901
}
@@ -977,7 +989,7 @@ fn kill_taskgroup(state: taskgroup_inner, me: *rust_task, is_main: bool) {
977989

978990
// FIXME (#2912): Work around core-vs-coretest function duplication. Can't use
979991
// a proper closure because the #[test]s won't understand. Have to fake it.
980-
unsafe fn taskgroup_key() -> local_data_key<taskgroup> {
992+
unsafe fn taskgroup_key() -> local_data_key<tcb> {
981993
// Use a "code pointer" value that will never be a real code pointer.
982994
unsafe::transmute((-2 as uint, 0u))
983995
}
@@ -998,13 +1010,12 @@ fn gen_child_taskgroup(linked: bool, supervised: bool)
9981010
mut descendants: new_taskset() }));
9991011
// Main task/group has no ancestors, no notifier, etc.
10001012
let group =
1001-
@taskgroup(spawner, tasks, ancestor_list(none), true, none);
1013+
@tcb(spawner, tasks, ancestor_list(none), true, none);
10021014
unsafe { local_set(spawner, taskgroup_key(), group); }
10031015
group
10041016
}
10051017
some(group) { group }
10061018
};
1007-
//for each_ancestor(&mut spawner_group.ancestors, none) |_a| { };
10081019
/*######################################################################*
10091020
* Step 2. Process spawn options for child.
10101021
*######################################################################*/
@@ -1027,7 +1038,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool)
10271038
// it should be enabled only in debug builds.
10281039
let new_generation =
10291040
alt *old_ancestors {
1030-
some(arc) { do arc.with |_c,nobe| { nobe.generation+1 } }
1041+
some(arc) { access_ancestors(arc, |a| a.generation+1) }
10311042
none { 0 } // the actual value doesn't really matter.
10321043
};
10331044
assert new_generation < uint::max_value;
@@ -1118,8 +1129,8 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
11181129
let notifier = notify_chan.map(|c| auto_notify(c));
11191130

11201131
if enlist_many(child, child_arc, &mut ancestors) {
1121-
let group = @taskgroup(child, child_arc, ancestors,
1122-
is_main, notifier);
1132+
let group = @tcb(child, child_arc, ancestors,
1133+
is_main, notifier);
11231134
unsafe { local_set(child, taskgroup_key(), group); }
11241135
// Run the child's body.
11251136
f();
@@ -1134,7 +1145,7 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
11341145
ancestors: &mut ancestor_list) -> bool {
11351146
// Join this taskgroup.
11361147
let mut result =
1137-
do child_arc.with |_c, child_tg| {
1148+
do access_group(child_arc) |child_tg| {
11381149
enlist_in_taskgroup(child_tg, child, true) // member
11391150
};
11401151
if result {
@@ -1151,7 +1162,7 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
11511162
};
11521163
// If any ancestor group fails, need to exit this group too.
11531164
if !result {
1154-
do child_arc.with |_c, child_tg| {
1165+
do access_group(child_arc) |child_tg| {
11551166
leave_taskgroup(child_tg, child, true); // member
11561167
}
11571168
}
@@ -1485,7 +1496,7 @@ fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port
14851496
do spawn_unlinked {
14861497
do spawn_unlinked {
14871498
// Give middle task a chance to fail-but-not-kill-us.
1488-
for iter::repeat(128) { task::yield(); }
1499+
for iter::repeat(16) { task::yield(); }
14891500
comm::send(ch, ()); // If killed first, grandparent hangs.
14901501
}
14911502
fail; // Shouldn't kill either (grand)parent or (grand)child.
@@ -1500,7 +1511,7 @@ fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails
15001511
fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
15011512
do spawn_supervised { fail; }
15021513
// Give child a chance to fail-but-not-kill-us.
1503-
for iter::repeat(128) { task::yield(); }
1514+
for iter::repeat(16) { task::yield(); }
15041515
}
15051516
#[test] #[should_fail] #[ignore(cfg(windows))]
15061517
fn test_spawn_unlinked_sup_fail_down() {
@@ -1569,7 +1580,7 @@ fn test_spawn_failure_propagate_grandchild() {
15691580
loop { task::yield(); }
15701581
}
15711582
}
1572-
for iter::repeat(128) { task::yield(); }
1583+
for iter::repeat(16) { task::yield(); }
15731584
fail;
15741585
}
15751586

@@ -1581,7 +1592,7 @@ fn test_spawn_failure_propagate_secondborn() {
15811592
loop { task::yield(); }
15821593
}
15831594
}
1584-
for iter::repeat(128) { task::yield(); }
1595+
for iter::repeat(16) { task::yield(); }
15851596
fail;
15861597
}
15871598

@@ -1593,7 +1604,7 @@ fn test_spawn_failure_propagate_nephew_or_niece() {
15931604
loop { task::yield(); }
15941605
}
15951606
}
1596-
for iter::repeat(128) { task::yield(); }
1607+
for iter::repeat(16) { task::yield(); }
15971608
fail;
15981609
}
15991610

@@ -1605,7 +1616,7 @@ fn test_spawn_linked_sup_propagate_sibling() {
16051616
loop { task::yield(); }
16061617
}
16071618
}
1608-
for iter::repeat(128) { task::yield(); }
1619+
for iter::repeat(16) { task::yield(); }
16091620
fail;
16101621
}
16111622

@@ -2006,7 +2017,9 @@ fn test_atomically_nested() {
20062017
fn test_child_doesnt_ref_parent() {
20072018
// If the child refcounts the parent task, this will stack overflow when
20082019
// climbing the task tree to dereference each ancestor. (See #1789)
2009-
const generations: uint = 128;
2020+
// (well, it would if the constant were 8000+ - I lowered it to be more
2021+
// valgrind-friendly. try this at home, instead..!)
2022+
const generations: uint = 16;
20102023
fn child_no(x: uint) -> fn~() {
20112024
return || {
20122025
if x < generations {

0 commit comments

Comments
 (0)