Shared pointer - #66
Conversation
This reverts commit 7b343bf.
# Conflicts: # src/types/allocator.rs
# Conflicts: # src/types/allocator.rs
| let mut this = red::SharedPtrBase::<T>::default(); | ||
| let refcount = RefCount::new(); | ||
| this.refCount = refcount.0 as *mut red::RefCnt; | ||
| this.instance = Box::leak(Box::new(value)) as *const _ as *mut _; |
There was a problem hiding this comment.
| this.instance = Box::leak(Box::new(value)) as *const _ as *mut _; | |
| this.instance = Box::leak(Box::new(value)) as *const _ as *mut _; |
this is pretty bad, using this constructor repeatedely will leak lots of memory
There was a problem hiding this comment.
isn't this memory eventually reclaimed in Drop impl ? (ptr::drop_in_place(ptr_instance);)
There was a problem hiding this comment.
From what I read on the internet, this part is partially correct, only that in the Drop implementation ptr::drop_in_place will not destroy the heap object.
What I saw, is that you do this:
| this.instance = Box::leak(Box::new(value)) as *const _ as *mut _; | |
| this.instance = Box::into_raw(value); |
and then in Drop instead of ptr::drop_in_place, you do drop(Box::from_raw(...)).
Note: I haven't used Rust, so this is my understanding after some Googling and reading the docs.
There was a problem hiding this comment.
SharedPtr<T> only makes sense if this specific SharedPtr<T> is already used by the game (not even for every T known to the engine), and only if the right allocator is used for instance in this specific SharedPtr<T> case (or sometimes T).
It doesn't make sense for any T that wasn't used as SharedPtr<T> by the game, and won't work for any instance allocated by rust instead of original engine allocator.
There was a problem hiding this comment.
thanks @wopss i think i understand your part and will make the appropriate changes.
following psiberx comment which i understand only partially @jac3km4, in Rust does it simply mean:
- making
SharedPtr<T>(red::SharedPtrBase<T>)intoSharedPtr<T, A>(red::SharedPtrBase<T>, PhantomData<A>);
then, e.g. turningimpl<T> Clone for SharedPtr<T>intoimpl<T> Clone for SharedPtr<T, crate::types::IAllocator>(for all the impl out there) - or i need to take the extra leap and use RED4ext methods from
bindgenlike maybeIAllocator_Alloc/IAllocator_AllocAlignedandIAllocator_Free(inIAllocator__bindgen_vtable).
these things are still out of my league, so ur help is very much appreciated ❤️
There was a problem hiding this comment.
What @psiberx pointed out is that the memory for the shared object is Allocated using Rust's default allocator, but the memory in the game engine is allocated using a custom allocator, through IAllocator interfaces and friends.
If you allocate memory in Rust but then pass the ownership to the engine (for example, by putting the object into a game-owned DynArray), the engine won't recognize where that memory came from and might panic.
So, the correct approach is: instead of using Box::new, use the allocator from the engine (IAllocator_*).
As for the SharedPtr<T, A> idea: I'm not sure if that's a good option, my Rust knowledge is 0. @jac3km4, might be better suited to judge that part. In our C++ code, we get the allocator from type aliases or methods like GetAllocator.
There was a problem hiding this comment.
I reckon that this might be simply this from my other PR @wopss : https://github.com/jac3km4/red4ext-rs/pull/87/changes#diff-6a173a8f0b02eb1ec671b6dddc0ff72617c14e04c89606b127db20ec279d7eb1R1612
Co-authored-by: jekky <gh@jekky.dev>
Co-authored-by: jekky <gh@jekky.dev>
|
Also this comment in RED4ext.SDK: static_assert(Memory::IsDeleteCompatible<T>,
"SharedPtr only supports types that define the allocator type and are destructible "
"(a polymorphic type requires a virtual destructor)"); |
|
I can't be 100% sure as I don't know rust either, but it looks to me like it's still incorrectly implemented and relies on rust allocations. |
|
Yes I left it aside, but I do remember the reason of it in the first place now. Currently with |
|
In a nutshell if I simply do this: fn store_community_registry(community_registry: Ref<WorldCommunityRegistryNode>) {}No compilation error. exports![
::red4ext_rs::GlobalExport(::red4ext_rs::global!(
c"MyMod.StoreCommunityRegistry",
store_community_registry
)),
]Rust compiler complains: the trait bound fn(Ref<...>) {store_community_registry}: GlobalInvocable<_, _> is not satisfied
the trait GlobalInvocable<_, _> is not implemented for fn item fn(red4ext_rs::types::Ref<WorldCommunityRegistryNode>) {store_community_registry}Even though #[repr(C)]
pub struct WorldCommunityRegistryNode {
pub base: ISerializable,
pub is_visible_in_game: bool, // 0x30
pub is_host_only: bool, // 0x31
pub spawn_set_name_to_community_id: GameCommunitySpawnSetNameToId, // 0x38
pub crowd_creation_registry: Ref<GameCrowdCreationDataRegistry>, // 0x48
pub communities_data: RedArray<WorldCommunityRegistryItem>, // 0x58
pub workspots_persistent_data: RedArray<AiSpotPersistentData>, // 0x68
pub represents_crowd: bool, // 0x78
}
unsafe impl ScriptClass for WorldCommunityRegistryNode {
const NAME: &'static str = "worldCommunityRegistryNode";
type Kind = class_kind::Native;
}
impl AsRef<ISerializable> for WorldCommunityRegistryNode {
#[inline]
fn as_ref(&self) -> &ISerializable {
&self.base
}
}Solving it would allow me to iterate and prototype faster @jac3km4 :) |
|
Ok at least I found a trick that allows me to get going. module MyMod
public native func LookMaICanUseISerializable(resource: ref<ISerializable>);and // in Plugin trait impl
exports![
::red4ext_rs::GlobalExport(::red4ext_rs::global!(
c"MyMod.LookMaICanUseISerializable",
look_ma_i_can_use_iserializable
)),
]
// then
fn look_ma_i_can_use_iserializable(resource: Ref<ISerializable>) {
if resource.is_null() {
red4ext_rs::log::error!("ref<ISerializable> is null");
return;
}
let Some(lanes) = (unsafe { resource.fields() }) else {
red4ext_rs::log::error!("ref<ISerializable> fields cannot be accessed");
return;
};
let lanes =
unsafe { std::mem::transmute::<&ISerializable, &WorldTrafficPersistentResource>(lanes) };
// do something with ref<worldTrafficPersistentResource> ...
} |
Add basic and leaky support for
red::SharedPtrto allow working with internal structs.Safety must be audited first.