use std::{clone::Clone, cell::Cell, error::Error, fmt, rc::Weak,
hash::{Hash, Hasher}, ptr::NonNull, panic, marker::PhantomData};
pub type HandleResult<T> = Result<T, HandleErr>;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum HandleErr {
AlreadyBorrowed,
AlreadyDropped
}
pub struct Handle<D: Clone, T, W: Handleable<D, T> + Sized> {
pub(crate) ptr: NonNull<T>,
pub(crate) handle: Weak<Cell<bool>>,
pub(crate) _marker: PhantomData<W>,
pub(crate) data: Option<D>
}
pub trait Handleable<D: Clone, T> {
#[doc(hidden)]
unsafe fn from_ptr(resource_ptr: *mut T) -> Option<Self> where Self: Sized;
#[doc(hidden)]
unsafe fn as_ptr(&self) -> *mut T;
#[doc(hidden)]
unsafe fn from_handle(&Handle<D, T, Self>) -> HandleResult<Self> where Self: Sized;
fn weak_reference(&self) -> Handle<D, T, Self> where Self: Sized;
}
impl <D: Clone, T, W: Handleable<D, T>> Clone for Handle<D, T, W> {
fn clone(&self) -> Self {
Handle { ptr: self.ptr,
handle: self.handle.clone(),
_marker: PhantomData,
data: self.data.clone()
}
}
}
impl <D: Clone, T, W: Handleable<D, T>> fmt::Debug for Handle<D, T, W> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Handle with pointer: {:p}", self.ptr.as_ptr())
}
}
impl <D: Clone, T, W: Handleable<D, T>> Hash for Handle<D, T, W> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.ptr.as_ptr().hash(state);
}
}
impl <D: Clone, T, W: Handleable<D, T>> PartialEq for Handle<D, T, W> {
fn eq(&self, other: &Handle<D, T, W>) -> bool {
self.ptr == other.ptr
}
}
impl <D: Clone, T, W: Handleable<D, T>> Eq for Handle<D, T, W> {}
impl <D: Clone, T, W: Handleable<D, T>> Default for Handle<D, T, W> {
fn default() -> Self {
Handle { ptr: NonNull::dangling(),
handle: Weak::new(),
_marker: PhantomData,
data: None }
}
}
impl <D: Clone, T, W: Handleable<D, T>> Handle<D, T, W> {
#[allow(dead_code)]
pub(crate) unsafe fn from_ptr(raw_ptr: *mut T) -> Handle<D, T, W> {
let ptr = match NonNull::new(raw_ptr) {
Some(ptr) => ptr,
None => return Self::default()
};
match W::from_ptr(ptr.as_ptr()) {
Some(wrapped_resource) => wrapped_resource.weak_reference(),
None => {
let mut handle = Self::default();
handle.ptr = ptr;
handle
}
}
}
#[doc(hidden)]
pub unsafe fn as_non_null(&self) -> NonNull<T> {
self.ptr
}
#[doc(hidden)]
pub unsafe fn as_ptr(&self) -> *mut T {
self.ptr.as_ptr()
}
pub fn run<F, R>(&self, runner: F) -> HandleResult<R>
where F: FnOnce(&mut W) -> R
{
let mut wrapped_obj = unsafe { self.upgrade()? };
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| runner(&mut wrapped_obj)));
self.handle.upgrade().map(|check| {
if !check.get() {
wlr_log!(WLR_ERROR, "After running callback, mutable lock was false");
panic!("Lock in incorrect state!");
}
check.set(false);
});
match res {
Ok(res) => Ok(res),
Err(err) => panic::resume_unwind(err)
}
}
pub fn is_alive(&self) -> bool {
self.handle.upgrade().map(|_| true).unwrap_or(false)
}
pub fn is_borrowed(&self) -> bool {
self.handle.upgrade().map(|check| check.get()).unwrap_or(false)
}
#[doc(hidden)]
pub unsafe fn upgrade(&self) -> HandleResult<W> {
self.handle.upgrade()
.ok_or(HandleErr::AlreadyDropped)
.and_then(|check| {
if check.get() {
return Err(HandleErr::AlreadyBorrowed)
}
let wrapper_obj = W::from_handle(self)?;
check.set(true);
Ok(wrapper_obj)
})
}
}
impl fmt::Display for HandleErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::HandleErr::*;
match *self {
AlreadyBorrowed => write!(f, "already borrowed"),
AlreadyDropped => write!(f, "already dropped")
}
}
}
impl Error for HandleErr {
fn description(&self) -> &str {
use self::HandleErr::*;
match *self {
AlreadyBorrowed => "Structure is already mutably borrowed",
AlreadyDropped => "Structure has already been dropped"
}
}
}