use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, OnceLock};
use uika_ffi::UObjectHandle;
use crate::api::api;
use crate::error::{UikaError, UikaResult};
use crate::object_ref::{Checked, UObjectRef};
use crate::traits::{UeClass, UeHandle, ValidHandle};
fn alive_registry() -> &'static Mutex<HashMap<usize, Arc<AtomicBool>>> {
static REGISTRY: OnceLock<Mutex<HashMap<usize, Arc<AtomicBool>>>> = OnceLock::new();
REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
}
pub fn notify_pinned_destroyed(handle: UObjectHandle) {
if let Ok(registry) = alive_registry().lock() {
if let Some(flag) = registry.get(&(handle.0 as usize)) {
flag.store(false, Ordering::Relaxed);
}
}
}
pub fn clear_all() {
if let Ok(mut registry) = alive_registry().lock() {
registry.clear();
}
}
pub struct Pinned<T: UeClass> {
handle: UObjectHandle,
alive: Arc<AtomicBool>,
_marker: PhantomData<*const T>, }
unsafe impl<T: UeClass> Send for Pinned<T> {}
impl<T: UeClass> Pinned<T> {
pub fn new(obj: UObjectRef<T>) -> UikaResult<Self> {
if !obj.is_valid() {
return Err(UikaError::ObjectDestroyed);
}
let alive = Arc::new(AtomicBool::new(true));
alive_registry().lock().unwrap()
.insert(obj.raw().0 as usize, alive.clone());
unsafe {
((*api().lifecycle).add_gc_root)(obj.raw());
((*api().lifecycle).register_pinned)(obj.raw());
}
Ok(Pinned {
handle: obj.raw(),
alive,
_marker: PhantomData,
})
}
#[inline]
pub fn is_alive(&self) -> bool {
self.alive.load(Ordering::Relaxed)
}
#[inline]
pub fn handle(&self) -> UObjectHandle {
self.handle
}
#[inline]
pub fn as_ref(&self) -> UObjectRef<T> {
unsafe { UObjectRef::from_raw(self.handle) }
}
#[inline]
pub fn as_checked(&self) -> Checked<T> {
debug_assert!(self.is_alive(), "Pinned object has been destroyed");
Checked::new_unchecked(self.handle)
}
}
impl<T: UeClass> Drop for Pinned<T> {
fn drop(&mut self) {
alive_registry().lock().unwrap().remove(&(self.handle.0 as usize));
unsafe {
((*api().lifecycle).unregister_pinned)(self.handle);
((*api().lifecycle).remove_gc_root)(self.handle);
}
}
}
impl<T: UeClass> ValidHandle for Pinned<T> {
#[inline]
fn handle(&self) -> UObjectHandle {
debug_assert!(self.is_alive(), "Pinned object has been destroyed");
self.handle
}
}
impl<T: UeClass> UeHandle for Pinned<T> {
#[inline]
fn checked_handle(&self) -> UikaResult<UObjectHandle> {
if self.alive.load(Ordering::Relaxed) {
Ok(self.handle)
} else {
Err(UikaError::ObjectDestroyed)
}
}
#[inline]
fn raw_handle(&self) -> UObjectHandle {
self.handle
}
}
impl<T: UeClass> std::fmt::Debug for Pinned<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Pinned")
.field("handle", &self.handle)
.field("alive", &self.is_alive())
.finish()
}
}