<commit_before>use fixedbitset::FixedBitSet;
use parking_lot::Mutex;
use std::cmp;

use gc::swiper::card::CardTable;
use gc::swiper::controller::SharedHeapConfig;
use gc::swiper::crossing::CrossingMap;
use gc::{arena, Address, Region};

// Choose 128K as chunk size for now
const CHUNK_SIZE_BITS: usize = 17;
const CHUNK_SIZE: usize = 1 << CHUNK_SIZE_BITS;

// experimental implementation of an Old generation consisting of chunks
// goal is to reduce work for full GCs
struct SplitOldGen {
    // total size of old generation
    total: Region,

    chunks: Vec<Chunk>,

    prot: Mutex<SplitOldGenProtected>,

    crossing_map: CrossingMap,
    card_table: CardTable,
    config: SharedHeapConfig,
}

impl SplitOldGen {
    fn new(
        start: Address,
        end: Address,
        crossing_map: CrossingMap,
        card_table: CardTable,
        config: SharedHeapConfig,
    ) -> SplitOldGen {
        assert!(start.is_chunk_aligned());

        let total = Region::new(start, end);
        let num_chunks = total.size() / CHUNK_SIZE;

        let mut chunks = Vec::with_capacity(num_chunks);

        for i in 0..num_chunks {
            let chunk_start = start.offset(i * CHUNK_SIZE);
            chunks.push(Chunk::new(chunk_start));
        }

        let prot = Mutex::new(SplitOldGenProtected {
            used_chunks: ChunkSet::empty(num_chunks),
            free_chunks: ChunkSet::full(num_chunks),
        });

        SplitOldGen {
            total: total,
            chunks: chunks,
            prot: prot,
            crossing_map: crossing_map,
            card_table: card_table,
            config: config,
        }
    }

    fn add_chunk(&self) -> bool {
        {
            let mut config = self.config.lock();
            if !config.grow_old(CHUNK_SIZE) {
                return false;
            }
        }

        let mut prot = self.prot.lock();

        if let Some(chunk) = prot.free_chunks.remove_leftmost() {
            prot.used_chunks.add(chunk);
            arena::commit(chunk_addr(chunk, self.total.start), CHUNK_SIZE, false);
            return true;
        }

        false
    }

    fn free_chunk(&self, chunk: ChunkId) {
        let mut prot = self.prot.lock();

        assert!(prot.used_chunks.remove(chunk));
        prot.free_chunks.add(chunk);

        arena::discard(chunk_addr(chunk, self.total.start), CHUNK_SIZE);

        {
            let mut config = self.config.lock();
            config.shrink_old(CHUNK_SIZE);
        }
    }
}

fn chunk_addr(chunk: ChunkId, start: Address) -> Address {
    start.offset(chunk.to_usize() * CHUNK_SIZE)
}

struct SplitOldGenProtected {
    // all used chunks
    used_chunks: ChunkSet,

    // completely free chunks (no live objects)
    free_chunks: ChunkSet,
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
struct ChunkId(usize);
<commit_msg>split_old: reduce lock duration<commit_after>use fixedbitset::FixedBitSet;
use parking_lot::Mutex;
use std::cmp;

use gc::swiper::card::CardTable;
use gc::swiper::controller::SharedHeapConfig;
use gc::swiper::crossing::CrossingMap;
use gc::{arena, Address, Region};

// Choose 128K as chunk size for now
const CHUNK_SIZE_BITS: usize = 17;
const CHUNK_SIZE: usize = 1 << CHUNK_SIZE_BITS;

// experimental implementation of an Old generation consisting of chunks
// goal is to reduce work for full GCs
struct SplitOldGen {
    // total size of old generation
    total: Region,

    chunks: Vec<Chunk>,

    prot: Mutex<SplitOldGenProtected>,

    crossing_map: CrossingMap,
    card_table: CardTable,
    config: SharedHeapConfig,
}

impl SplitOldGen {
    fn new(
        start: Address,
        end: Address,
        crossing_map: CrossingMap,
        card_table: CardTable,
        config: SharedHeapConfig,
    ) -> SplitOldGen {
        assert!(start.is_chunk_aligned());

        let total = Region::new(start, end);
        let num_chunks = total.size() / CHUNK_SIZE;

        let mut chunks = Vec::with_capacity(num_chunks);

        for i in 0..num_chunks {
            let chunk_start = start.offset(i * CHUNK_SIZE);
            chunks.push(Chunk::new(chunk_start));
        }

        let prot = Mutex::new(SplitOldGenProtected {
            used_chunks: ChunkSet::empty(num_chunks),
            free_chunks: ChunkSet::full(num_chunks),
        });

        SplitOldGen {
            total: total,
            chunks: chunks,
            prot: prot,
            crossing_map: crossing_map,
            card_table: card_table,
            config: config,
        }
    }

    fn add_chunk(&self) -> bool {
        {
            let mut config = self.config.lock();
            if !config.grow_old(CHUNK_SIZE) {
                return false;
            }
        }

        {
            let mut prot = self.prot.lock();

            if let Some(chunk) = prot.free_chunks.remove_leftmost() {
                prot.used_chunks.add(chunk);
                arena::commit(chunk_addr(chunk, self.total.start), CHUNK_SIZE, false);
                return true;
            }
        }

        false
    }

    fn free_chunk(&self, chunk: ChunkId) {
        {
            let mut prot = self.prot.lock();
            assert!(prot.used_chunks.remove(chunk));
            prot.free_chunks.add(chunk);
            arena::discard(chunk_addr(chunk, self.total.start), CHUNK_SIZE);
        }

        {
            let mut config = self.config.lock();
            config.shrink_old(CHUNK_SIZE);
        }
    }
}

fn chunk_addr(chunk: ChunkId, start: Address) -> Address {
    start.offset(chunk.to_usize() * CHUNK_SIZE)
}

struct SplitOldGenProtected {
    // all used chunks
    used_chunks: ChunkSet,

    // completely free chunks (no live objects)
    free_chunks: ChunkSet,
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
struct ChunkId(usize);

{'fraction_non_alphanumeric': 0.10921325051759834, 'fraction_numerical': 0.0024154589371980675, 'mean_word_length': 2.267756482525366, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3920204', 'n_tokens_mistral': 1832, 'n_tokens_neox': 1810, 'n_words': 403}