26
26
import result:: result;
27
27
import dvec:: extensions;
28
28
import dvec_iter:: extensions;
29
- import arc:: methods;
30
29
31
30
export task;
32
31
export task_result;
@@ -564,11 +563,7 @@ unsafe fn unkillable(f: fn()) {
564
563
}
565
564
566
565
567
- /****************************************************************************
568
- * Internal
569
- ****************************************************************************/
570
-
571
- /* spawning */
566
+ /* Internal */
572
567
573
568
type sched_id = int ;
574
569
type task_id = int ;
@@ -578,185 +573,42 @@ type task_id = int;
578
573
type rust_task = libc:: c_void ;
579
574
type rust_closure = libc:: c_void ;
580
575
581
- /* linked failure */
582
-
583
- type taskgroup_arc = arc:: exclusive < option < dvec:: dvec < option < * rust_task > > > > ;
584
-
585
- class taskgroup {
586
- // FIXME (#2816): Change dvec to an O(1) data structure (and change 'me'
587
- // to a node-handle or somesuch when so done (or remove the field entirely
588
- // if keyed by *rust_task)).
589
- let tasks : taskgroup_arc; // 'none' means the group already failed.
590
- let me: * rust_task ;
591
- let my_pos: uint ;
592
- // let parent_group: taskgroup_arc; // TODO(bblum)
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.
598
- }
599
- // Runs on task exit.
600
- drop {
601
- if self. failed {
602
- // Take everybody down with us.
603
- kill_taskgroup( self . tasks , self . me , self . my_pos ) ;
604
- } else {
605
- // Remove ourselves from the group.
606
- leave_taskgroup ( self . tasks , self . me , self . my_pos ) ;
607
- }
608
- }
609
- }
610
-
611
- fn taskgroup_key ( +_group : @taskgroup ) { } // For TLS
612
-
613
- fn enlist_in_taskgroup ( group_arc : taskgroup_arc ,
614
- me : * rust_task ) -> option < uint > {
615
- do group_arc. with |_c, state| {
616
- // If 'none', the group was failing. Can't enlist.
617
- do state. map |tasks| {
618
- // Try to find an empty slot.
619
- alt tasks. position ( |x| x == none) {
620
- some ( empty_index) {
621
- tasks. set_elt ( empty_index, some ( me) ) ;
622
- empty_index
623
- }
624
- none {
625
- tasks. push ( some ( me) ) ;
626
- tasks. len ( ) - 1
627
- }
628
- }
629
- }
630
- }
631
- }
632
-
633
- // NB: Runs in destructor/post-exit context. Can't 'fail'.
634
- fn leave_taskgroup ( group_arc : taskgroup_arc , me : * rust_task , index : uint ) {
635
- do group_arc. with |_c, state| {
636
- // If 'none', already failing and we've already gotten a kill signal.
637
- do state. map |tasks| {
638
- assert tasks[ index] == some ( me) ;
639
- tasks. set_elt ( index, none) ;
640
- } ;
641
- } ;
642
- }
643
-
644
- // NB: Runs in destructor/post-exit context. Can't 'fail'.
645
- fn kill_taskgroup ( group_arc : taskgroup_arc , me : * rust_task , index : uint ) {
646
- // NB: We could do the killing iteration outside of the group arc, by
647
- // having "let mut newstate" here, swapping inside, and iterating after.
648
- // But that would let other exiting tasks fall-through and exit while we
649
- // were trying to kill them, causing potential use-after-free. A task's
650
- // presence in the arc guarantees it's alive only while we hold the lock,
651
- // so if we're failing, all concurrently exiting tasks must wait for us.
652
- // To do it differently, we'd have to use the runtime's task refcounting.
653
- do group_arc. with |_c, state| {
654
- let mut newstate = none;
655
- * state <-> newstate;
656
- // Might already be none, if somebody is failing simultaneously.
657
- // That's ok; only one task needs to do the dirty work. (Might also
658
- // see 'none' if somebody already failed and we got a kill signal.)
659
- do newstate. map |tasks| {
660
- // First remove ourself (killing ourself won't do much good). This
661
- // is duplicated here to avoid having to lock twice.
662
- assert tasks[ index] == some ( me) ;
663
- tasks. set_elt ( index, none) ;
664
- // Now send takedown signal.
665
- for tasks. each |entry| {
666
- do entry. map |task| {
667
- rustrt:: rust_task_kill_other( task) ;
668
- } ;
669
- }
670
- } ;
671
- } ;
672
- }
673
-
674
- fn share_parent_taskgroup ( ) -> taskgroup_arc {
675
- let me = rustrt:: rust_get_task ( ) ;
676
- alt unsafe { local_get ( me, taskgroup_key) } {
677
- some ( group) {
678
- group. tasks . clone ( )
679
- }
680
- none {
681
- /* Main task, doing first spawn ever. */
682
- let tasks = arc:: exclusive ( some ( dvec:: from_elem ( some ( me) ) ) ) ;
683
- let group = @taskgroup ( tasks. clone ( ) , me, 0 ) ;
684
- unsafe { local_set ( me, taskgroup_key, group) ; }
685
- tasks
686
- }
687
- }
688
- }
689
-
690
576
fn spawn_raw ( opts : task_opts , +f : fn ~( ) ) {
691
- // Decide whether the child needs to be in a new linked failure group.
692
- let child_tg : taskgroup_arc = if opts. supervise {
693
- share_parent_taskgroup ( )
577
+
578
+ let mut f = if opts. supervise {
579
+ f
694
580
} else {
695
- arc:: exclusive ( some ( dvec:: from_elem ( none) ) )
581
+ // FIXME (#1868, #1789): The runtime supervision API is weird here
582
+ // because it was designed to let the child unsupervise itself,
583
+ // when what we actually want is for parents to unsupervise new
584
+ // children.
585
+ fn ~( ) {
586
+ rustrt:: unsupervise ( ) ;
587
+ f ( ) ;
588
+ }
696
589
} ;
697
590
698
591
unsafe {
699
- let child_data_ptr = ~mut some ( ( child_tg, f) ) ;
700
- // Being killed with the unsafe task/closure pointers would leak them.
701
- do unkillable {
702
- // Agh. Get move-mode items into the closure. FIXME (#2829)
703
- let mut child_data = none;
704
- * child_data_ptr <-> child_data;
705
- let ( child_tg, f) = option:: unwrap ( child_data) ;
706
- // Create child task.
707
- let new_task = alt opts. sched {
708
- none { rustrt : : new_task ( ) }
709
- some( sched_opts) { new_task_in_new_sched ( sched_opts) }
710
- } ;
711
- assert !new_task. is_null ( ) ;
712
- // Getting killed after here would leak the task.
713
-
714
- let child_wrapper =
715
- make_child_wrapper ( new_task, child_tg, opts. supervise , f) ;
716
- let fptr = ptr:: addr_of ( child_wrapper) ;
717
- let closure: * rust_closure = unsafe :: reinterpret_cast ( fptr) ;
718
-
719
- do option:: iter ( opts. notify_chan ) |c| {
720
- // FIXME (#1087): Would like to do notification in Rust
721
- rustrt:: rust_task_config_notify ( new_task, c) ;
722
- }
592
+ let fptr = ptr:: addr_of ( f) ;
593
+ let closure: * rust_closure = unsafe :: reinterpret_cast ( fptr) ;
723
594
724
- // Getting killed between these two calls would free the child's
725
- // closure. (Reordering them wouldn't help - then getting killed
726
- // between them would leak.)
727
- rustrt:: start_task ( new_task, closure) ;
728
- unsafe :: forget ( child_wrapper) ;
729
- }
730
- }
595
+ let new_task = alt opts. sched {
596
+ none {
597
+ rustrt : : new_task ( )
598
+ }
599
+ some( sched_opts) {
600
+ new_task_in_new_sched ( sched_opts)
601
+ }
602
+ } ;
603
+ assert !new_task. is_null ( ) ;
731
604
732
- fn make_child_wrapper ( child_task : * rust_task , -child_tg : taskgroup_arc ,
733
- supervise : bool , -f : fn ~( ) ) -> fn ~( ) {
734
- let child_tg_ptr = ~mut some ( child_tg) ;
735
- fn ~( ) {
736
- // Agh. Get move-mode items into the closure. FIXME (#2829)
737
- let mut child_tg_opt = none;
738
- * child_tg_ptr <-> child_tg_opt;
739
- let child_tg = option:: unwrap ( child_tg_opt) ;
740
- // Child task runs this code.
741
- if !supervise {
742
- // FIXME (#1868, #1789) take this out later
743
- rustrt:: unsupervise ( ) ;
744
- }
745
- // Set up membership in taskgroup. If this returns none, the
746
- // parent was already failing, so don't bother doing anything.
747
- alt enlist_in_taskgroup ( child_tg, child_task) {
748
- some ( my_index) {
749
- let group = @taskgroup ( child_tg, child_task, my_index) ;
750
- unsafe { local_set ( child_task, taskgroup_key, group) ; }
751
- // Run the child's body.
752
- f ( ) ;
753
- // Report successful exit. (TLS cleanup code will tear
754
- // down the group.)
755
- group. failed = false ;
756
- }
757
- none { }
758
- }
605
+ do option:: iter ( opts. notify_chan ) |c| {
606
+ // FIXME (#1087): Would like to do notification in Rust
607
+ rustrt:: rust_task_config_notify ( new_task, c) ;
759
608
}
609
+
610
+ rustrt:: start_task ( new_task, closure) ;
611
+ unsafe :: forget ( f) ;
760
612
}
761
613
762
614
fn new_task_in_new_sched ( opts : sched_opts ) -> * rust_task {
@@ -788,6 +640,7 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
788
640
} ;
789
641
rustrt:: rust_new_task_in_sched ( sched_id)
790
642
}
643
+
791
644
}
792
645
793
646
/****************************************************************************
@@ -907,7 +760,7 @@ unsafe fn local_get<T>(task: *rust_task,
907
760
local_get_helper ( task, key, false )
908
761
}
909
762
910
- unsafe fn local_set < T > ( task : * rust_task , key : local_data_key < T > , + data : @T ) {
763
+ unsafe fn local_set < T > ( task : * rust_task , key : local_data_key < T > , - data : @T ) {
911
764
let map = get_task_local_map ( task) ;
912
765
// Store key+data as *voids. Data is invisibly referenced once; key isn't.
913
766
let keyval = key_to_key_value ( key) ;
@@ -969,7 +822,7 @@ unsafe fn local_data_get<T>(key: local_data_key<T>) -> option<@T> {
969
822
* Store a value in task-local data. If this key already has a value,
970
823
* that value is overwritten (and its destructor is run).
971
824
*/
972
- unsafe fn local_data_set < T > ( key : local_data_key < T > , + data : @T ) {
825
+ unsafe fn local_data_set < T > ( key : local_data_key < T > , - data : @T ) {
973
826
local_set ( rustrt:: rust_get_task ( ) , key, data)
974
827
}
975
828
/**
@@ -1000,12 +853,11 @@ extern mod rustrt {
1000
853
1001
854
fn start_task ( task : * rust_task , closure : * rust_closure ) ;
1002
855
1003
- fn rust_task_is_unwinding ( task : * rust_task ) -> bool ;
856
+ fn rust_task_is_unwinding ( rt : * rust_task ) -> bool ;
1004
857
fn unsupervise ( ) ;
1005
858
fn rust_osmain_sched_id ( ) -> sched_id ;
1006
859
fn rust_task_inhibit_kill ( ) ;
1007
860
fn rust_task_allow_kill ( ) ;
1008
- fn rust_task_kill_other ( task : * rust_task ) ;
1009
861
1010
862
#[ rust_stack]
1011
863
fn rust_get_task_local_data ( task : * rust_task ) -> * libc:: c_void ;
@@ -1380,7 +1232,7 @@ fn test_unkillable() {
1380
1232
let ch = po. chan ( ) ;
1381
1233
1382
1234
// We want to do this after failing
1383
- do spawn_raw ( { supervise : false with default_task_opts ( ) } ) {
1235
+ do spawn {
1384
1236
for iter:: repeat ( 10 u) { yield ( ) }
1385
1237
ch. send ( ( ) ) ;
1386
1238
}
@@ -1417,7 +1269,7 @@ fn test_unkillable_nested() {
1417
1269
let ch = po. chan ( ) ;
1418
1270
1419
1271
// We want to do this after failing
1420
- do spawn_raw ( { supervise : false with default_task_opts ( ) } ) {
1272
+ do spawn {
1421
1273
for iter:: repeat ( 10 u) { yield ( ) }
1422
1274
ch. send ( ( ) ) ;
1423
1275
}
0 commit comments