use super::{PyGraphRecord, PyGraphRecordInner};
use graphrecords_core::{GraphRecord, errors::GraphRecordResult};
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use pyo3::{Py, Python};
use std::{
fmt::{Debug, Formatter, Result},
ptr::NonNull,
};
pub(super) struct BorrowedGraphRecord {
ptr: RwLock<Option<NonNull<GraphRecord>>>,
mutable: bool,
}
unsafe impl Send for BorrowedGraphRecord {}
unsafe impl Sync for BorrowedGraphRecord {}
impl Debug for BorrowedGraphRecord {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("BorrowedGraphRecord")
.field("alive", &self.ptr.read().is_some())
.field("mutable", &self.mutable)
.finish()
}
}
impl BorrowedGraphRecord {
pub(super) const fn dead() -> Self {
Self {
ptr: RwLock::new(None),
mutable: false,
}
}
pub(super) const fn is_mutable(&self) -> bool {
self.mutable
}
pub(super) fn read(&self) -> RwLockReadGuard<'_, Option<NonNull<GraphRecord>>> {
self.ptr.read()
}
pub(super) fn write(&self) -> RwLockWriteGuard<'_, Option<NonNull<GraphRecord>>> {
self.ptr.write()
}
}
macro_rules! impl_scope {
($(#[$meta:meta])* $name:ident, $ref_type:ty, $mutable:expr) => {
$(#[$meta])*
pub fn $name<R>(
py: Python<'_>,
graphrecord: $ref_type,
function: impl FnOnce(Python<'_>, &Py<Self>) -> GraphRecordResult<R>,
) -> GraphRecordResult<R> {
struct PanicOnDrop(bool);
impl Drop for PanicOnDrop {
fn drop(&mut self) {
assert!(!self.0, "failed to clear PyGraphRecord borrow");
}
}
struct Guard<'py>(Python<'py>, Py<PyGraphRecord>, NonNull<GraphRecord>);
impl Drop for Guard<'_> {
#[allow(clippy::significant_drop_tightening)]
fn drop(&mut self) {
let panic_on_drop = PanicOnDrop(true);
let py_graphrecord = self.1.bind(self.0).get();
match &py_graphrecord.inner {
PyGraphRecordInner::Borrowed(borrowed) => {
let mut guard = borrowed.write();
assert_eq!(
guard.take(),
Some(self.2),
"PyGraphRecord was tampered with"
);
}
PyGraphRecordInner::Owned(_)
| PyGraphRecordInner::Connected(_) => {
panic!("PyGraphRecord was replaced with a non-borrowed variant");
}
}
std::mem::forget(panic_on_drop);
}
}
let pointer = NonNull::from(graphrecord);
let guard = Guard(
py,
Py::new(
py,
Self {
inner: PyGraphRecordInner::Borrowed(BorrowedGraphRecord {
ptr: RwLock::new(Some(pointer)),
mutable: $mutable,
}),
},
)
.expect("PyGraphRecord must be creatable"),
pointer,
);
function(py, &guard.1)
}
};
}
impl PyGraphRecord {
impl_scope!(
scope, &GraphRecord, false
);
impl_scope!(
scope_mut, &mut GraphRecord, true
);
}