use crate::Finalize;
use crate::finalizer::FinalizerFn;
use crate::pointers::SafePtr;
use std::sync::Mutex;
use std::sync::atomic::{AtomicBool, Ordering};
use tracing::warn;
type DropFn = Option<Box<dyn FnOnce() + Send + Sync>>;
const INITIAL_MARK_STATE: bool = true;
pub(crate) struct ObjectMetadata {
ptr: SafePtr,
size: usize,
marked: AtomicBool,
was_ever_marked: AtomicBool,
drop_fn: Mutex<DropFn>,
finalizer: Mutex<Option<FinalizerFn>>,
}
impl ObjectMetadata {
pub(crate) fn new_for_gc<T: Send + Sync>(ptr: SafePtr, size: usize) -> Self {
let ptr_addr = ptr.0;
let drop_fn: DropFn = Some(Box::new(move || {
unsafe {
if ptr_addr != 0 {
let raw_ptr = ptr_addr as *mut T;
if !raw_ptr.is_null() {
let _boxed = Box::from_raw(raw_ptr);
}
}
}
}));
Self {
ptr,
size,
marked: AtomicBool::new(INITIAL_MARK_STATE),
was_ever_marked: AtomicBool::new(false),
drop_fn: Mutex::new(drop_fn),
finalizer: Mutex::new(None), }
}
pub(crate) fn new_for_gc_with_finalizer<T: Send + Sync + Finalize>(
ptr: SafePtr,
size: usize,
) -> Self {
let ptr_addr = ptr.0;
let drop_fn: DropFn = Some(Box::new(move || {
unsafe {
if ptr_addr != 0 {
let raw_ptr = ptr_addr as *mut T;
if !raw_ptr.is_null() {
let _boxed = Box::from_raw(raw_ptr);
}
}
}
}));
let finalizer: Option<FinalizerFn> = if ptr_addr != 0 {
Some(Box::new(move || {
unsafe {
let data_ptr = ptr_addr as *const T;
if !data_ptr.is_null() {
(*data_ptr).finalize();
}
}
}))
} else {
None
};
Self {
ptr,
size,
marked: AtomicBool::new(INITIAL_MARK_STATE),
was_ever_marked: AtomicBool::new(false),
drop_fn: Mutex::new(drop_fn),
finalizer: Mutex::new(finalizer),
}
}
pub(crate) fn ptr(&self) -> &SafePtr {
&self.ptr
}
pub(crate) fn size(&self) -> usize {
self.size
}
pub(crate) fn is_marked(&self) -> bool {
self.marked.load(Ordering::Acquire)
}
pub(crate) fn mark(&self) -> bool {
self.was_ever_marked.store(true, Ordering::Release);
!self.marked.swap(true, Ordering::AcqRel)
}
pub(crate) fn was_ever_marked(&self) -> bool {
self.was_ever_marked.load(Ordering::Acquire)
}
pub(crate) fn unmark(&self) {
self.marked.store(false, Ordering::Release);
}
pub(crate) fn drop_object(&self) {
self.run_drop_closures();
}
pub(crate) fn run_finalizer(&self) {
match self.finalizer.try_lock() {
Ok(mut guard) => {
if let Some(finalizer) = guard.take() {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
finalizer();
}))
.unwrap_or_else(|_| {
warn!("Finalizer panicked during object cleanup");
});
}
}
Err(_) => {
warn!("Failed to acquire finalizer lock during object cleanup; finalizer skipped");
}
}
}
pub(crate) fn run_drop_fn(&self) {
match self.drop_fn.try_lock() {
Ok(mut guard) => {
if let Some(drop_fn) = guard.take() {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
drop_fn();
}))
.unwrap_or_else(|_| {
warn!("Drop function panicked during object cleanup");
});
}
}
Err(_) => {
warn!("Failed to acquire drop_fn lock during object cleanup; drop skipped");
}
}
}
fn run_drop_closures(&self) {
self.run_finalizer();
self.run_drop_fn();
}
}
#[expect(clippy::missing_fields_in_debug)]
impl std::fmt::Debug for ObjectMetadata {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ObjectMetadata")
.field("ptr", &self.ptr)
.field("size", &self.size)
.field("marked", &self.is_marked())
.field("was_ever_marked", &self.was_ever_marked())
.finish()
}
}