1- use crate :: runtime:: scheduler:: { local_queue:: LocalQueue , send_wrapper:: SendWrapper } ;
1+ use crate :: runtime:: scheduler:: {
2+ drop_hook:: DropHook , local_queue:: LocalQueue , send_wrapper:: SendWrapper ,
3+ } ;
24use async_task:: { Runnable , Task } ;
35use compio_driver:: NotifyHandle ;
46use crossbeam_queue:: SegQueue ;
5- use std:: { future:: Future , marker:: PhantomData , sync:: Arc } ;
7+ use slab:: Slab ;
8+ use std:: { cell:: RefCell , future:: Future , marker:: PhantomData , rc:: Rc , sync:: Arc , task:: Waker } ;
69
10+ mod drop_hook;
711mod local_queue;
812mod send_wrapper;
913
@@ -68,13 +72,38 @@ impl TaskQueue {
6872 let local_queue = unsafe { self . local_queue . get_unchecked ( ) } ;
6973 local_queue. is_empty ( ) && self . sync_queue . is_empty ( )
7074 }
75+
76+ /// Clears both queues.
77+ ///
78+ /// # Safety
79+ ///
80+ /// Call this method in the same thread as the creator.
81+ unsafe fn clear ( & self ) {
82+ // SAFETY: See the safety comment of this method.
83+ let local_queue = unsafe { self . local_queue . get_unchecked ( ) } ;
84+
85+ while let Some ( item) = local_queue. pop ( ) {
86+ drop ( item) ;
87+ }
88+
89+ while let Some ( item) = self . sync_queue . pop ( ) {
90+ drop ( item) ;
91+ }
92+ }
7193}
7294
7395/// A scheduler for managing and executing tasks.
7496pub ( crate ) struct Scheduler {
97+ /// Queue for scheduled tasks.
7598 task_queue : Arc < TaskQueue > ,
99+
100+ /// `Waker` of active tasks.
101+ active_tasks : Rc < RefCell < Slab < Waker > > > ,
102+
103+ /// Number of scheduler ticks for each `run` invocation.
76104 event_interval : usize ,
77- // `Scheduler` is `!Send` and `!Sync`.
105+
106+ /// Makes this type `!Send` and `!Sync`.
78107 _local_marker : PhantomData < * const ( ) > ,
79108}
80109
@@ -83,6 +112,7 @@ impl Scheduler {
83112 pub ( crate ) fn new ( event_interval : usize ) -> Self {
84113 Self {
85114 task_queue : Arc :: new ( TaskQueue :: new ( ) ) ,
115+ active_tasks : Rc :: new ( RefCell :: new ( Slab :: new ( ) ) ) ,
86116 event_interval,
87117 _local_marker : PhantomData ,
88118 }
@@ -101,20 +131,39 @@ impl Scheduler {
101131 where
102132 F : Future ,
103133 {
134+ let mut active_tasks = self . active_tasks . borrow_mut ( ) ;
135+ let task_entry = active_tasks. vacant_entry ( ) ;
136+
137+ let future = {
138+ let active_tasks = self . active_tasks . clone ( ) ;
139+ let index = task_entry. key ( ) ;
140+
141+ // Wrap the future with a drop hook to remove the waker upon completion.
142+ DropHook :: new ( future, move || {
143+ active_tasks. borrow_mut ( ) . try_remove ( index) ;
144+ } )
145+ } ;
146+
104147 let schedule = {
105- // Use `Weak` to break reference cycle.
106- // `TaskQueue` -> `Runnable` -> `TaskQueue`
148+ // The schedule closure is managed by the `Waker` and may be dropped on another thread,
149+ // so use `Weak` to ensure the `TaskQueue` is always dropped on the creator thread.
107150 let task_queue = Arc :: downgrade ( & self . task_queue ) ;
108151
109152 move |runnable| {
110- if let Some ( task_queue ) = task_queue . upgrade ( ) {
111- task_queue . push ( runnable , & notify ) ;
112- }
153+ // The `upgrade()` never fails because all tasks are dropped when the `Scheduler` is dropped,
154+ // if a `Waker` is used after that, the schedule closure will never be called.
155+ task_queue . upgrade ( ) . unwrap ( ) . push ( runnable , & notify ) ;
113156 }
114157 } ;
115158
116159 let ( runnable, task) = async_task:: spawn_unchecked ( future, schedule) ;
160+
161+ // Store the waker.
162+ task_entry. insert ( runnable. waker ( ) ) ;
163+
164+ // Schedule the task for execution.
117165 runnable. schedule ( ) ;
166+
118167 task
119168 }
120169
@@ -124,11 +173,13 @@ impl Scheduler {
124173 pub ( crate ) fn run ( & self ) -> bool {
125174 for _ in 0 ..self . event_interval {
126175 // SAFETY:
127- // `Scheduler` is `!Send` and `!Sync`, so this method is only called
128- // on `TaskQueue`'s creator thread .
176+ // This method is only called on `TaskQueue`'s creator thread
177+ // because `Scheduler` is `!Send` and `!Sync` .
129178 let tasks = unsafe { self . task_queue . pop ( ) } ;
130179
131180 // Run the tasks, which will poll the futures.
181+ //
182+ // SAFETY:
132183 // Since spawned tasks are not required to be `Send`, they must always be polled
133184 // on the same thread. Because `Scheduler` is `!Send` and `!Sync`, this is safe.
134185 match tasks {
@@ -147,8 +198,29 @@ impl Scheduler {
147198 }
148199
149200 // SAFETY:
150- // `Scheduler` is `!Send` and `!Sync`, so this method is only called
151- // on `TaskQueue`'s creator thread .
201+ // This method is only called on `TaskQueue`'s creator thread
202+ // because `Scheduler` is `!Send` and `!Sync` .
152203 !unsafe { self . task_queue . is_empty ( ) }
153204 }
205+
206+ /// Clears all active tasks.
207+ ///
208+ /// This method **must** be called before the scheduler is dropped.
209+ pub ( crate ) fn clear ( & self ) {
210+ // Drain and wake all wakers, which will schedule all active tasks.
211+ self . active_tasks
212+ . borrow_mut ( )
213+ . drain ( )
214+ . for_each ( |waker| waker. wake ( ) ) ;
215+
216+ // Then drop all scheduled tasks, which will drop all futures.
217+ //
218+ // SAFETY:
219+ // Since spawned tasks are not required to be `Send`, they must always be dropped
220+ // on the same thread. Because `Scheduler` is `!Send` and `!Sync`, this is safe.
221+ //
222+ // This method is only called on `TaskQueue`'s creator thread
223+ // because `Scheduler` is `!Send` and `!Sync`.
224+ unsafe { self . task_queue . clear ( ) } ;
225+ }
154226}
0 commit comments