use crate::generation::colour::{Colour, GCInitialColour};
use std::cell::RefCell;
use std::fmt::Debug;
#[cfg(feature = "weak_pointer")]
use crate::errors::{Error, ErrorEnum};
#[cfg(feature = "weak_pointer")]
use std::backtrace::Backtrace;
#[cfg(feature = "weak_pointer")]
use std::sync::Arc;
#[cfg(feature = "multi_thread")]
use std::sync::atomic::{AtomicUsize, Ordering};
const COUNT_SHIFT: u32 = 2;
const COLOUR_MASK: usize = 0x3_usize;
const EXPIRED_COLOUR: usize = 0x0_usize;
const WHITE_COLOUR: usize = 0x1_usize;
const GREY_COLOUR: usize = 0x2_usize;
const BLACK_COLOUR: usize = 0x3_usize;
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum RefCountResult {
NeedGC,
NoNeed,
}
#[derive(PartialEq, Eq, Debug)]
pub(crate) enum ObjectState {
Live,
Expired,
}
pub(crate) struct RefCount {
v: RefCell<usize>,
}
#[cfg(feature = "multi_thread")]
pub(crate) struct MTRefCount {
v: AtomicUsize,
}
impl Default for RefCount {
fn default() -> Self {
RefCount {
v: RefCell::new((1_usize << COUNT_SHIFT) | BLACK_COLOUR),
}
}
}
#[cfg(feature = "multi_thread")]
impl Default for MTRefCount {
fn default() -> Self {
MTRefCount {
v: AtomicUsize::new((1_usize << COUNT_SHIFT) | BLACK_COLOUR),
}
}
}
impl Debug for RefCount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
let (refs, colour, state) = self.load();
f.debug_struct("RefCount")
.field("references", &refs)
.field("colour", &colour)
.field("state", &state)
.finish()
}
}
#[cfg(feature = "multi_thread")]
impl Debug for MTRefCount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
let (refs, colour, state) = self.load();
f.debug_struct("MTRefCount")
.field("references", &refs)
.field("colour", &colour)
.field("state", &state)
.finish()
}
}
impl RefCount {
#[inline]
pub(super) const fn new(initial_refcount: usize) -> Self {
RefCount {
v: RefCell::new((initial_refcount << COUNT_SHIFT) | BLACK_COLOUR),
}
}
#[inline]
pub(crate) fn load(&self) -> (usize, Colour, ObjectState) {
let v = *self.v.borrow();
let (colour, state) = match v & COLOUR_MASK {
EXPIRED_COLOUR => (Colour::White, ObjectState::Expired),
WHITE_COLOUR => (Colour::White, ObjectState::Live),
GREY_COLOUR => (Colour::Grey, ObjectState::Live),
BLACK_COLOUR => (Colour::Black, ObjectState::Live),
_ => panic!("Colour constants are wrong."),
};
let refcount = v >> COUNT_SHIFT;
(refcount, colour, state)
}
pub(super) fn gc_colour_reset(&self) -> GCInitialColour {
let mut colour = GCInitialColour::White;
self.v.replace_with(|v| {
let mut v = *v;
assert_ne!((v & COLOUR_MASK), EXPIRED_COLOUR);
v &= !COLOUR_MASK;
if (v & !COLOUR_MASK) == 0 {
v |= WHITE_COLOUR;
colour = GCInitialColour::White;
} else {
v |= GREY_COLOUR;
colour = GCInitialColour::Grey;
}
v
});
colour
}
pub(super) fn invalidate(&self) {
self.v.replace_with(|v| {
let mut v = *v;
assert_ne!(v & COLOUR_MASK, EXPIRED_COLOUR);
v &= !COLOUR_MASK;
v |= EXPIRED_COLOUR;
v
});
}
pub(super) fn try_invalidate(&self) -> Result<(), ()> {
let mut v = self.v.borrow_mut();
match *v & COLOUR_MASK {
EXPIRED_COLOUR => Err(()),
_ => {
*v &= !COLOUR_MASK;
*v |= EXPIRED_COLOUR;
Ok(())
}
}
}
#[cfg(feature = "weak_pointer")]
pub(super) fn try_inc(
&self,
has_data: impl FnOnce() -> bool,
opt_backtrace: &Option<Arc<Backtrace>>,
) -> Result<(), Error> {
let mut v: usize = *self.v.borrow();
if (v & COLOUR_MASK) == EXPIRED_COLOUR {
return Err(Error::new(ErrorEnum::Expired(opt_backtrace.clone())));
}
if !has_data() {
return Err(Error::new(ErrorEnum::NotYetInitialized(
opt_backtrace.clone(),
)));
}
v = v.strict_add(1 << COUNT_SHIFT);
if (v & COLOUR_MASK) == WHITE_COLOUR {
v &= !COLOUR_MASK;
v |= GREY_COLOUR;
}
*self.v.borrow_mut() = v;
Ok(())
}
pub(super) fn inc(&self) {
self.v.replace_with(|v| {
let mut v = (*v).strict_add(1 << COUNT_SHIFT);
if (v & COLOUR_MASK) == WHITE_COLOUR {
v &= !COLOUR_MASK;
v |= GREY_COLOUR;
}
v
});
}
pub(super) fn dec(&self) -> RefCountResult {
let mut result = RefCountResult::NoNeed;
self.v.replace_with(|v| {
let v = (*v).strict_sub(1 << COUNT_SHIFT);
if (v & !COLOUR_MASK) == 0 {
result = RefCountResult::NeedGC;
}
v
});
result
}
pub(super) fn dec_reachable(&self) {
self.v.replace_with(|v| {
assert_ne!(*v & COLOUR_MASK, WHITE_COLOUR);
assert_ne!(*v & COLOUR_MASK, EXPIRED_COLOUR);
(*v).strict_sub(1 << COUNT_SHIFT)
});
}
pub(super) fn mark_black(&self) {
self.v.replace_with(|v| {
let mut v = *v;
assert_eq!(v & COLOUR_MASK, GREY_COLOUR);
v &= !COLOUR_MASK;
v |= BLACK_COLOUR;
v
});
}
pub(super) fn try_mark_grey(&self) -> Result<(), ()> {
let mut v = *self.v.borrow();
assert_ne!(v & COLOUR_MASK, EXPIRED_COLOUR);
match v & COLOUR_MASK {
WHITE_COLOUR => {
v &= !COLOUR_MASK;
v |= GREY_COLOUR;
*self.v.borrow_mut() = v;
Ok(())
}
_ => Err(()),
}
}
}
#[cfg(feature = "multi_thread")]
impl MTRefCount {
#[inline]
pub(super) const fn new(initial_refcount: usize) -> Self {
MTRefCount {
v: AtomicUsize::new((initial_refcount << COUNT_SHIFT) | BLACK_COLOUR),
}
}
#[inline]
pub(crate) fn load(&self) -> (usize, Colour, ObjectState) {
let v = self.v.load(Ordering::Relaxed);
let (colour, state) = match v & COLOUR_MASK {
EXPIRED_COLOUR => (Colour::White, ObjectState::Expired),
WHITE_COLOUR => (Colour::White, ObjectState::Live),
GREY_COLOUR => (Colour::Grey, ObjectState::Live),
BLACK_COLOUR => (Colour::Black, ObjectState::Live),
_ => panic!("Colour constants are wrong."),
};
let refcount = v >> COUNT_SHIFT;
(refcount, colour, state)
}
pub(super) fn gc_colour_reset(&self) -> GCInitialColour {
let mut old_v: usize = BLACK_COLOUR;
loop {
assert_ne!((old_v & COLOUR_MASK), EXPIRED_COLOUR);
let mut new_v = old_v & !COLOUR_MASK;
let colour = if (old_v & !COLOUR_MASK) == 0 {
new_v |= WHITE_COLOUR;
GCInitialColour::White
} else {
new_v |= GREY_COLOUR;
GCInitialColour::Grey
};
match self
.v
.compare_exchange_weak(old_v, new_v, Ordering::Relaxed, Ordering::Relaxed)
{
Ok(_) => break colour,
Err(x) => old_v = x,
};
}
}
pub(super) fn invalidate(&self) {
let mut old_v = WHITE_COLOUR;
loop {
assert_ne!((old_v & COLOUR_MASK), EXPIRED_COLOUR);
let new_v = (old_v & !COLOUR_MASK) | EXPIRED_COLOUR;
match self
.v
.compare_exchange_weak(old_v, new_v, Ordering::Relaxed, Ordering::Relaxed)
{
Ok(_) => break,
Err(x) => old_v = x,
}
}
}
pub(super) fn try_invalidate(&self) -> Result<(), ()> {
let mut old_v = self.v.load(Ordering::Relaxed);
loop {
if (old_v & COLOUR_MASK) == EXPIRED_COLOUR {
return Err(());
}
let mut new_v = old_v;
new_v &= !COLOUR_MASK;
new_v |= EXPIRED_COLOUR;
match self
.v
.compare_exchange(old_v, new_v, Ordering::Relaxed, Ordering::Relaxed)
{
Ok(_) => break,
Err(x) => old_v = x,
};
}
Ok(())
}
#[cfg(feature = "weak_pointer")]
pub(super) fn try_inc<R>(
&self,
has_data: impl FnOnce() -> bool,
lock_fn: impl Fn() -> R,
opt_backtrace: &Option<Arc<Backtrace>>,
) -> Result<Option<R>, Error> {
let mut old_v = self.v.load(Ordering::Relaxed);
let mut has_data = Some(has_data);
let weak_lockout = loop {
if (old_v & COLOUR_MASK) == EXPIRED_COLOUR {
return Err(Error::new(ErrorEnum::Expired(opt_backtrace.clone())));
}
if has_data.take().map(|has_data| !has_data()).unwrap_or(false) {
return Err(Error::new(ErrorEnum::NotYetInitialized(
opt_backtrace.clone(),
)));
}
let mut weak_lockout = None;
let mut new_v = old_v.strict_add(1 << COUNT_SHIFT);
if (old_v & COLOUR_MASK) == WHITE_COLOUR {
new_v &= !COLOUR_MASK;
new_v |= GREY_COLOUR;
weak_lockout = Some(lock_fn());
}
match self
.v
.compare_exchange(old_v, new_v, Ordering::Relaxed, Ordering::Relaxed)
{
Ok(_) => break weak_lockout,
Err(x) => old_v = x,
};
};
Ok(weak_lockout)
}
pub(super) fn inc<R>(&self, lock_fn: impl Fn() -> R) -> Option<R> {
let mut old_v = self.v.load(Ordering::Relaxed);
loop {
assert_ne!(old_v & COLOUR_MASK, EXPIRED_COLOUR);
let mut new_v = old_v.strict_add(1 << COUNT_SHIFT);
let mut r = None;
if (old_v & COLOUR_MASK) == WHITE_COLOUR {
new_v &= !COLOUR_MASK;
new_v |= GREY_COLOUR;
r = Some(lock_fn());
}
match self
.v
.compare_exchange_weak(old_v, new_v, Ordering::Relaxed, Ordering::Relaxed)
{
Ok(_) => break r,
Err(x) => old_v = x,
}
}
}
#[inline]
pub(super) fn inc_strong(&self) {
let old_v = self.v.fetch_add(1 << COUNT_SHIFT, Ordering::Relaxed);
assert_ne!(old_v & COLOUR_MASK, EXPIRED_COLOUR);
assert_ne!(old_v & !COLOUR_MASK, 0);
}
pub(super) fn dec(&self) -> RefCountResult {
let old_v = self.v.fetch_sub(1 << COUNT_SHIFT, Ordering::Relaxed);
assert_ne!(old_v & !COLOUR_MASK, 0);
if (old_v & !COLOUR_MASK) == 1 << COUNT_SHIFT {
RefCountResult::NeedGC
} else {
RefCountResult::NoNeed
}
}
pub(super) fn dec_reachable(&self) {
let old_v = self.v.fetch_sub(1 << COUNT_SHIFT, Ordering::Relaxed);
assert_ne!(old_v & !COLOUR_MASK, 0);
assert_ne!(old_v & COLOUR_MASK, WHITE_COLOUR);
assert_ne!(old_v & COLOUR_MASK, EXPIRED_COLOUR);
}
pub(super) fn mark_black(&self) {
let mut old_v = self.v.load(Ordering::Relaxed);
loop {
assert_eq!(old_v & COLOUR_MASK, GREY_COLOUR);
let mut new_v = old_v;
new_v &= !COLOUR_MASK;
new_v |= BLACK_COLOUR;
match self
.v
.compare_exchange_weak(old_v, new_v, Ordering::Relaxed, Ordering::Relaxed)
{
Ok(_) => break,
Err(x) => old_v = x,
}
}
}
pub(super) fn try_mark_grey<R>(&self, lock_fn: impl Fn() -> R) -> Result<R, ()> {
let mut old_v = WHITE_COLOUR;
loop {
assert_ne!(old_v & COLOUR_MASK, EXPIRED_COLOUR);
match old_v & COLOUR_MASK {
WHITE_COLOUR => {
let r = lock_fn();
let mut new_v = old_v;
new_v &= !COLOUR_MASK;
new_v |= GREY_COLOUR;
match self.v.compare_exchange_weak(
old_v,
new_v,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => return Ok(r),
Err(x) => old_v = x,
}
}
_ => return Err(()),
}
}
}
}