mnemosyne-local 0.6.0

Thread-local allocation engine for Mnemosyne
Documentation
//! Orphan-pool teardown used to give miri a quiescent heap at the end
//! of a test run.

/// Drain `B`'s orphan pool, reclaiming what is dead and returning what is
/// still live, then purge the segment pool.
///
/// # Safety
///
/// The caller must hold the serialized test lock (`TEST_LOCK`) for the whole
/// call, so that no allocator operation on any thread can reach `B`'s pools
/// or the segments detached from them while they are detached. Every segment
/// in `B`'s orphan pool must belong to backend `B`: the reclamation and the
/// deallocation below both run through `B`, and a segment mapped by another
/// backend would be released through the wrong one.
#[doc(hidden)]
pub unsafe fn miri_cleanup_pools<B: mnemosyne_arena::HasSegmentPool>() {
    let mut orphaned = std::vec::Vec::new();
    while let Some(segment) = B::global_orphan_pool().pop() {
        orphaned.push(segment);
    }

    for segment in orphaned {
        // SAFETY: `segment` was just popped from the orphan pool.
        let encrypted = unsafe { (*segment).free_list_encrypted };
        // SAFETY: `OccupiedPageBits` skips bit 0; each `page_index` is a valid occupied page.
        for page_index in
            mnemosyne_core::types::OccupiedPageBits::new(unsafe { (*segment).page_occupied_mask })
        {
            // SAFETY: the caller holds the serialized test lock.
            unsafe {
                let randomized = (*segment).pages[page_index].secondary_free.is_some();
                mnemosyne_core::types::Page::reclaim_thread_free_if_present_in_segment_with_randomized(
                    segment,
                    page_index,
                    encrypted,
                    randomized,
                );
            }
        }

        if unsafe {
            (*segment)
                .pages
                .iter()
                .skip(1)
                .any(|page| page.alloc_count != 0)
        } {
            // SAFETY: `segment` remains live because its allocation count is
            // non-zero, so the detached ownership returns to the orphan pool.
            unsafe { B::global_orphan_pool().push_unbounded(segment) };
        } else {
            // SAFETY: the segment has no live allocations and ownership was
            // transferred from the detached orphan chain to this cleanup.
            unsafe { mnemosyne_arena::deallocate_segment::<B>(segment) };
        }
    }

    // SAFETY: the caller holds the serialized test lock, so no allocator
    // operation can race the detached pools. Live orphan mappings were
    // preserved above so Miri still reports them.
    unsafe { mnemosyne_arena::purge_segment_pool::<B>() };
}