#![allow(clippy::inline_always)]
use core::sync::atomic::{AtomicI32, Ordering};
use crate::object::lean_is_scalar;
use crate::repr::LeanObjectRepr;
use crate::types::lean_object;
unsafe extern "C" {
pub fn lean_dec_ref_cold(o: *mut lean_object);
pub fn lean_mark_mt(o: *mut lean_object);
pub fn lean_mark_persistent(o: *mut lean_object);
}
#[inline(always)]
unsafe fn rc_atom<'a>(o: *mut lean_object) -> &'a AtomicI32 {
unsafe {
let repr = o.cast::<LeanObjectRepr>();
AtomicI32::from_ptr(&raw mut (*repr).m_rc)
}
}
#[inline(always)]
pub unsafe fn lean_inc_ref_n(o: *mut lean_object, n: usize) {
let rc = unsafe { rc_atom(o) };
let cur = rc.load(Ordering::Relaxed);
let delta = n as i32;
if cur > 0 {
rc.store(cur.strict_add(delta), Ordering::Relaxed);
} else if cur != 0 {
rc.fetch_sub(delta, Ordering::Relaxed);
}
}
#[inline(always)]
pub unsafe fn lean_inc_ref(o: *mut lean_object) {
unsafe { lean_inc_ref_n(o, 1) }
}
#[inline(always)]
pub unsafe fn lean_inc(o: *mut lean_object) {
unsafe {
if !lean_is_scalar(o) {
lean_inc_ref(o);
}
}
}
#[inline(always)]
pub unsafe fn lean_inc_n(o: *mut lean_object, n: usize) {
unsafe {
if !lean_is_scalar(o) {
lean_inc_ref_n(o, n);
}
}
}
#[inline(always)]
pub unsafe fn lean_dec_ref(o: *mut lean_object) {
let rc = unsafe { rc_atom(o) };
let cur = rc.load(Ordering::Relaxed);
if cur > 1 {
rc.store(cur.strict_sub(1), Ordering::Relaxed);
} else if cur != 0 {
unsafe { lean_dec_ref_cold(o) }
}
}
#[inline(always)]
pub unsafe fn lean_dec(o: *mut lean_object) {
unsafe {
if !lean_is_scalar(o) {
lean_dec_ref(o);
}
}
}
#[cfg(test)]
mod tests {
use super::{lean_dec, lean_dec_ref, lean_inc, lean_inc_n, lean_inc_ref, lean_inc_ref_n};
use crate::object::lean_box;
use crate::repr::test_support::MockObject;
#[test]
fn single_threaded_inc_dec_round_trips() {
let obj = MockObject::new(1, 0, 0);
let o = obj.ptr();
unsafe {
lean_inc_ref(o);
assert_eq!(obj.rc(), 2);
lean_dec_ref(o);
assert_eq!(obj.rc(), 1);
}
}
#[test]
fn inc_ref_n_adds_in_place() {
let obj = MockObject::new(1, 0, 0);
let o = obj.ptr();
unsafe {
lean_inc_ref_n(o, 9);
}
assert_eq!(obj.rc(), 10);
}
#[test]
fn multi_threaded_inc_decrements_negated_count() {
let obj = MockObject::new(-1, 0, 0);
let o = obj.ptr();
unsafe {
lean_inc_ref(o);
}
assert_eq!(obj.rc(), -2);
}
#[test]
fn scalar_pointers_are_left_untouched() {
unsafe {
let scalar = lean_box(42);
lean_inc(scalar);
lean_inc_n(scalar, 7);
lean_dec(scalar);
assert_eq!(scalar, lean_box(42));
}
}
}