@@ -591,16 +591,21 @@ class taskgroup {
591
591
let my_pos: uint ;
592
592
// let parent_group: taskgroup_arc; // TODO(bblum)
593
593
// TODO XXX bblum: add a list of empty slots to get runtime back
594
- let mut failed: bool ;
595
- new ( -tasks: taskgroup_arc, me: * rust_task, my_pos: uint) {
596
- self . tasks = tasks; self . me = me; self . my_pos = my_pos;
597
- self . failed = true ; // This will get un-set on successful exit.
594
+ // Indicates whether this is the main (root) taskgroup. If so, failure
595
+ // here should take down the entire runtime.
596
+ let is_main: bool ;
597
+ new ( -tasks: taskgroup_arc, me: * rust_task, my_pos: uint, is_main: bool ) {
598
+ self . tasks = tasks;
599
+ self . me = me;
600
+ self . my_pos = my_pos;
601
+ self . is_main = is_main;
598
602
}
599
603
// Runs on task exit.
600
604
drop {
601
- if self. failed {
605
+ // If we are failing, the whole taskgroup needs to die.
606
+ if rustrt:: rust_task_is_unwinding ( self . me ) {
602
607
// Take everybody down with us.
603
- kill_taskgroup( self . tasks , self . me , self . my_pos ) ;
608
+ kill_taskgroup ( self . tasks , self . me , self . my_pos , self . is_main ) ;
604
609
} else {
605
610
// Remove ourselves from the group.
606
611
leave_taskgroup( self . tasks , self . me , self . my_pos ) ;
@@ -642,7 +647,8 @@ fn leave_taskgroup(group_arc: taskgroup_arc, me: *rust_task, index: uint) {
642
647
}
643
648
644
649
// NB: Runs in destructor/post-exit context. Can't 'fail'.
645
- fn kill_taskgroup ( group_arc : taskgroup_arc , me : * rust_task , index : uint ) {
650
+ fn kill_taskgroup ( group_arc : taskgroup_arc , me : * rust_task , index : uint ,
651
+ is_main : bool ) {
646
652
// NB: We could do the killing iteration outside of the group arc, by
647
653
// having "let mut newstate" here, swapping inside, and iterating after.
648
654
// But that would let other exiting tasks fall-through and exit while we
@@ -667,32 +673,40 @@ fn kill_taskgroup(group_arc: taskgroup_arc, me: *rust_task, index: uint) {
667
673
rustrt:: rust_task_kill_other( task) ;
668
674
} ;
669
675
}
676
+ // Only one task should ever do this.
677
+ if is_main {
678
+ rustrt:: rust_task_kill_all ( me) ;
679
+ }
670
680
} ;
681
+ // (note: multiple tasks may reach this point)
671
682
} ;
672
683
}
673
684
674
- fn share_parent_taskgroup ( ) -> taskgroup_arc {
685
+ fn share_parent_taskgroup ( ) -> ( taskgroup_arc , bool ) {
675
686
let me = rustrt:: rust_get_task ( ) ;
676
687
alt unsafe { local_get ( me, taskgroup_key) } {
677
688
some ( group) {
678
- group. tasks . clone ( )
689
+ // Clone the shared state for the child; propagate main-ness.
690
+ ( group. tasks . clone ( ) , group. is_main )
679
691
}
680
692
none {
681
- /* Main task, doing first spawn ever. */
693
+ // Main task, doing first spawn ever.
682
694
let tasks = arc:: exclusive ( some ( dvec:: from_elem ( some ( me) ) ) ) ;
683
- let group = @taskgroup ( tasks. clone ( ) , me, 0 ) ;
695
+ let group = @taskgroup ( tasks. clone ( ) , me, 0 , true ) ;
684
696
unsafe { local_set ( me, taskgroup_key, group) ; }
685
- tasks
697
+ // Tell child task it's also in the main group.
698
+ ( tasks, true )
686
699
}
687
700
}
688
701
}
689
702
690
703
fn spawn_raw ( opts : task_opts , +f : fn ~( ) ) {
691
704
// Decide whether the child needs to be in a new linked failure group.
692
- let child_tg: taskgroup_arc = if opts. supervise {
705
+ let ( child_tg, is_main ) = if opts. supervise {
693
706
share_parent_taskgroup ( )
694
707
} else {
695
- arc:: exclusive ( some ( dvec:: from_elem ( none) ) )
708
+ // Detached from the parent group; create a new (non-main) one.
709
+ ( arc:: exclusive ( some ( dvec:: from_elem ( none) ) ) , false )
696
710
} ;
697
711
698
712
unsafe {
@@ -712,7 +726,8 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
712
726
// Getting killed after here would leak the task.
713
727
714
728
let child_wrapper =
715
- make_child_wrapper ( new_task, child_tg, opts. supervise , f) ;
729
+ make_child_wrapper ( new_task, child_tg,
730
+ opts. supervise , is_main, f) ;
716
731
let fptr = ptr:: addr_of ( child_wrapper) ;
717
732
let closure: * rust_closure = unsafe :: reinterpret_cast ( fptr) ;
718
733
@@ -730,7 +745,8 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
730
745
}
731
746
732
747
fn make_child_wrapper ( child_task : * rust_task , -child_tg : taskgroup_arc ,
733
- supervise : bool , -f : fn ~( ) ) -> fn ~( ) {
748
+ supervise : bool , is_main : bool ,
749
+ -f : fn ~( ) ) -> fn ~( ) {
734
750
let child_tg_ptr = ~mut some ( child_tg) ;
735
751
fn ~( ) {
736
752
// Agh. Get move-mode items into the closure. FIXME (#2829)
@@ -746,13 +762,12 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
746
762
// parent was already failing, so don't bother doing anything.
747
763
alt enlist_in_taskgroup ( child_tg, child_task) {
748
764
some ( my_index) {
749
- let group = @taskgroup ( child_tg, child_task, my_index) ;
765
+ let group =
766
+ @taskgroup ( child_tg, child_task, my_index, is_main) ;
750
767
unsafe { local_set ( child_task, taskgroup_key, group) ; }
751
768
// Run the child's body.
752
769
f ( ) ;
753
- // Report successful exit. (TLS cleanup code will tear
754
- // down the group.)
755
- group. failed = false ;
770
+ // TLS cleanup code will exit the taskgroup.
756
771
}
757
772
none { }
758
773
}
@@ -1006,6 +1021,7 @@ extern mod rustrt {
1006
1021
fn rust_task_inhibit_kill ( ) ;
1007
1022
fn rust_task_allow_kill ( ) ;
1008
1023
fn rust_task_kill_other ( task : * rust_task ) ;
1024
+ fn rust_task_kill_all ( task : * rust_task ) ;
1009
1025
1010
1026
#[ rust_stack]
1011
1027
fn rust_get_task_local_data ( task : * rust_task ) -> * libc:: c_void ;
0 commit comments