@@ -536,6 +536,7 @@ pub impl Coroutine {
536
536
537
537
priv fn build_start_wrapper ( start : ~fn ( ) ) -> ~fn ( ) {
538
538
// XXX: The old code didn't have this extra allocation
539
+ let start_cell = Cell ( start) ;
539
540
let wrapper: ~fn ( ) = || {
540
541
// This is the first code to execute after the initial
541
542
// context switch to the task. The previous context may
@@ -547,7 +548,19 @@ pub impl Coroutine {
547
548
let sched = Local :: unsafe_borrow :: < Scheduler > ( ) ;
548
549
let task = ( * sched) . current_task . get_mut_ref ( ) ;
549
550
// FIXME #6141: shouldn't neet to put `start()` in another closure
550
- task. task . run ( ||start ( ) ) ;
551
+ let start_cell = Cell ( start_cell. take ( ) ) ;
552
+ do task. task . run {
553
+ // N.B. Removing `start` from the start wrapper closure
554
+ // by emptying a cell is critical for correctness. The ~Task
555
+ // pointer, and in turn the closure used to initialize the first
556
+ // call frame, is destroyed in scheduler context, not task context.
557
+ // So any captured closures must not contain user-definable dtors
558
+ // that expect to be in task context. By moving `start` out of
559
+ // the closure, all the user code goes out of scope while
560
+ // the task is still running.
561
+ let start = start_cell. take ( ) ;
562
+ start ( ) ;
563
+ } ;
551
564
}
552
565
553
566
let sched = Local :: take :: < Scheduler > ( ) ;
@@ -840,4 +853,26 @@ mod test {
840
853
841
854
}
842
855
856
+ #[ test]
857
+ fn start_closure_dtor ( ) {
858
+ use ops:: Drop ;
859
+
860
+ // Regression test that the `start` task entrypoint can contain dtors
861
+ // that use task resources
862
+ do run_in_newsched_task {
863
+ struct S { field: ( ) }
864
+
865
+ impl Drop for S {
866
+ fn finalize ( & self ) {
867
+ let _foo = @0 ;
868
+ }
869
+ }
870
+
871
+ let s = S { field : ( ) } ;
872
+
873
+ do spawntask {
874
+ let _ss = & s;
875
+ }
876
+ }
877
+ }
843
878
}
0 commit comments