@@ -100,12 +100,21 @@ pub struct Task {
100
100
pub storage : LocalStorage ,
101
101
pub unwinder : Unwinder ,
102
102
pub death : Death ,
103
- pub destroyed : bool ,
104
103
pub name : Option < SendStr > ,
105
104
105
+ state : TaskState ,
106
106
imp : Option < Box < Runtime + Send > > ,
107
107
}
108
108
109
+ // Once a task has entered the `Armed` state it must be destroyed via `drop`,
110
+ // and no other method. This state is used to track this transition.
111
+ #[ deriving( PartialEq ) ]
112
+ enum TaskState {
113
+ New ,
114
+ Armed ,
115
+ Destroyed ,
116
+ }
117
+
109
118
pub struct TaskOpts {
110
119
/// Invoke this procedure with the result of the task when it finishes.
111
120
pub on_exit : Option < proc ( Result ) : Send > ,
@@ -159,7 +168,7 @@ impl Task {
159
168
storage : LocalStorage ( None ) ,
160
169
unwinder : Unwinder :: new ( ) ,
161
170
death : Death :: new ( ) ,
162
- destroyed : false ,
171
+ state : New ,
163
172
name : None ,
164
173
imp : None ,
165
174
}
@@ -203,7 +212,7 @@ impl Task {
203
212
/// }).destroy();
204
213
/// # }
205
214
/// ```
206
- pub fn run ( self : Box < Task > , f: ||) -> Box < Task > {
215
+ pub fn run ( mut self : Box < Task > , f: ||) -> Box < Task > {
207
216
assert ! ( !self . is_destroyed( ) , "cannot re-use a destroyed task" ) ;
208
217
209
218
// First, make sure that no one else is in TLS. This does not allow
@@ -212,6 +221,7 @@ impl Task {
212
221
if Local :: exists ( None :: < Task > ) {
213
222
fail ! ( "cannot run a task recursively inside another" ) ;
214
223
}
224
+ self . state = Armed ;
215
225
Local :: put ( self ) ;
216
226
217
227
// There are two primary reasons that general try/catch is unsafe. The
@@ -333,12 +343,12 @@ impl Task {
333
343
// Now that we're done, we remove the task from TLS and flag it for
334
344
// destruction.
335
345
let mut task: Box < Task > = Local :: take ( ) ;
336
- task. destroyed = true ;
346
+ task. state = Destroyed ;
337
347
return task;
338
348
}
339
349
340
350
/// Queries whether this can be destroyed or not.
341
- pub fn is_destroyed ( & self ) -> bool { self . destroyed }
351
+ pub fn is_destroyed ( & self ) -> bool { self . state == Destroyed }
342
352
343
353
/// Inserts a runtime object into this task, transferring ownership to the
344
354
/// task. It is illegal to replace a previous runtime object in this task
@@ -453,12 +463,20 @@ impl Task {
453
463
pub fn can_block ( & self ) -> bool {
454
464
self . imp . get_ref ( ) . can_block ( )
455
465
}
466
+
467
+ /// Consume this task, flagging it as a candidate for destruction.
468
+ ///
469
+ /// This function is required to be invoked to destroy a task. A task
470
+ /// destroyed through a normal drop will abort.
471
+ pub fn drop ( mut self ) {
472
+ self . state = Destroyed ;
473
+ }
456
474
}
457
475
458
476
impl Drop for Task {
459
477
fn drop ( & mut self ) {
460
478
rtdebug ! ( "called drop for a task: {}" , self as * mut Task as uint) ;
461
- rtassert ! ( self . destroyed ) ;
479
+ rtassert ! ( self . state != Armed ) ;
462
480
}
463
481
}
464
482
@@ -634,12 +652,17 @@ mod test {
634
652
begin_unwind ( "cause" , file ! ( ) , line ! ( ) )
635
653
}
636
654
655
+ #[ test]
656
+ fn drop_new_task_ok ( ) {
657
+ drop ( Task :: new ( ) ) ;
658
+ }
659
+
637
660
// Task blocking tests
638
661
639
662
#[ test]
640
663
fn block_and_wake ( ) {
641
664
let task = box Task :: new ( ) ;
642
665
let mut task = BlockedTask :: block ( task) . wake ( ) . unwrap ( ) ;
643
- task. destroyed = true ;
666
+ task. drop ( ) ;
644
667
}
645
668
}
0 commit comments