#![cfg_attr(not(feature = "std"), no_std)]
use core::{
any::Any,
cell::UnsafeCell,
fmt::Debug,
ops::{Deref, DerefMut},
pin::Pin,
ptr::NonNull,
};
#[cfg(all(feature = "std", not(feature = "bevy_platform")))]
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
};
#[cfg(feature = "bevy_platform")]
use bevy_platform::{
prelude::{Box, Vec},
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
};
#[cfg(feature = "local_collector")]
mod local;
#[cfg(feature = "local_collector")]
pub use local::*;
#[cfg(feature = "triple_buffer")]
mod triple_buffer_gc;
#[cfg(feature = "triple_buffer")]
pub use triple_buffer_gc::*;
struct CollectorState {
registry: Mutex<Vec<Box<dyn StrongCount + 'static>>>,
any_dropped: AtomicBool,
}
impl CollectorState {
const fn new() -> Self {
Self {
registry: Mutex::new(Vec::new()),
any_dropped: AtomicBool::new(false),
}
}
#[cfg(feature = "local_collector")]
fn with_capacity(capacity: usize) -> Self {
Self {
registry: Mutex::new(Vec::with_capacity(capacity)),
any_dropped: AtomicBool::new(false),
}
}
fn register<T: ?Sized + 'static>(&self, data: Arc<T>)
where
Arc<T>: StrongCount,
{
self.registry.lock().unwrap().push(Box::new(data));
}
fn remove<T: ?Sized>(&self, data: &Arc<T>) {
if Arc::strong_count(data) == 2 {
self.any_dropped.store(true, Ordering::Relaxed);
}
}
fn collect(&self) {
if self.any_dropped.load(Ordering::Relaxed) {
self.any_dropped.store(false, Ordering::Relaxed);
self.registry.lock().unwrap().retain(|ptr| ptr.count() > 1);
}
}
fn any_dropped(&self) -> bool {
self.any_dropped.load(Ordering::Relaxed)
}
fn num_allocations(&self) -> usize {
self.registry.lock().unwrap().len()
}
}
pub trait Collector: Send + Sync {
fn register<T>(&self, data: Arc<T>)
where
T: ?Sized + Send + Sync + 'static,
Arc<T>: StrongCount;
fn remove<T>(&self, data: &Arc<T>)
where
T: ?Sized + Send + Sync + 'static,
Arc<T>: StrongCount;
}
#[derive(Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GlobalRtGc;
static GLOBAL_COLLECTOR: CollectorState = CollectorState::new();
impl GlobalRtGc {
pub fn any_dropped() -> bool {
GLOBAL_COLLECTOR.any_dropped()
}
pub fn num_allocations() -> usize {
GLOBAL_COLLECTOR.num_allocations()
}
pub fn collect() {
GLOBAL_COLLECTOR.collect();
}
}
impl Collector for GlobalRtGc {
fn register<T>(&self, data: Arc<T>)
where
T: ?Sized + Send + Sync + 'static,
Arc<T>: StrongCount,
{
GLOBAL_COLLECTOR.register(data);
}
fn remove<T>(&self, data: &Arc<T>)
where
T: ?Sized + Send + Sync + 'static,
Arc<T>: StrongCount,
{
GLOBAL_COLLECTOR.remove(data);
}
}
pub trait StrongCount: Send + Sync {
fn count(&self) -> usize;
}
impl<T: Send + Sync + ?Sized> StrongCount for Arc<T> {
fn count(&self) -> usize {
Arc::strong_count(self)
}
}
pub struct ArcGc<T: ?Sized + Send + Sync + 'static, C: Collector = GlobalRtGc> {
data: Arc<T>,
collector: C,
}
impl<T: Send + Sync + 'static> ArcGc<T, GlobalRtGc> {
pub fn new(value: T) -> Self {
let data = Arc::new(value);
GLOBAL_COLLECTOR.register(Arc::clone(&data));
Self {
data,
collector: GlobalRtGc::default(),
}
}
}
impl<T: ?Sized + Send + Sync + 'static> ArcGc<T, GlobalRtGc> {
pub fn new_unsized(f: impl FnOnce() -> Arc<T>) -> Self {
let data = f();
GLOBAL_COLLECTOR.register(Arc::clone(&data));
Self {
data,
collector: GlobalRtGc::default(),
}
}
}
impl ArcGc<dyn Any + Send + Sync + 'static, GlobalRtGc> {
pub fn new_any<T: Send + Sync + 'static>(value: T) -> Self {
ArcGc::<dyn Any + Send + Sync + 'static, GlobalRtGc>::new_unsized(|| {
let a: Arc<dyn Any + Send + Sync + 'static> = Arc::new(value);
a
})
}
}
impl<T: ?Sized + Send + Sync + 'static, C: Collector> ArcGc<T, C> {
#[inline(always)]
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
Arc::ptr_eq(&this.data, &other.data)
}
}
impl<T: ?Sized + Send + Sync + 'static, C: Collector> Deref for ArcGc<T, C> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: ?Sized + Send + Sync + 'static, C: Collector> Drop for ArcGc<T, C> {
fn drop(&mut self) {
self.collector.remove(&self.data);
}
}
impl<T: ?Sized + Send + Sync + 'static, C: Collector + Clone> Clone for ArcGc<T, C> {
fn clone(&self) -> Self {
Self {
data: Arc::clone(&self.data),
collector: self.collector.clone(),
}
}
}
impl<T: ?Sized + Send + Sync + 'static, C: Collector> PartialEq for ArcGc<T, C> {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.data, &other.data)
}
}
impl<T: ?Sized + Send + Sync + 'static, C: Collector> Eq for ArcGc<T, C> {}
impl<T: Debug + ?Sized + Send + Sync + 'static, C: Collector> Debug for ArcGc<T, C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Debug::fmt(ArcGc::deref(&self), f)
}
}
pub struct OwnedGc<T: ?Sized + Send + 'static, C: Collector = GlobalRtGc> {
data: ArcGc<OwnedGcWrapper<T>, C>,
}
impl<T: Send + 'static> OwnedGc<T, GlobalRtGc> {
pub fn new(value: T) -> Self {
Self {
data: ArcGc::<_, GlobalRtGc>::new(OwnedGcWrapper(UnsafeCell::new(value))),
}
}
}
impl<T: ?Sized + Send + 'static, C: Collector> OwnedGc<T, C> {
pub fn get(&self) -> &T {
unsafe { &*UnsafeCell::get(&(*self.data).0) }
}
pub fn get_mut(&mut self) -> &mut T {
unsafe { &mut *UnsafeCell::get(&(*self.data).0) }
}
}
impl<T: Send + 'static, C: Collector> OwnedGc<T, C> {
pub fn swap(&mut self, data: &mut T) {
core::mem::swap(self.get_mut(), data);
}
}
impl<T: ?Sized + Send + 'static, C: Collector> Deref for OwnedGc<T, C> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.get()
}
}
impl<T: ?Sized + Send + 'static, C: Collector> DerefMut for OwnedGc<T, C> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.get_mut()
}
}
impl<T: Debug + ?Sized + Send + 'static, C: Collector> Debug for OwnedGc<T, C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Debug::fmt(self.get(), f)
}
}
#[repr(transparent)]
struct OwnedGcWrapper<T: ?Sized + Send + 'static>(UnsafeCell<T>);
unsafe impl<T: ?Sized + Send + 'static> Sync for OwnedGcWrapper<T> {}
pub struct OwnedGcUnsized<T: ?Sized + Send + 'static, C: Collector = GlobalRtGc> {
_owned: OwnedGc<Pin<Box<T>>, C>,
ptr: NonNull<T>,
}
impl<T: ?Sized + Send + 'static> OwnedGcUnsized<T, GlobalRtGc> {
pub fn new_unsized(data: Box<T>) -> Self {
let pinned = Box::into_pin(data);
let ptr = NonNull::from_ref(pinned.deref());
Self {
_owned: OwnedGc::new(pinned),
ptr,
}
}
}
impl<T: ?Sized + Send + 'static, C: Collector> OwnedGcUnsized<T, C> {
pub fn get(&self) -> &T {
unsafe { self.ptr.as_ref() }
}
pub fn get_mut(&mut self) -> &mut T {
unsafe { self.ptr.as_mut() }
}
}
unsafe impl<T: ?Sized + Send + 'static, C: Collector> Send for OwnedGcUnsized<T, C> {}
impl<T: ?Sized + Send + 'static, C: Collector> Deref for OwnedGcUnsized<T, C> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.get()
}
}
impl<T: ?Sized + Send + 'static, C: Collector> DerefMut for OwnedGcUnsized<T, C> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.get_mut()
}
}
impl<T: Debug + ?Sized + Send + 'static, C: Collector> Debug for OwnedGcUnsized<T, C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Debug::fmt(self.get(), f)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_global() {
assert_eq!(GLOBAL_COLLECTOR.num_allocations(), 0);
let value = ArcGc::new(1);
assert_eq!(GLOBAL_COLLECTOR.num_allocations(), 1);
assert_eq!(GLOBAL_COLLECTOR.any_dropped(), false);
GLOBAL_COLLECTOR.collect();
assert_eq!(GLOBAL_COLLECTOR.num_allocations(), 1);
assert_eq!(GLOBAL_COLLECTOR.any_dropped(), false);
drop(value);
assert_eq!(GLOBAL_COLLECTOR.num_allocations(), 1);
assert_eq!(GLOBAL_COLLECTOR.any_dropped(), true);
GLOBAL_COLLECTOR.collect();
assert_eq!(GLOBAL_COLLECTOR.num_allocations(), 0);
assert_eq!(GLOBAL_COLLECTOR.any_dropped(), false);
let value = ArcGc::new_unsized(|| Arc::<[i32]>::from([1, 2, 3]));
assert_eq!(GLOBAL_COLLECTOR.num_allocations(), 1);
assert_eq!(GLOBAL_COLLECTOR.any_dropped(), false);
GLOBAL_COLLECTOR.collect();
assert_eq!(GLOBAL_COLLECTOR.num_allocations(), 1);
assert_eq!(GLOBAL_COLLECTOR.any_dropped(), false);
drop(value);
assert_eq!(GLOBAL_COLLECTOR.num_allocations(), 1);
assert_eq!(GLOBAL_COLLECTOR.any_dropped(), true);
GLOBAL_COLLECTOR.collect();
assert_eq!(GLOBAL_COLLECTOR.num_allocations(), 0);
assert_eq!(GLOBAL_COLLECTOR.any_dropped(), false);
}
}