use crate::heap::Allocator;
use super::gc::Gc;
use super::header::{GcHeader, GcMark, SizedHeader, SliceHeader, StrHeader};
use super::pointee::Thin;
use super::pointee::{sized_alloc_layout, slice_alloc_layout, str_alloc_layout};
use super::trace::{Collector, Trace, TraceJob};
use alloc::vec;
use alloc::vec::Vec;
use core::cell::RefCell;
use core::ptr::{copy, write, NonNull};
pub struct Mutator<'gc> {
collector: &'gc Collector,
allocator: Allocator,
rescan: RefCell<Vec<TraceJob>>,
mark: GcMark,
}
impl<'gc> Drop for Mutator<'gc> {
fn drop(&mut self) {
let work = self.rescan.take();
self.collector.send_work(work);
self.collector.decrement_mutators();
}
}
impl<'gc> Mutator<'gc> {
pub(crate) fn new(collector: &'gc Collector) -> Self {
let mark = collector.prev_mark();
let allocator = collector.new_allocator();
collector.increment_mutators();
Self {
allocator,
collector,
rescan: RefCell::new(vec![]),
mark,
}
}
pub fn alloc<T: Trace>(&self, value: T) -> Gc<'gc, T> {
let (alloc_layout, val_offset) = sized_alloc_layout::<T>();
unsafe {
let ptr = self.allocator.alloc(alloc_layout) as *mut u8;
let val_ptr = ptr.add(val_offset).cast();
let header_ptr = ptr.cast();
write(val_ptr, value);
write(header_ptr, SizedHeader::<T>::new(self.mark));
Gc::from_ptr(val_ptr)
}
}
pub fn alloc_array<T: Trace + Copy>(&'gc self, value: T, len: usize) -> Gc<'gc, [T]> {
let (alloc_layout, slice_offset) = slice_alloc_layout::<T>(len);
unsafe {
let ptr = self.allocator.alloc(alloc_layout) as *mut u8;
let header_ptr = ptr.cast();
let slice_ptr: *mut T = ptr.add(slice_offset).cast();
for i in 0..len {
write(slice_ptr.add(i), value);
}
let slice: *const [T] = core::ptr::slice_from_raw_parts(slice_ptr, len);
write(header_ptr, SliceHeader::<T>::new(self.mark, slice.len()));
Gc::from_ptr(slice)
}
}
pub fn alloc_array_from_slice<T: Trace + Copy>(&'gc self, slice: &[T]) -> Gc<'gc, [T]> {
let (alloc_layout, slice_offset) = slice_alloc_layout::<T>(slice.len());
unsafe {
let ptr = self.allocator.alloc(alloc_layout) as *mut u8;
let header_ptr = ptr.cast();
let slice_ptr: *mut T = ptr.add(slice_offset).cast();
copy(slice.as_ptr(), slice_ptr, slice.len());
let slice: *const [T] = core::ptr::slice_from_raw_parts(slice_ptr, slice.len());
write(header_ptr, SliceHeader::<T>::new(self.mark, slice.len()));
Gc::from_ptr(slice)
}
}
pub fn alloc_array_from_fn<T, F>(&'gc self, len: usize, mut cb: F) -> Gc<'gc, [T]>
where
T: Trace,
F: FnMut(usize) -> T,
{
let (alloc_layout, slice_offset) = slice_alloc_layout::<T>(len);
unsafe {
let ptr = self.allocator.alloc(alloc_layout) as *mut u8;
let header_ptr = ptr.cast();
let slice_ptr: *mut T = ptr.add(slice_offset).cast();
for i in 0..len {
let item = cb(i);
write(slice_ptr.add(i), item);
}
let slice: *const [T] = core::ptr::slice_from_raw_parts(slice_ptr, len);
write(header_ptr, SliceHeader::<T>::new(self.mark, len));
Gc::from_ptr(slice)
}
}
pub fn alloc_str(&'gc self, s: &str) -> Gc<'gc, str> {
let (alloc_layout, str_offset) = str_alloc_layout(s.len());
unsafe {
let ptr = self.allocator.alloc(alloc_layout) as *mut u8;
let header_ptr = ptr.cast();
let str_ptr: *mut u8 = ptr.add(str_offset);
copy(s.as_ptr(), str_ptr, s.len());
let str_slice: *const str = core::ptr::slice_from_raw_parts(str_ptr, s.len()) as *const str;
write(header_ptr, StrHeader::new(self.mark, s.len()));
Gc::from_ptr(str_slice)
}
}
pub fn gc_yield(&self) -> bool {
if self.collector.yield_flag() {
return true;
}
self.collector.minor_trigger() || self.collector.major_trigger()
}
pub(crate) fn has_marked<T: Trace + ?Sized>(&self, gc_ptr: &Gc<'gc, T>) -> bool {
gc_ptr.get_header().get_mark() == self.collector.get_current_mark()
}
pub(crate) fn get_mark(&self) -> GcMark {
self.collector.get_current_mark()
}
pub(crate) fn get_prev_mark(&self) -> GcMark {
self.collector.get_current_mark().rotate().rotate()
}
pub fn retrace<T: Trace + ?Sized>(&self, obj: &'gc T) {
let ptr: NonNull<Thin<T>> = NonNull::from(obj).cast(); let trace_job = TraceJob::new::<T>(ptr);
self.rescan.borrow_mut().push(trace_job);
if self.rescan.borrow().len() >= self.collector.config().mutator_share_min {
let work = self.rescan.take();
self.collector.send_work(work);
}
}
}