@@ -46,6 +46,7 @@ export run;
46
46
export future_result;
47
47
export future_task;
48
48
export unsupervise;
49
+ export parent;
49
50
export run_listener;
50
51
export run_with;
51
52
@@ -169,6 +170,7 @@ type sched_opts = {
169
170
*/
170
171
type task_opts = {
171
172
supervise : bool ,
173
+ parented : bool ,
172
174
notify_chan : option < comm:: chan < notification > > ,
173
175
sched : option < sched_opts > ,
174
176
} ;
@@ -206,6 +208,7 @@ fn default_task_opts() -> task_opts {
206
208
207
209
{
208
210
supervise: true,
211
+ parented: false ,
209
212
notify_chan: none,
210
213
sched: none
211
214
}
@@ -362,6 +365,11 @@ fn unsupervise(builder: builder) {
362
365
} ) ;
363
366
}
364
367
368
+ fn parent ( builder : builder ) {
369
+ //! Configures the new task to be killed if the parent group is killed.
370
+ set_opts ( builder, { parented: true with get_opts ( builder) } ) ;
371
+ }
372
+
365
373
fn run_with < A : send > ( -builder : builder ,
366
374
+arg : A ,
367
375
+f : fn ~( +A ) ) {
@@ -591,15 +599,19 @@ class taskgroup {
591
599
// FIXME (#2816): Change dvec to an O(1) data structure (and change 'me'
592
600
// to a node-handle or somesuch when so done (or remove the field entirely
593
601
// if keyed by *rust_task)).
594
- let tasks : taskgroup_arc; // 'none' means the group already failed.
595
602
let me: * rust_task;
596
- let my_pos: uint ;
597
- // let parent_group: taskgroup_arc; // FIXME (#1868) (bblum)
603
+ // List of tasks with whose fates this one's is intertwined.
604
+ let tasks: taskgroup_arc ; // 'none' means the group already failed.
605
+ let my_pos: uint ; // Index into above for this task's slot.
606
+ // Lists of tasks who will kill us if they fail, but whom we won't kill.
607
+ let parents: option < ( taskgroup_arc , uint ) > ;
598
608
let is_main: bool ;
599
- new ( -tasks : taskgroup_arc , me: * rust_task, my_pos : uint , is_main : bool ) {
600
- self . tasks = tasks ;
609
+ new ( me: * rust_task, -tasks : taskgroup_arc , my_pos : uint ,
610
+ -parents : option< ( taskgroup_arc , uint ) > , is_main : bool ) {
601
611
self . me = me;
612
+ self . tasks = tasks;
602
613
self . my_pos = my_pos;
614
+ self . parents = parents;
603
615
self . is_main = is_main;
604
616
}
605
617
// Runs on task exit.
@@ -609,9 +621,17 @@ class taskgroup {
609
621
// Take everybody down with us.
610
622
kill_taskgroup ( self . tasks , self . me , self . my_pos , self . is_main ) ;
611
623
} else {
612
- // Remove ourselves from the group.
624
+ // Remove ourselves from the group(s) .
613
625
leave_taskgroup( self . tasks , self . me , self . my_pos ) ;
614
626
}
627
+ // It doesn't matter whether this happens before or after dealing with
628
+ // our own taskgroup, so long as both happen before we die.
629
+ alt self. parents {
630
+ some ( ( parent_group, pos_in_group) ) {
631
+ leave_taskgroup ( parent_group, self . me , pos_in_group) ;
632
+ }
633
+ none { }
634
+ }
615
635
}
616
636
}
617
637
@@ -714,7 +734,8 @@ fn share_parent_taskgroup() -> (taskgroup_arc, bool) {
714
734
// Main task, doing first spawn ever.
715
735
let tasks = arc:: exclusive ( some ( ( dvec:: from_elem ( some ( me) ) ,
716
736
dvec:: dvec ( ) ) ) ) ;
717
- let group = @taskgroup ( tasks. clone ( ) , me, 0 , true ) ;
737
+ // Main group has no parent group.
738
+ let group = @taskgroup ( me, tasks. clone ( ) , 0 , none, true ) ;
718
739
unsafe { local_set ( me, taskgroup_key ( ) , group) ; }
719
740
// Tell child task it's also in the main group.
720
741
( tasks, true )
@@ -724,21 +745,29 @@ fn share_parent_taskgroup() -> (taskgroup_arc, bool) {
724
745
725
746
fn spawn_raw ( opts : task_opts , +f : fn ~( ) ) {
726
747
// Decide whether the child needs to be in a new linked failure group.
727
- let ( child_tg, is_main) = if opts. supervise {
728
- share_parent_taskgroup ( )
748
+ let ( ( child_tg, is_main) , parent_tg) = if opts. supervise {
749
+ // It doesn't mean anything for a linked-spawned-task to have a parent
750
+ // group. The spawning task is already bidirectionally linked to it.
751
+ ( share_parent_taskgroup ( ) , none)
729
752
} else {
730
753
// Detached from the parent group; create a new (non-main) one.
731
- ( arc:: exclusive ( some ( ( dvec:: dvec ( ) , dvec:: dvec ( ) ) ) ) , false )
754
+ ( ( arc:: exclusive ( some ( ( dvec:: dvec ( ) , dvec:: dvec ( ) ) ) ) , false ) ,
755
+ // Allow the parent to unidirectionally fail the child?
756
+ if opts. parented { // FIXME(#1868) rename to unsupervise.
757
+ let ( pg, _) = share_parent_taskgroup ( ) ; some ( pg)
758
+ } else {
759
+ none
760
+ } )
732
761
} ;
733
762
734
763
unsafe {
735
- let child_data_ptr = ~mut some ( ( child_tg, f) ) ;
764
+ let child_data_ptr = ~mut some ( ( child_tg, parent_tg , f) ) ;
736
765
// Being killed with the unsafe task/closure pointers would leak them.
737
766
do unkillable {
738
767
// Agh. Get move-mode items into the closure. FIXME (#2829)
739
768
let mut child_data = none;
740
769
* child_data_ptr <-> child_data;
741
- let ( child_tg, f) = option:: unwrap ( child_data) ;
770
+ let ( child_tg, parent_tg , f) = option:: unwrap ( child_data) ;
742
771
// Create child task.
743
772
let new_task = alt opts. sched {
744
773
none { rustrt : : new_task ( ) }
@@ -748,7 +777,7 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
748
777
// Getting killed after here would leak the task.
749
778
750
779
let child_wrapper =
751
- make_child_wrapper ( new_task, child_tg, is_main, f) ;
780
+ make_child_wrapper ( new_task, child_tg, parent_tg , is_main, f) ;
752
781
let fptr = ptr:: addr_of ( child_wrapper) ;
753
782
let closure: * rust_closure = unsafe :: reinterpret_cast ( fptr) ;
754
783
@@ -765,28 +794,63 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
765
794
}
766
795
}
767
796
768
- fn make_child_wrapper ( child_task : * rust_task , -child_tg : taskgroup_arc ,
769
- is_main : bool , -f : fn ~( ) ) -> fn ~( ) {
770
- let child_tg_ptr = ~mut some ( child_tg) ;
797
+ // This function returns a closure-wrapper that we pass to the child task.
798
+ // In brief, it does the following:
799
+ // if enlist_in_group(child_group) {
800
+ // if parent_group {
801
+ // if !enlist_in_group(parent_group) {
802
+ // leave_group(child_group); // Roll back
803
+ // ret; // Parent group failed. Don't run child's f().
804
+ // }
805
+ // }
806
+ // stash_taskgroup_data_in_TLS(child_group, parent_group);
807
+ // f();
808
+ // } else {
809
+ // // My group failed. Don't run chid's f().
810
+ // }
811
+ fn make_child_wrapper ( child : * rust_task , -child_tg : taskgroup_arc ,
812
+ -parent_tg : option < taskgroup_arc > , is_main : bool ,
813
+ -f : fn ~( ) ) -> fn ~( ) {
814
+ let child_tg_ptr = ~mut some ( ( child_tg, parent_tg) ) ;
771
815
fn ~( ) {
772
816
// Agh. Get move-mode items into the closure. FIXME (#2829)
773
- let mut child_tg_opt = none;
774
- * child_tg_ptr <-> child_tg_opt ;
775
- let child_tg = option:: unwrap ( child_tg_opt ) ;
817
+ let mut tg_data_opt = none;
818
+ * child_tg_ptr <-> tg_data_opt ;
819
+ let ( child_tg, parent_tg ) = option:: unwrap ( tg_data_opt ) ;
776
820
// Child task runs this code.
777
- // Set up membership in taskgroup. If this returns none, the
778
- // parent was already failing, so don't bother doing anything.
779
- alt enlist_in_taskgroup ( child_tg, child_task) {
780
- some ( my_index) {
781
- let group =
782
- @taskgroup ( child_tg, child_task, my_index, is_main) ;
783
- unsafe { local_set ( child_task, taskgroup_key ( ) , group) ; }
784
- // Run the child's body.
785
- f ( ) ;
786
- // TLS cleanup code will exit the taskgroup.
787
- }
788
- none {
821
+ // Set up membership in taskgroup. If this returns none, some
822
+ // task was already failing, so don't bother doing anything.
823
+ alt enlist_in_taskgroup ( child_tg, child) {
824
+ some ( my_pos) {
825
+ // Enlist in parent group too. If enlist returns none, a
826
+ // parent was failing: don't spawn; leave this group too.
827
+ let ( pg, enlist_ok) = if parent_tg. is_some ( ) {
828
+ let parent_group = option:: unwrap ( parent_tg) ;
829
+ alt enlist_in_taskgroup ( parent_group, child) {
830
+ some ( my_p_index) {
831
+ // Successful enlist.
832
+ ( some ( ( parent_group, my_p_index) ) , true )
833
+ }
834
+ none {
835
+ // Couldn't enlist. Have to quit here too.
836
+ leave_taskgroup( child_tg, child, my_pos) ;
837
+ ( none, false )
838
+ }
839
+ }
840
+ } else {
841
+ // No parent group to enlist in. No worry.
842
+ ( none, true )
843
+ } ;
844
+ if enlist_ok {
845
+ let group = @taskgroup ( child, child_tg, my_pos,
846
+ pg, is_main) ;
847
+ unsafe { local_set ( child, taskgroup_key ( ) , group) ; }
848
+ // Run the child's body.
849
+ f ( ) ;
850
+ // TLS cleanup code will exit the taskgroup.
851
+ }
789
852
}
853
+ none { }
790
854
}
791
855
}
792
856
}
0 commit comments