Only called from `mi_heap_absorb`.
| 295 | |
| 296 | // Only called from `mi_heap_absorb`. |
| 297 | size_t _mi_page_queue_append(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_queue_t* append) { |
| 298 | mi_assert_internal(mi_heap_contains_queue(heap,pq)); |
| 299 | mi_assert_internal(pq->block_size == append->block_size); |
| 300 | |
| 301 | if (append->first==NULL) return 0; |
| 302 | |
| 303 | // set append pages to new heap and count |
| 304 | size_t count = 0; |
| 305 | for (mi_page_t* page = append->first; page != NULL; page = page->next) { |
| 306 | // inline `mi_page_set_heap` to avoid wrong assertion during absorption; |
| 307 | // in this case it is ok to be delayed freeing since both "to" and "from" heap are still alive. |
| 308 | mi_atomic_store_release(&page->xheap, (uintptr_t)heap); |
| 309 | // set the flag to delayed free (not overriding NEVER_DELAYED_FREE) which has as a |
| 310 | // side effect that it spins until any DELAYED_FREEING is finished. This ensures |
| 311 | // that after appending only the new heap will be used for delayed free operations. |
| 312 | _mi_page_use_delayed_free(page, MI_USE_DELAYED_FREE, false); |
| 313 | count++; |
| 314 | } |
| 315 | |
| 316 | if (pq->last==NULL) { |
| 317 | // take over afresh |
| 318 | mi_assert_internal(pq->first==NULL); |
| 319 | pq->first = append->first; |
| 320 | pq->last = append->last; |
| 321 | mi_heap_queue_first_update(heap, pq); |
| 322 | } |
| 323 | else { |
| 324 | // append to end |
| 325 | mi_assert_internal(pq->last!=NULL); |
| 326 | mi_assert_internal(append->first!=NULL); |
| 327 | pq->last->next = append->first; |
| 328 | append->first->prev = pq->last; |
| 329 | pq->last = append->last; |
| 330 | } |
| 331 | return count; |
| 332 | } |
no test coverage detected