1use std::collections::HashMap;
2use std::marker::PhantomData;
3use std::ops::Deref;
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::sync::{Arc, Mutex, OnceLock};
6
7use crate::lock_or_recover;
8
9use rusteal_ffi::UObjectHandle;
10
11use crate::error::{RustealError, RustealResult};
12use crate::ffi_dispatch;
13use crate::object_ref::{Checked, UObjectRef};
14use crate::traits::{HasParent, UeClass, UeHandle, ValidHandle};
15
16fn alive_registry() -> &'static Mutex<HashMap<u64, Arc<AtomicBool>>> {
17 static REGISTRY: OnceLock<Mutex<HashMap<u64, Arc<AtomicBool>>>> = OnceLock::new();
18 REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
19}
20
21pub fn notify_pinned_destroyed(handle: UObjectHandle) {
22 if let Ok(registry) = alive_registry().lock()
23 && let Some(flag) = registry.get(&handle.to_addr())
24 {
25 flag.store(false, Ordering::Relaxed);
26 }
27}
28
29pub fn clear_all() {
30 if let Ok(mut registry) = alive_registry().lock() {
31 registry.clear();
32 }
33}
34
35#[repr(C)]
36pub struct Pinned<T: UeClass> {
37 handle: UObjectHandle,
38 alive: Arc<AtomicBool>,
39 _marker: PhantomData<*const T>,
40}
41
42unsafe impl<T: UeClass> Send for Pinned<T> {}
43
44impl<T: UeClass> Pinned<T> {
45 #[track_caller]
46 pub fn new(obj: UObjectRef<T>) -> RustealResult<Self> {
47 if !obj.is_valid() {
48 return Err(RustealError::object_destroyed());
49 }
50
51 let alive = Arc::new(AtomicBool::new(true));
52
53 lock_or_recover(alive_registry()).insert(obj.raw().to_addr(), alive.clone());
54
55 unsafe {
56 ffi_dispatch::lifecycle_add_gc_root(obj.raw());
57 ffi_dispatch::lifecycle_register_pinned(obj.raw());
58 }
59
60 Ok(Pinned {
61 handle: obj.raw(),
62 alive,
63 _marker: PhantomData,
64 })
65 }
66
67 #[inline]
68 pub fn is_alive(&self) -> bool {
69 self.alive.load(Ordering::Relaxed)
70 }
71
72 #[inline]
73 pub fn handle(&self) -> UObjectHandle {
74 self.handle
75 }
76
77 #[inline]
78 pub fn as_ref(&self) -> UObjectRef<T> {
79 unsafe { UObjectRef::from_raw(self.handle) }
80 }
81
82 #[inline]
83 pub fn as_checked(&self) -> Checked<T> {
84 debug_assert!(self.is_alive(), "Pinned object has been destroyed");
85
86 Checked::new_unchecked(self.handle)
87 }
88}
89
90impl<T: UeClass> Drop for Pinned<T> {
91 fn drop(&mut self) {
92 lock_or_recover(alive_registry()).remove(&self.handle.to_addr());
93
94 unsafe {
95 ffi_dispatch::lifecycle_unregister_pinned(self.handle);
96 ffi_dispatch::lifecycle_remove_gc_root(self.handle);
97 }
98 }
99}
100
101impl<T: UeClass> ValidHandle for Pinned<T> {
102 #[inline]
103 fn handle(&self) -> UObjectHandle {
104 debug_assert!(self.is_alive(), "Pinned object has been destroyed");
105
106 self.handle
107 }
108}
109
110impl<T: UeClass> UeHandle for Pinned<T> {
111 #[inline]
112 fn checked_handle(&self) -> RustealResult<UObjectHandle> {
113 if self.alive.load(Ordering::Relaxed) {
114 Ok(self.handle)
115 } else {
116 Err(RustealError::object_destroyed())
117 }
118 }
119
120 #[inline]
121 fn raw_handle(&self) -> UObjectHandle {
122 self.handle
123 }
124}
125
126impl<T: UeClass> std::fmt::Debug for Pinned<T> {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 f.debug_struct("Pinned")
129 .field("handle", &self.handle)
130 .field("alive", &self.is_alive())
131 .finish()
132 }
133}
134
135impl<T: HasParent> Deref for Pinned<T> {
136 type Target = Pinned<T::Parent>;
137 #[inline]
138 fn deref(&self) -> &Pinned<T::Parent> {
139 unsafe { &*(self as *const _ as *const Pinned<T::Parent>) }
140 }
141}