2 * Copyright (c) 2018 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 #include <kern/priority_queue.h>
31 #include <mach/vm_param.h>
34 static_assert(PRIORITY_QUEUE_ENTRY_CHILD_BITS
>= VM_KERNEL_POINTER_SIGNIFICANT_BITS
,
35 "Priority Queue child pointer packing failed");
39 #pragma mark priority queue helpers
42 * These traits allow to parametrize `struct pqueue` below.
45 template <typename queue_t
, typename entry_t
>
46 struct pqueue_entry_traits
{
48 * Explain how to compare two elements in the natural order.
51 compare(queue_t que
, entry_t a
, entry_t b
);
54 template <typename queue_t
>
55 struct pqueue_entry_traits
<queue_t
, priority_queue_entry_t
> {
57 compare(queue_t que
, priority_queue_entry_t e1
, priority_queue_entry_t e2
)
59 return que
->pq_cmp_fn(e1
, e2
);
63 template <typename queue_t
>
64 struct pqueue_entry_traits
<queue_t
, priority_queue_entry_deadline_t
> {
66 compare(queue_t que __unused
,
67 priority_queue_entry_deadline_t e1
, priority_queue_entry_deadline_t e2
)
69 return priority_heap_compare_ints(e1
->deadline
, e2
->deadline
);
73 template <typename queue_t
>
74 struct pqueue_entry_traits
<queue_t
, priority_queue_entry_sched_t
> {
76 compare(queue_t que __unused
,
77 priority_queue_entry_sched_t e1
, priority_queue_entry_sched_t e2
)
79 return (int)e2
->key
- (int)e1
->key
;
83 template <typename queue_t
>
84 struct pqueue_entry_traits
<queue_t
, priority_queue_entry_stable_t
> {
86 compare(queue_t que __unused
,
87 priority_queue_entry_stable_t e1
, priority_queue_entry_stable_t e2
)
90 * the key is (2 * pri + preempted) so preempted entries
91 * sort "higher" than non preempted entries at the same priority.
93 if (e1
->key
!= e2
->key
) {
94 return (int)e2
->key
- (int)e1
->key
;
96 if (e1
->stamp
!= e2
->stamp
) {
98 * preempted entries: younger (bigger timestamp) is "higher"
99 * non preempted entries: older (smaller timestamp) is "higher"
101 if (e1
->key
& PRIORITY_QUEUE_ENTRY_PREEMPTED
) {
102 return e1
->stamp
< e2
->stamp
? 1 : -1;
104 return e1
->stamp
> e2
->stamp
? 1 : -1;
111 #pragma mark main template
114 * Template for our priority queue.
116 * It is parametrized with:
117 * - `queue_t`: the queue type
118 * - `entry_t`: the element type
121 * - priority_queue_is_min_heap() to determine if it is a min/max heap
122 * - pqueue_entry_traits<queue_t, entry_t>::compare for the ordering
124 template <typename queue_t
, typename entry_t
>
126 using entry_traits
= pqueue_entry_traits
<queue_t
, entry_t
>;
129 pack_child(entry_t e
, const entry_t child
)
131 e
->child
= (long)child
;
134 static inline entry_t
135 unpack_child(entry_t e
)
137 return (entry_t
)e
->child
;
142 merge_parent_is_subtree_b(queue_t que
, entry_t subtree_a
, entry_t subtree_b
)
144 if (priority_queue_is_max_heap((queue_t
)nullptr)) {
145 return entry_traits::compare(que
, subtree_a
, subtree_b
) > 0;
147 return entry_traits::compare(que
, subtree_a
, subtree_b
) < 0;
150 static inline entry_t
151 merge_pair_inline(queue_t que
, entry_t subtree_a
, entry_t subtree_b
)
153 entry_t merge_result
= NULL
;
154 if (subtree_a
== NULL
) {
155 merge_result
= subtree_b
;
156 } else if (subtree_b
== NULL
|| (subtree_a
== subtree_b
)) {
157 merge_result
= subtree_a
;
159 entry_t parent
= subtree_a
;
160 entry_t child
= subtree_b
;
161 if (merge_parent_is_subtree_b(que
, subtree_a
, subtree_b
)) {
165 /* Insert the child as the first element in the parent's child list */
166 child
->next
= unpack_child(parent
);
167 child
->prev
= parent
;
168 if (unpack_child(parent
) != NULL
) {
169 unpack_child(parent
)->prev
= child
;
171 /* Create the parent child relationship */
172 pack_child(parent
, child
);
175 merge_result
= parent
;
182 merge_pair(queue_t que
, entry_t subtree_a
, entry_t subtree_b
)
184 return merge_pair_inline(que
, subtree_a
, subtree_b
);
189 meld_pair(queue_t que
, entry_t elt
)
191 entry_t pq_meld_result
= NULL
;
192 entry_t pair_list
= NULL
;
194 assert(elt
); // caller needs to check this.
197 /* Split the list into a set of pairs going front to back. */
198 /* Hook these pairs onto an intermediary list in reverse order of traversal.*/
201 /* Consider two elements at a time for pairing */
202 entry_t pair_item_a
= elt
;
203 entry_t pair_item_b
= elt
->next
;
204 if (pair_item_b
== NULL
) {
205 /* Odd number of elements in the list; link the odd element */
206 /* as it is on the intermediate list. */
207 pair_item_a
->prev
= pair_list
;
208 pair_list
= pair_item_a
;
211 /* Found two elements to pair up */
212 elt
= pair_item_b
->next
;
213 entry_t pair
= merge_pair_inline(que
, pair_item_a
, pair_item_b
);
214 /* Link the pair onto the intermediary list */
215 pair
->prev
= pair_list
;
217 } while (elt
!= NULL
);
219 /* Phase 2: Merge all the pairs in the pair_list */
221 elt
= pair_list
->prev
;
222 pq_meld_result
= merge_pair_inline(que
, pq_meld_result
, pair_list
);
224 } while (pair_list
!= NULL
);
226 return pq_meld_result
;
230 list_remove(entry_t elt
)
232 assert(elt
->prev
!= NULL
);
233 /* Check if elt is head of list at its level; */
234 /* If yes, make the next node the head at that level */
235 /* Else, remove elt from the list at that level */
236 if (unpack_child(elt
->prev
) == elt
) {
237 pack_child(elt
->prev
, elt
->next
);
239 elt
->prev
->next
= elt
->next
;
241 /* Update prev for next element in list */
242 if (elt
->next
!= NULL
) {
243 elt
->next
->prev
= elt
->prev
;
248 sift_down(queue_t que
, entry_t elt
)
250 bool was_root
= remove(que
, elt
);
256 sift_up(queue_t que
, entry_t elt
)
258 if (elt
== que
->pq_root
) {
262 /* Remove the element from its current level list */
264 /* Re-insert the element into the heap with a merge */
265 return insert(que
, elt
);
268 static inline entry_t
269 remove_non_root(queue_t que
, entry_t elt
)
271 entry_t child
, new_root
;
273 /* To remove a non-root element with children levels, */
274 /* - Remove element from its current level list */
275 /* - Pairwise split all the elements in the child level list */
276 /* - Meld all these splits (right-to-left) to form new subtree */
277 /* - Merge the root subtree with the newly formed subtree */
280 child
= unpack_child(elt
);
282 child
= meld_pair(que
, child
);
283 new_root
= merge_pair(que
, que
->pq_root
, child
);
284 que
->pq_root
= new_root
;
298 destroy(queue_t que
, uintptr_t offset
, void (^callback
)(void *e
))
300 assert(callback
!= NULL
);
301 entry_t head
= que
->pq_root
;
304 while (head
!= NULL
) {
305 entry_t child_list
= unpack_child(head
);
307 tail
->next
= child_list
;
315 callback((void *)((char *)elt
- offset
));
318 /* poison the queue now that it's destroyed */
319 que
->pq_root
= (entry_t
)(~0ul);
323 insert(queue_t que
, entry_t elt
)
325 return (que
->pq_root
= merge_pair(que
, que
->pq_root
, elt
)) == elt
;
328 static inline entry_t
329 remove_root(queue_t que
, entry_t old_root
)
331 entry_t new_root
= unpack_child(old_root
);
332 que
->pq_root
= new_root
? meld_pair(que
, new_root
) : NULL
;
337 remove(queue_t que
, entry_t elt
)
339 if (elt
== que
->pq_root
) {
340 remove_root(que
, elt
);
341 elt
->next
= elt
->prev
= NULL
;
345 remove_non_root(que
, elt
);
346 elt
->next
= elt
->prev
= NULL
;
353 entry_increased(queue_t que
, entry_t elt
)
355 if (priority_queue_is_max_heap(que
)) {
356 return sift_up(que
, elt
);
358 return sift_down(que
, elt
);
363 entry_decreased(queue_t que
, entry_t elt
)
365 if (priority_queue_is_min_heap(que
)) {
366 return sift_up(que
, elt
);
368 return sift_down(que
, elt
);
373 #pragma mark instantiation
375 #define PRIORITY_QUEUE_MAKE_IMPL(pqueue_t, queue_t, entry_t) \
377 using pqueue_t = pqueue<queue_t, entry_t>; \
381 __pqueue_overloadable void \
382 _priority_queue_destroy(queue_t que, uintptr_t offset, void (^cb)(void *e)) \
384 pqueue_t::destroy(que, offset, cb); \
387 __pqueue_overloadable extern bool \
388 priority_queue_insert(queue_t que, entry_t elt) \
390 return pqueue_t::insert(que, elt); \
393 __pqueue_overloadable extern entry_t \
394 _priority_queue_remove_root(queue_t que) \
396 return pqueue_t::remove_root(que, que->pq_root); \
399 __pqueue_overloadable extern bool \
400 priority_queue_remove(queue_t que, entry_t elt) \
402 return pqueue_t::remove(que, elt); \
405 __pqueue_overloadable extern bool \
406 priority_queue_entry_decreased(queue_t que, entry_t elt) \
408 return pqueue_t::entry_decreased(que, elt); \
411 __pqueue_overloadable extern bool \
412 priority_queue_entry_increased(queue_t que, entry_t elt) \
414 return pqueue_t::entry_increased(que, elt); \
419 PRIORITY_QUEUE_MAKE_IMPL(pqueue_min_t
,
420 struct priority_queue_min
*, priority_queue_entry_t
);
421 PRIORITY_QUEUE_MAKE_IMPL(pqueue_max_t
,
422 struct priority_queue_max
*, priority_queue_entry_t
);
424 PRIORITY_QUEUE_MAKE_IMPL(pqueue_sched_min_t
,
425 struct priority_queue_sched_min
*, priority_queue_entry_sched_t
);
426 PRIORITY_QUEUE_MAKE_IMPL(pqueue_sched_max_t
,
427 struct priority_queue_sched_max
*, priority_queue_entry_sched_t
);
429 PRIORITY_QUEUE_MAKE_IMPL(pqueue_deadline_min_t
,
430 struct priority_queue_deadline_min
*, priority_queue_entry_deadline_t
);
431 PRIORITY_QUEUE_MAKE_IMPL(pqueue_deadline_max_t
,
432 struct priority_queue_deadline_max
*, priority_queue_entry_deadline_t
);
434 PRIORITY_QUEUE_MAKE_IMPL(pqueue_sched_stable_min_t
,
435 struct priority_queue_sched_stable_min
*, priority_queue_entry_stable_t
);
436 PRIORITY_QUEUE_MAKE_IMPL(pqueue_sched_stable_max_t
,
437 struct priority_queue_sched_stable_max
*, priority_queue_entry_stable_t
);