use core::{any::Any, cell::UnsafeCell, ops::Deref, ptr::NonNull};
#[cfg(all(feature = "std", not(feature = "bevy_platform")))]
use std::sync::Arc;
#[cfg(feature = "bevy_platform")]
use bevy_platform::{prelude::Box, sync::Arc};
use crate::{
ArcGc, Collector, CollectorState, OwnedGc, OwnedGcUnsized, OwnedGcWrapper, StrongCount,
};
pub struct LocalRtGc {
shared_state: Arc<CollectorState>,
}
impl LocalRtGc {
pub fn new() -> Self {
Self {
shared_state: Arc::new(CollectorState::new()),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
shared_state: Arc::new(CollectorState::with_capacity(capacity)),
}
}
pub fn handle(&self) -> LocalRtGcHandle {
LocalRtGcHandle {
shared_state: Arc::clone(&self.shared_state),
}
}
pub fn any_dropped(&self) -> bool {
self.shared_state.any_dropped()
}
pub fn num_allocations(&self) -> usize {
self.shared_state.num_allocations()
}
pub fn collect(&mut self) {
self.shared_state.collect();
}
pub fn strong_count(&self) -> usize {
Arc::strong_count(&self.shared_state)
}
}
impl Collector for LocalRtGc {
fn register<T>(&self, data: Arc<T>)
where
T: ?Sized + Send + Sync + 'static,
Arc<T>: StrongCount,
{
self.shared_state.register(data);
}
fn remove<T>(&self, data: &Arc<T>)
where
T: ?Sized + Send + Sync + 'static,
Arc<T>: StrongCount,
{
self.shared_state.remove(data);
}
}
impl Default for LocalRtGc {
fn default() -> Self {
Self::new()
}
}
impl Clone for LocalRtGc {
fn clone(&self) -> Self {
Self {
shared_state: Arc::clone(&self.shared_state),
}
}
}
pub struct LocalRtGcHandle {
shared_state: Arc<CollectorState>,
}
impl LocalRtGcHandle {
pub fn num_allocations(&self) -> usize {
self.shared_state.num_allocations()
}
pub fn any_dropped(&self) -> bool {
self.shared_state.any_dropped()
}
pub fn strong_count(&self) -> usize {
Arc::strong_count(&self.shared_state)
}
}
impl Clone for LocalRtGcHandle {
fn clone(&self) -> Self {
Self {
shared_state: Arc::clone(&self.shared_state),
}
}
}
impl<T: Send + Sync + 'static> ArcGc<T, LocalRtGc> {
pub fn new_loc(value: T, handle: &LocalRtGcHandle) -> Self {
let data = Arc::new(value);
handle.shared_state.register(Arc::clone(&data));
Self {
data,
collector: LocalRtGc {
shared_state: Arc::clone(&handle.shared_state),
},
}
}
}
impl<T: ?Sized + Send + Sync + 'static> ArcGc<T, LocalRtGc> {
pub fn new_unsized_loc(f: impl FnOnce() -> Arc<T>, handle: &LocalRtGcHandle) -> Self {
let data = f();
handle.shared_state.register(Arc::clone(&data));
Self {
data,
collector: LocalRtGc {
shared_state: Arc::clone(&handle.shared_state),
},
}
}
}
impl ArcGc<dyn Any + Send + Sync + 'static, LocalRtGc> {
pub fn new_any_loc<T: Send + Sync + 'static>(value: T, handle: &LocalRtGcHandle) -> Self {
ArcGc::<dyn Any + Send + Sync + 'static, LocalRtGc>::new_unsized_loc(
|| {
let a: Arc<dyn Any + Send + Sync + 'static> = Arc::new(value);
a
},
handle,
)
}
}
impl<T: Send + 'static> OwnedGc<T, LocalRtGc> {
pub fn new_loc(value: T, handle: &LocalRtGcHandle) -> Self {
Self {
data: ArcGc::<_, LocalRtGc>::new_loc(OwnedGcWrapper(UnsafeCell::new(value)), handle),
}
}
}
impl<T: ?Sized + Send + 'static> OwnedGcUnsized<T, LocalRtGc> {
pub fn new_unsized_loc(data: Box<T>, handle: &LocalRtGcHandle) -> Self {
let pinned = Box::into_pin(data);
let ptr = NonNull::from_ref(pinned.deref());
Self {
_owned: OwnedGc::<_, LocalRtGc>::new_loc(pinned, handle),
ptr,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_local_drop() {
let mut collector = LocalRtGc::new();
let handle = collector.handle();
assert_eq!(collector.num_allocations(), 0);
let value = ArcGc::new_loc(1, &handle);
assert_eq!(collector.num_allocations(), 1);
assert_eq!(collector.any_dropped(), false);
collector.collect();
assert_eq!(collector.num_allocations(), 1);
assert_eq!(collector.any_dropped(), false);
drop(value);
assert_eq!(collector.num_allocations(), 1);
assert_eq!(collector.any_dropped(), true);
collector.collect();
assert_eq!(collector.num_allocations(), 0);
assert_eq!(collector.any_dropped(), false);
}
#[test]
fn test_local_unsized() {
let mut collector = LocalRtGc::new();
let handle = collector.handle();
assert_eq!(collector.num_allocations(), 0);
let value = ArcGc::new_unsized_loc(|| Arc::<[i32]>::from([1, 2, 3]), &handle);
assert_eq!(collector.num_allocations(), 1);
assert_eq!(collector.any_dropped(), false);
collector.collect();
assert_eq!(collector.num_allocations(), 1);
assert_eq!(collector.any_dropped(), false);
drop(value);
assert_eq!(collector.num_allocations(), 1);
assert_eq!(collector.any_dropped(), true);
collector.collect();
assert_eq!(collector.num_allocations(), 0);
assert_eq!(collector.any_dropped(), false);
}
}