use std::ops::Deref;
use std::sync::Arc;
use bevy::prelude::World;
use parking_lot::{
MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard,
};
#[derive(Debug, Clone)]
pub struct WorldPointer(Arc<RwLock<Option<*mut World>>>);
#[derive(Debug)]
pub struct WorldPointerGuard(WorldPointer);
impl Deref for WorldPointerGuard {
type Target = WorldPointer;
fn deref(&self) -> &Self::Target {
&self.0
}
}
unsafe impl Send for WorldPointer {}
unsafe impl Sync for WorldPointer {}
impl WorldPointerGuard {
#[allow(clippy::arc_with_non_send_sync)]
pub unsafe fn new(world: &mut World) -> Self {
WorldPointerGuard(WorldPointer(Arc::new(RwLock::new(Some(world)))))
}
}
impl Drop for WorldPointerGuard {
fn drop(&mut self) {
let world_ptr: &WorldPointer = &self.0;
let _: Option<*mut World> = RwLock::write(&world_ptr.0).take();
}
}
impl WorldPointer {
pub fn read(&self) -> MappedRwLockReadGuard<World> {
self.try_read().expect("concurrent read/write world access")
}
pub fn write(&self) -> MappedRwLockWriteGuard<World> {
self.try_write()
.expect("concurrent read/write world access")
}
pub fn try_read(&self) -> Option<MappedRwLockReadGuard<World>> {
self.try_read_inner(false)
}
pub fn try_write(&self) -> Option<MappedRwLockWriteGuard<World>> {
self.try_write_inner(false)
}
pub fn read_blocking(&self) -> MappedRwLockReadGuard<World> {
self.try_read_blocking()
.expect("the world pointer is out of scope")
}
pub fn write_blocking(&self) -> MappedRwLockWriteGuard<World> {
self.try_write_blocking()
.expect("the world pointer is out of scope")
}
pub fn try_read_blocking(&self) -> Option<MappedRwLockReadGuard<World>> {
self.try_read_inner(true)
}
pub fn try_write_blocking(&self) -> Option<MappedRwLockWriteGuard<World>> {
self.try_write_inner(true)
}
fn try_read_inner(&self, blocking: bool) -> Option<MappedRwLockReadGuard<World>> {
let guard = if blocking {
self.0.read()
} else {
self.0.try_read()?
};
if guard.is_none() {
return None;
}
Some(RwLockReadGuard::map(
guard,
|ptr: &Option<*mut World>| unsafe { &*ptr.unwrap() },
))
}
fn try_write_inner(&self, blocking: bool) -> Option<MappedRwLockWriteGuard<World>> {
let guard = if blocking {
self.0.write()
} else {
self.0.try_write()?
};
if guard.is_none() {
return None;
}
Some(RwLockWriteGuard::map(
guard,
|ptr: &mut Option<*mut World>| unsafe { &mut *ptr.unwrap() },
))
}
}