use super::gc::{Gc, GcOpt};
use super::mutator::Mutator;
use super::tagged::{Tag, Tagged};
use super::trace::{Trace, Tracer};
use core::sync::atomic::{AtomicU8, Ordering};
pub struct WriteBarrier<'gc, T: Trace + ?Sized> {
inner: &'gc T,
}
impl<'gc, T: Trace + ?Sized> WriteBarrier<'gc, T> {
pub(crate) unsafe fn new(gc: &'gc T) -> Self {
WriteBarrier { inner: gc }
}
pub fn inner(&self) -> &'gc T {
self.inner
}
#[doc(hidden)]
pub unsafe fn __from_field(inner: &'gc T, _: *const T) -> Self {
Self { inner }
}
}
impl<'gc, T: Trace + ?Sized> WriteBarrier<'gc, Gc<'gc, T>> {
pub fn set(&self, gc: impl Into<Gc<'gc, T>>) {
unsafe {
self.inner.set(gc.into());
}
}
}
impl<'gc, T: Trace + ?Sized> WriteBarrier<'gc, GcOpt<'gc, T>> {
pub fn set(&self, gc: impl Into<GcOpt<'gc, T>>) {
unsafe {
self.inner.set(gc.into());
}
}
}
impl<'gc, T: Trace> WriteBarrier<'gc, [T]> {
pub fn at(&self, idx: usize) -> WriteBarrier<'gc, T> {
WriteBarrier {
inner: &self.inner[idx],
}
}
}
impl<'gc, T: Trace> WriteBarrier<'gc, Option<T>> {
pub fn into(&self) -> Option<WriteBarrier<'_, T>> {
self.inner.as_ref().map(|inner| WriteBarrier { inner })
}
}
pub struct InnerBarrier<T: Trace> {
mark: AtomicU8,
inner: T,
}
impl<T: Trace> InnerBarrier<T> {
pub fn new(mu: &Mutator, inner: T) -> Self {
Self {
mark: AtomicU8::new(mu.get_prev_mark().into()),
inner,
}
}
pub fn inner(&self) -> &T {
&self.inner
}
pub fn get_mark(&self) -> u8 {
self.mark.load(Ordering::Acquire)
}
pub fn mark(&self, tracer: &mut Tracer) -> bool {
let mark = self.mark.load(Ordering::Acquire);
if mark == tracer.get_mark().into() {
return false;
}
self.mark.store(tracer.get_mark().into(), Ordering::SeqCst);
return true;
}
pub fn write_barrier<'gc, F>(&self, mu: &'gc Mutator, f: F)
where
F: FnOnce(&WriteBarrier<T>),
{
let barrier = unsafe { WriteBarrier::new(&self.inner) };
f(&barrier);
if self.mark.load(Ordering::Acquire) == mu.get_mark().into() {
mu.retrace(&self.inner);
}
}
}
unsafe impl<T: Trace> Trace for InnerBarrier<T> {
const IS_LEAF: bool = T::IS_LEAF;
fn trace(&self, tracer: &mut Tracer) {
if self.mark.load(Ordering::Acquire) != tracer.get_mark().into() {
self.mark(tracer);
self.inner.trace(tracer);
}
}
}
impl<'gc, B: Tag> WriteBarrier<'gc, Tagged<'gc, B>> {
pub fn set(&self, tagged_ptr: Tagged<'gc, B>) {
unsafe { self.inner.set(tagged_ptr.get_raw()) };
}
}
#[macro_export]
macro_rules! field {
($value:expr, $type:path, $field:ident) => {{
let _: &$crate::WriteBarrier<_> = $value;
match $value.inner() {
$type { ref $field, .. } => unsafe {
$crate::WriteBarrier::__from_field($field, $field as *const _)
},
_ => panic!("WriteBarrier field! macro failed to match on inner field"),
}
}};
}