1 #include <darwintest.h>
2 #include <darwintest_utils.h>
10 #define XNU_KERNEL_PRIVATE 1
12 #define OS_REFCNT_DEBUG 1
13 #define STRESS_TESTS 0
15 #define __container_of(ptr, type, field) __extension__({ \
16 const __typeof__(((type *)nullptr)->field) *__ptr = (ptr); \
17 (type *)((uintptr_t)__ptr - offsetof(type, field)); \
20 #pragma clang diagnostic ignored "-Watomic-implicit-seq-cst"
21 #pragma clang diagnostic ignored "-Wc++98-compat"
23 #include "../osfmk/kern/macro_help.h"
24 #include "../osfmk/kern/priority_queue.h"
25 #include "../libkern/c++/priority_queue.cpp"
27 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true));
30 compare_numbers_descending(const void * a
, const void * b
)
32 const uint16_t x
= *(const uint16_t *)a
;
33 const uint16_t y
= *(const uint16_t *)b
;
43 #define PRIORITY_QUEUE_NODES 8
45 typedef union test_node
{
47 struct priority_queue_entry e
;
50 struct priority_queue_entry_sched ke
;
51 struct priority_queue_entry_stable se
;
55 dump_pqueue_entry(priority_queue_entry_sched_t e
, int depth
)
57 priority_queue_entry_sched_t t
;
59 printf("%*s [%02d] %p\n", depth
* 4, "", e
->key
, (void *)e
);
60 t
= pqueue_sched_max_t::unpack_child(e
);
62 dump_pqueue_entry(t
, depth
+ 1);
66 dump_pqueue_entry(e
, depth
);
72 dump_pqueue(struct priority_queue_sched_max
*pq
)
74 dump_pqueue_entry(pq
->pq_root
, 0);
78 T_DECL(priority_queue_sched_max
, "Basic sched priority queue testing")
80 /* Configuration for the test */
81 static uint16_t priority_list
[] = { 20, 3, 7, 6, 50, 2, 8, 12};
83 struct priority_queue_sched_max pq
;
84 uint16_t increase_pri
= 100;
85 uint16_t decrease_pri
= 90;
87 boolean_t update_result
= false;
88 test_node_t node
= NULL
;
90 priority_queue_init(&pq
);
92 /* Add all priorities to the first priority queue */
93 for (int i
= 0; i
< PRIORITY_QUEUE_NODES
; i
++) {
95 T_QUIET
; T_ASSERT_NOTNULL(node
, NULL
);
97 priority_queue_entry_init(&node
->ke
);
98 priority_queue_entry_set_sched_pri(&pq
, &node
->ke
, priority_list
[i
], 0);
99 priority_queue_insert(&pq
, &node
->ke
);
102 /* Test the priority increase operation by updating the last node added (7) */
103 priority_queue_entry_set_sched_pri(&pq
, &node
->ke
, increase_pri
, 0);
104 update_result
= priority_queue_entry_increased(&pq
, &node
->ke
);
105 T_ASSERT_TRUE(update_result
, "increase key updated root");
106 key
= priority_queue_max_sched_pri(&pq
);
107 T_ASSERT_EQ(key
, increase_pri
, "verify priority_queue_entry_increased() operation");
109 /* Test the priority decrease operation by updating the last node added */
110 priority_queue_entry_set_sched_pri(&pq
, &node
->ke
, decrease_pri
, 0);
111 update_result
= priority_queue_entry_decreased(&pq
, &node
->ke
);
112 T_ASSERT_TRUE(update_result
, "decrease key updated root");
113 key
= priority_queue_max_sched_pri(&pq
);
114 T_ASSERT_EQ(key
, decrease_pri
, "verify priority_queue_entry_decreased() operation");
116 /* Update our local priority list as well */
117 priority_list
[PRIORITY_QUEUE_NODES
- 1] = decrease_pri
;
119 /* Sort the local list in descending order */
120 qsort(priority_list
, PRIORITY_QUEUE_NODES
, sizeof(priority_list
[0]), compare_numbers_descending
);
122 priority_queue_entry_sched_t k
= NULL
;
124 node
= pqe_element_fast(k
, test_node
, ke
);
126 /* Test the maximum operation by comparing max node with local list */
127 for (int i
= 0; i
< PRIORITY_QUEUE_NODES
; i
++) {
128 key
= priority_queue_max_sched_pri(&pq
);
129 T_ASSERT_EQ(key
, priority_list
[i
], "[%d] priority queue max node removal", i
);
130 node
= priority_queue_remove_max(&pq
, test_node
, ke
);
134 T_ASSERT_TRUE(priority_queue_empty(&pq
), "queue is empty");
135 priority_queue_destroy(&pq
, union test_node
, ke
, ^(test_node_t n
) {
136 T_FAIL("Called with %p", n
);
140 T_DECL(priority_queue_max
, "Basic generic priority queue testing")
142 /* Configuration for the test */
143 static uint16_t priority_list
[] = { 20, 3, 7, 6, 50, 2, 8, 12};
145 struct priority_queue_max pq
;
146 uint16_t increase_pri
= 100;
147 uint16_t decrease_pri
= 90;
149 boolean_t update_result
= false;
150 test_node_t node
= NULL
;
152 priority_queue_compare_fn_t cmp_fn
=
153 priority_heap_make_comparator(a
, b
, union test_node
, e
, {
154 if (a
->node_key
!= b
->node_key
) {
155 return priority_heap_compare_ints(a
->node_key
, b
->node_key
);
160 priority_queue_init(&pq
, cmp_fn
);
162 /* Add all priorities to the first priority queue */
163 for (int i
= 0; i
< PRIORITY_QUEUE_NODES
; i
++) {
164 node
= new test_node
;
165 T_QUIET
; T_ASSERT_NOTNULL(node
, NULL
);
167 priority_queue_entry_init(&node
->e
);
168 node
->node_key
= priority_list
[i
];
169 priority_queue_insert(&pq
, &node
->e
);
172 /* Test the priority increase operation by updating the last node added (8) */
173 node
->node_key
= increase_pri
;
174 update_result
= priority_queue_entry_increased(&pq
, &node
->e
);
175 T_ASSERT_TRUE(update_result
, "increase key updated root");
176 result
= priority_queue_max(&pq
, union test_node
, e
);
177 T_ASSERT_EQ(result
->node_key
, increase_pri
, "verify priority_queue_entry_increased() operation");
180 /* Test the priority decrease operation by updating the last node added */
181 node
->node_key
= decrease_pri
;
182 update_result
= priority_queue_entry_decreased(&pq
, &node
->e
);
183 T_ASSERT_TRUE(update_result
, "decrease key updated root");
184 result
= priority_queue_max(&pq
, union test_node
, e
);
185 T_ASSERT_EQ(result
->node_key
, decrease_pri
, "verify priority_queue_entry_decreased() operation");
187 /* Update our local priority list as well */
188 priority_list
[PRIORITY_QUEUE_NODES
- 1] = decrease_pri
;
190 /* Sort the local list in descending order */
191 qsort(priority_list
, PRIORITY_QUEUE_NODES
, sizeof(priority_list
[0]), compare_numbers_descending
);
193 /* Test the maximum operation by comparing max node with local list */
194 for (int i
= 0; i
< PRIORITY_QUEUE_NODES
; i
++) {
195 result
= priority_queue_remove_max(&pq
, union test_node
, e
);
196 T_ASSERT_EQ(result
->node_key
, priority_list
[i
],
197 "[%d] priority queue max node removal", i
);
201 T_ASSERT_TRUE(priority_queue_empty(&pq
), "queue is empty");
202 priority_queue_destroy(&pq
, union test_node
, e
, ^(test_node_t n
) {
203 T_FAIL("Called with %p", n
);
207 T_DECL(priority_queue_sched_stable_max
, "Basic stable sched priority queue testing")
209 /* Configuration for the test */
210 static struct config
{
212 priority_queue_entry_sched_modifier_t modifier
;
215 { 20, PRIORITY_QUEUE_ENTRY_NONE
, 8 },
216 { 3, PRIORITY_QUEUE_ENTRY_NONE
, 7 },
217 { 3, PRIORITY_QUEUE_ENTRY_PREEMPTED
, 6 },
218 { 6, PRIORITY_QUEUE_ENTRY_NONE
, 5 },
219 { 50, PRIORITY_QUEUE_ENTRY_PREEMPTED
, 4 },
220 { 50, PRIORITY_QUEUE_ENTRY_PREEMPTED
, 3 },
221 { 50, PRIORITY_QUEUE_ENTRY_NONE
, 2 },
222 { 50, PRIORITY_QUEUE_ENTRY_NONE
, 1 },
225 struct priority_queue_sched_stable_max pq
;
226 test_node_t node
= NULL
;
228 priority_queue_init(&pq
);
230 /* Add all priorities to the first priority queue */
231 for (int i
= 0; i
< PRIORITY_QUEUE_NODES
; i
++) {
232 node
= new test_node
;
233 T_QUIET
; T_ASSERT_NOTNULL(node
, NULL
);
235 priority_queue_entry_init(node
);
236 node
->se
.stamp
= config
[i
].stamp
;
237 priority_queue_entry_set_sched_pri(&pq
, &node
->se
,
238 config
[i
].pri
, config
[i
].modifier
);
239 priority_queue_insert(&pq
, &node
->se
);
242 /* Sort the local list in descending order */
243 qsort_b(config
, PRIORITY_QUEUE_NODES
, sizeof(struct config
), ^(const void *a
, const void *b
){
244 const struct config
&c1
= *(const struct config
*)a
;
245 const struct config
&c2
= *(const struct config
*)b
;
246 if (c1
.pri
!= c2
.pri
) {
247 return c1
.pri
< c2
.pri
? 1 : -1;
249 if (c1
.modifier
!= c2
.modifier
) {
250 return c1
.modifier
< c2
.modifier
? 1 : -1;
252 if (c1
.stamp
!= c2
.stamp
) {
254 /* younger is better */
255 return c1
.stamp
< c1
.stamp
? 1 : -1;
257 /* older is better */
258 return c1
.stamp
> c2
.stamp
? 1 : -1;
264 /* Test the maximum operation by comparing max node with local list */
265 for (int i
= 0; i
< PRIORITY_QUEUE_NODES
; i
++) {
266 node
= priority_queue_max(&pq
, union test_node
, se
);
267 T_LOG("[%d]: { pri: %2d, modifier: %d, stamp: %lld }\n",
268 i
, config
[i
].pri
, config
[i
].modifier
, config
[i
].stamp
);
269 auto pri
= priority_queue_entry_sched_pri(&pq
, &node
->se
);
270 T_ASSERT_EQ(pri
, config
[i
].pri
,
271 "[%d] priority queue max node removal", i
);
272 auto modifier
= priority_queue_entry_sched_modifier(&pq
, &node
->se
);
273 T_ASSERT_EQ(modifier
, config
[i
].modifier
,
274 "[%d] priority queue max node removal", i
);
275 T_ASSERT_EQ(node
->se
.stamp
, config
[i
].stamp
,
276 "[%d] priority queue max node removal", i
);
277 priority_queue_remove_max(&pq
, union test_node
, se
);
281 T_ASSERT_TRUE(priority_queue_empty(&pq
), "queue is empty");
282 priority_queue_destroy(&pq
, union test_node
, se
, ^(test_node_t n
) {
283 T_FAIL("Called with %p", n
);