@@ -42,12 +42,24 @@ pub(crate) type TaskListItem = Option<TaskPtr>;
4242
4343/// An abstraction that allows the task to contain multiple different queue pointers.
4444pub ( crate ) trait TaskListElement : Default {
45+ /// Returns the pointer to the next element in the list.
4546 fn next ( task : TaskPtr ) -> Option < TaskPtr > ;
47+
48+ /// Sets the pointer to the next element in the list.
4649 fn set_next ( task : TaskPtr , next : Option < TaskPtr > ) ;
50+
51+ /// Returns whether the task is in the list. If this function returns `None`, we don't know.
52+ fn is_in_queue ( _task : TaskPtr ) -> Option < bool > {
53+ // By default we don't store this information, so we return "Don't know".
54+ None
55+ }
56+
57+ /// Marks whether the task is in the list.
58+ fn mark_in_queue ( _task : TaskPtr , _in_queue : bool ) { }
4759}
4860
4961macro_rules! task_list_item {
50- ( $struct: ident, $field: ident) => {
62+ ( $struct: ident, $field: ident $ ( , $in_queue_field : ident ) ? ) => {
5163 #[ derive( Default ) ]
5264 pub ( crate ) struct $struct;
5365 impl TaskListElement for $struct {
@@ -60,14 +72,27 @@ macro_rules! task_list_item {
6072 task. as_mut( ) . $field = next;
6173 }
6274 }
75+
76+ $(
77+ fn is_in_queue( task: TaskPtr ) -> Option <bool > {
78+ Some ( unsafe { task. as_ref( ) . $in_queue_field } )
79+ }
80+
81+ fn mark_in_queue( mut task: TaskPtr , in_queue: bool ) {
82+ unsafe {
83+ task. as_mut( ) . $in_queue_field = in_queue;
84+ }
85+ }
86+ ) ?
6387 }
6488 } ;
6589}
6690
91+ task_list_item ! ( TaskReadyQueueElement , ready_queue_item, run_queued) ;
92+ task_list_item ! ( TaskTimerQueueElement , timer_queue_item, timer_queued) ;
93+ // These aren't perf critical, no need to waste memory on caching list status:
6794task_list_item ! ( TaskAllocListElement , alloc_list_item) ;
68- task_list_item ! ( TaskReadyQueueElement , ready_queue_item) ;
6995task_list_item ! ( TaskDeleteListElement , delete_list_item) ;
70- task_list_item ! ( TaskTimerQueueElement , timer_queue_item) ;
7196
7297/// Extension trait for common task operations. These should be inherent methods but we can't
7398/// implement stuff for NonNull.
@@ -153,6 +178,11 @@ impl<E: TaskListElement> TaskList<E> {
153178 }
154179
155180 pub fn push ( & mut self , task : TaskPtr ) {
181+ if E :: is_in_queue ( task) == Some ( true ) {
182+ return ;
183+ }
184+ E :: mark_in_queue ( task, true ) ;
185+
156186 debug_assert ! ( E :: next( task) . is_none( ) ) ;
157187 E :: set_next ( task, self . head ) ;
158188 self . head = Some ( task) ;
@@ -164,12 +194,18 @@ impl<E: TaskListElement> TaskList<E> {
164194 if let Some ( task) = popped {
165195 self . head = E :: next ( task) ;
166196 E :: set_next ( task, None ) ;
197+ E :: mark_in_queue ( task, false ) ;
167198 }
168199
169200 popped
170201 }
171202
172203 pub fn remove ( & mut self , task : TaskPtr ) {
204+ if E :: is_in_queue ( task) == Some ( false ) {
205+ return ;
206+ }
207+ E :: mark_in_queue ( task, false ) ;
208+
173209 // TODO: maybe this (and TaskQueue::remove) may prove too expensive.
174210 let mut list = core:: mem:: take ( self ) ;
175211 while let Some ( popped) = list. pop ( ) {
@@ -213,6 +249,11 @@ impl<E: TaskListElement> TaskQueue<E> {
213249 }
214250
215251 pub fn push ( & mut self , task : TaskPtr ) {
252+ if E :: is_in_queue ( task) == Some ( true ) {
253+ return ;
254+ }
255+ E :: mark_in_queue ( task, true ) ;
256+
216257 debug_assert ! ( E :: next( task) . is_none( ) ) ;
217258 if let Some ( tail) = self . tail {
218259 E :: set_next ( tail, Some ( task) ) ;
@@ -231,41 +272,39 @@ impl<E: TaskListElement> TaskQueue<E> {
231272 if self . head . is_none ( ) {
232273 self . tail = None ;
233274 }
275+ E :: mark_in_queue ( task, false ) ;
234276 }
235277
236278 popped
237279 }
238280
239281 #[ cfg( multi_core) ]
240282 pub fn pop_if ( & mut self , cond : impl Fn ( & Task ) -> bool ) -> Option < TaskPtr > {
241- let mut head = self . head . take ( ) ;
242- self . tail = None ;
243-
244283 let mut popped = None ;
245- while let Some ( task) = head {
246- head = E :: next ( task) ;
247- E :: set_next ( task, None ) ;
248- if cond ( unsafe { task. as_ref ( ) } ) {
284+
285+ let mut list = core:: mem:: take ( self ) ;
286+ while let Some ( task) = list. pop ( ) {
287+ if popped. is_none ( ) && cond ( unsafe { task. as_ref ( ) } ) {
288+ E :: mark_in_queue ( task, false ) ;
249289 popped = Some ( task) ;
250- break ;
251290 } else {
252291 self . push ( task) ;
253292 }
254293 }
255294
256- while let Some ( task) = head {
257- head = E :: next ( task) ;
258- E :: set_next ( task, None ) ;
259- self . push ( task) ;
260- }
261-
262295 popped
263296 }
264297
265298 pub fn remove ( & mut self , task : TaskPtr ) {
299+ if E :: is_in_queue ( task) == Some ( false ) {
300+ return ;
301+ }
302+
266303 let mut list = core:: mem:: take ( self ) ;
267304 while let Some ( popped) = list. pop ( ) {
268- if popped != task {
305+ if popped == task {
306+ E :: mark_in_queue ( task, false ) ;
307+ } else {
269308 self . push ( popped) ;
270309 }
271310 }
@@ -372,6 +411,11 @@ pub(crate) struct Task {
372411
373412 pub wakeup_at : u64 ,
374413
414+ /// Whether the task is currently queued in the run queue.
415+ pub run_queued : bool ,
416+ /// Whether the task is currently queued in the timer queue.
417+ pub timer_queued : bool ,
418+
375419 /// The current wait queue this task is in.
376420 pub ( crate ) current_queue : Option < NonNull < WaitQueue > > ,
377421
@@ -463,6 +507,8 @@ impl Task {
463507 pinned_to,
464508
465509 wakeup_at : 0 ,
510+ timer_queued : false ,
511+ run_queued : false ,
466512
467513 alloc_list_item : TaskListItem :: None ,
468514 ready_queue_item : TaskListItem :: None ,
@@ -555,6 +601,8 @@ pub(super) fn allocate_main_task(
555601 scheduler. per_cpu [ current_cpu] . main_task . priority = Priority :: ZERO ;
556602 scheduler. per_cpu [ current_cpu] . main_task . state = TaskState :: Ready ;
557603 scheduler. per_cpu [ current_cpu] . main_task . stack = stack;
604+ scheduler. per_cpu [ current_cpu] . main_task . run_queued = false ;
605+ scheduler. per_cpu [ current_cpu] . main_task . timer_queued = false ;
558606 #[ cfg( multi_core) ]
559607 {
560608 scheduler. per_cpu [ current_cpu] . main_task . pinned_to = Some ( cpu) ;
0 commit comments