Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shared pointer #66

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Next Next commit
feat: implement shared ptr
Roms1383 committed Sep 18, 2024
commit c90c449c6b83d779555d54005929fff2b15075dc
2 changes: 1 addition & 1 deletion src/types.rs
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ pub use tweak_db_id::TweakDbId;
pub mod array;
pub use array::RedArray;
mod refs;
pub use refs::{Ref, ScriptRef, WeakRef};
pub use refs::{Ref, ScriptRef, SharedPtr, WeakRef};
mod string;
pub use string::RedString;
mod cname;
15 changes: 15 additions & 0 deletions src/types/allocator.rs
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ use std::{mem, ops, ptr};
use once_cell::race::OnceNonZeroUsize;
use sealed::sealed;

use super::refs::RefCount;
use super::{GlobalFunction, IScriptable, Method, Property, StaticMethod};
use crate::raw::root::RED4ext as red;
use crate::raw::root::RED4ext::Memory::AllocationResult;
@@ -112,6 +113,11 @@ impl Poolable for IScriptable {
type Pool = ScriptPool;
}

#[sealed]
impl Poolable for RefCount {
type Pool = RefCountPool;
}

#[sealed]
impl<T> Poolable for mem::MaybeUninit<T>
where
@@ -209,6 +215,15 @@ impl Pool for ScriptPool {
const NAME: &'static str = "PoolScript";
}

/// A pool for reference counters.
#[derive(Debug)]
pub struct RefCountPool;

#[sealed]
impl Pool for RefCountPool {
const NAME: &'static str = "PoolRefCount";
}

#[cold]
unsafe fn vault_get(handle: u32) -> Option<NonZero<usize>> {
let vault = &mut *red::Memory::Vault::Get();
75 changes: 73 additions & 2 deletions src/types/refs.rs
Original file line number Diff line number Diff line change
@@ -2,11 +2,12 @@ use std::marker::PhantomData;
use std::sync::atomic::{AtomicU32, Ordering};
use std::{mem, ptr};

use super::{CName, ISerializable, Type};
use super::{CName, ISerializable, PoolRef, Type};
use crate::class::{NativeType, ScriptClass};
use crate::raw::root::RED4ext as red;
use crate::repr::NativeRepr;
use crate::systems::RttiSystem;
use crate::types::PoolableOps;
use crate::{ClassKind, VoidPtr};

/// A reference counted shared pointer to a script class.
@@ -268,7 +269,7 @@ impl<T> Clone for BaseRef<T> {

#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
struct RefCount(red::RefCnt);
pub(crate) struct RefCount(red::RefCnt);

impl RefCount {
#[inline]
@@ -280,6 +281,16 @@ impl RefCount {
fn weak_refs(&self) -> &AtomicU32 {
unsafe { AtomicU32::from_ptr(&self.0.weakRefs as *const _ as _) }
}

fn new() -> PoolRef<Self> {
let mut refcount = RefCount::alloc().expect("should allocate a RefCount");
let ptr = refcount.as_mut_ptr();
unsafe {
(*ptr).0.strongRefs = 1;
(*ptr).0.weakRefs = 1;
refcount.assume_init()
}
}
}

/// A reference to local script data.
@@ -318,3 +329,63 @@ impl<'a, T: NativeRepr> ScriptRef<'a, T> {
!self.0.ref_.is_null()
}
}

#[derive(Debug)]
#[repr(transparent)]
pub struct SharedPtr<T>(red::SharedPtrBase<T>);

impl<T: NativeRepr> Clone for SharedPtr<T> {
#[inline]
fn clone(&self) -> Self {
self.inc_strong();
unsafe { ptr::read(self) }
}
}

impl<T: Default + NativeRepr> SharedPtr<T> {
pub fn new_with(mut value: T) -> Self {
let mut this = red::SharedPtrBase::<T>::default();
let mut refcount = RefCount::new();
this.refCount = &mut *refcount as *const _ as *mut _;
this.instance = &mut value as *const _ as *mut _;
mem::forget(refcount);
mem::forget(value);
Self(this)
}
}

impl<T> SharedPtr<T> {
#[inline]
fn ref_count(&self) -> Option<&RefCount> {
unsafe { self.0.refCount.cast::<RefCount>().as_ref() }
}

#[inline]
fn inc_strong(&self) {
if let Some(cnt) = self.ref_count() {
cnt.strong().fetch_add(1, Ordering::Relaxed);
}
}

fn dec_strong(&mut self) -> bool {
let Some(cnt) = self.ref_count() else {
return false;
};

cnt.strong().fetch_sub(1, Ordering::Relaxed) == 1
}
}

impl<T> Drop for SharedPtr<T> {
fn drop(&mut self) {
if self.dec_strong() && !self.0.instance.is_null() {
let own_refcount =
unsafe { mem::transmute::<*mut red::RefCnt, PoolRef<RefCount>>(self.0.refCount) };
let ptr_instance = self.0.instance;
mem::drop(own_refcount);
unsafe {
ptr::drop_in_place(ptr_instance);
}
}
}
}