@@ -50,33 +50,32 @@ use super::SpawnToken;
50
50
/// A task's complete life cycle is as follows:
51
51
///
52
52
/// ```text
53
- /// ┌────────────┐ ┌────────────────────────┐
54
- /// ┌─► │Not spawned │◄─6 ┤Not spawned|Run enqueued│
55
- /// │ │ ├7 ─►│ │
56
- /// │ └─────┬──────┘ └──────▲─────────────────┘
57
- /// │ 1 │
58
- /// │ │ ┌────────────┘
59
- /// │ │ 5
60
- /// │ ┌─────▼────┴─────────┐
61
- /// │ │ Spawned|Run enqueued│
62
- /// │ │ │
63
- /// │ └─────┬▲─────────────┘
64
- /// │ 2│
65
- /// │ │3
66
- /// │ ┌─────▼┴─────┐
67
- /// └─4┤ Spawned │
68
- /// │ │
69
- /// └────────────┘
53
+ /// ┌────────────┐ ┌────────────────────────┐
54
+ /// │Not spawned │◄─5 ┤Not spawned|Run enqueued│
55
+ /// │ ├6 ─►│ │
56
+ /// └─────┬──────┘ └──────▲─────────────────┘
57
+ /// 1 │
58
+ /// │ ┌────────────┘
59
+ /// │ 4
60
+ /// ┌─────▼────┴─────────┐
61
+ /// │Spawned|Run enqueued│
62
+ /// │ │
63
+ /// └─────┬▲─────────────┘
64
+ /// 2│
65
+ /// │3
66
+ /// ┌─────▼┴─────┐
67
+ /// │ Spawned │
68
+ /// │ │
69
+ /// └────────────┘
70
70
/// ```
71
71
///
72
72
/// Transitions:
73
73
/// - 1: Task is spawned - `AvailableTask::claim -> Executor::spawn`
74
74
/// - 2: During poll - `RunQueue::dequeue_all -> State::run_dequeue`
75
- /// - 3: Task wakes itself, waker wakes task - `Waker::wake -> wake_task -> State::run_enqueue`
76
- /// - 4: Task exits - `TaskStorage::poll -> Poll::Ready`
77
- /// - 5: A run-queued task exits - `TaskStorage::poll -> Poll::Ready`
78
- /// - 6: Task is dequeued and then ignored via `State::run_dequeue`
79
- /// - 7: A task is waken when it is not spawned - `wake_task -> State::run_enqueue`
75
+ /// - 3: Task wakes itself, waker wakes task, or task exits - `Waker::wake -> wake_task -> State::run_enqueue`
76
+ /// - 4: A run-queued task exits - `TaskStorage::poll -> Poll::Ready`
77
+ /// - 5: Task is dequeued. The task's future is not polled, because exiting the task replaces its `poll_fn`.
78
+ /// - 6: A task is waken when it is not spawned - `wake_task -> State::run_enqueue`
80
79
pub ( crate ) struct TaskHeader {
81
80
pub ( crate ) state : State ,
82
81
pub ( crate ) run_queue_item : RunQueueItem ,
@@ -162,6 +161,10 @@ pub struct TaskStorage<F: Future + 'static> {
162
161
future : UninitCell < F > , // Valid if STATE_SPAWNED
163
162
}
164
163
164
+ unsafe fn poll_exited ( _p : TaskRef ) {
165
+ // Nothing to do, the task is already !SPAWNED and dequeued.
166
+ }
167
+
165
168
impl < F : Future + ' static > TaskStorage < F > {
166
169
const NEW : Self = Self :: new ( ) ;
167
170
@@ -203,14 +206,23 @@ impl<F: Future + 'static> TaskStorage<F> {
203
206
}
204
207
205
208
unsafe fn poll ( p : TaskRef ) {
206
- let this = & * ( p. as_ptr ( ) as * const TaskStorage < F > ) ;
209
+ let this = & * p. as_ptr ( ) . cast :: < TaskStorage < F > > ( ) ;
207
210
208
211
let future = Pin :: new_unchecked ( this. future . as_mut ( ) ) ;
209
212
let waker = waker:: from_task ( p) ;
210
213
let mut cx = Context :: from_waker ( & waker) ;
211
214
match future. poll ( & mut cx) {
212
215
Poll :: Ready ( _) => {
216
+ // As the future has finished and this function will not be called
217
+ // again, we can safely drop the future here.
213
218
this. future . drop_in_place ( ) ;
219
+
220
+ // We replace the poll_fn with a despawn function, so that the task is cleaned up
221
+ // when the executor polls it next.
222
+ this. raw . poll_fn . set ( Some ( poll_exited) ) ;
223
+
224
+ // Make sure we despawn last, so that other threads can only spawn the task
225
+ // after we're done with it.
214
226
this. raw . state . despawn ( ) ;
215
227
}
216
228
Poll :: Pending => { }
@@ -411,15 +423,6 @@ impl SyncExecutor {
411
423
self . run_queue . dequeue_all ( |p| {
412
424
let task = p. header ( ) ;
413
425
414
- if !task. state . run_dequeue ( ) {
415
- // If task is not running, ignore it. This can happen in the following scenario:
416
- // - Task gets dequeued, poll starts
417
- // - While task is being polled, it gets woken. It gets placed in the queue.
418
- // - Task poll finishes, returning done=true
419
- // - RUNNING bit is cleared, but the task is already in the queue.
420
- return ;
421
- }
422
-
423
426
#[ cfg( feature = "trace" ) ]
424
427
trace:: task_exec_begin ( self , & p) ;
425
428
0 commit comments