graphrecords_python/graphrecord/
borrowed.rs1use super::{PyGraphRecord, PyGraphRecordInner};
2use graphrecords_core::{
3 PluginGraphRecord,
4 errors::{GraphRecordError, GraphRecordResult},
5};
6use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
7use pyo3::{Py, Python};
8use std::{
9 fmt::{Debug, Formatter, Result},
10 ptr::NonNull,
11};
12
13pub(super) struct BorrowedGraphRecord(RwLock<Option<NonNull<PluginGraphRecord>>>);
25
26unsafe impl Send for BorrowedGraphRecord {}
31unsafe impl Sync for BorrowedGraphRecord {}
32
33impl Debug for BorrowedGraphRecord {
34 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
35 f.debug_struct("BorrowedGraphRecord")
36 .field("alive", &self.0.read().is_some())
37 .finish()
38 }
39}
40
41impl BorrowedGraphRecord {
42 pub(super) const fn dead() -> Self {
44 Self(RwLock::new(None))
45 }
46
47 pub(super) fn read(&self) -> RwLockReadGuard<'_, Option<NonNull<PluginGraphRecord>>> {
48 self.0.read()
49 }
50
51 pub(super) fn write(&self) -> RwLockWriteGuard<'_, Option<NonNull<PluginGraphRecord>>> {
52 self.0.write()
53 }
54}
55
56impl PyGraphRecord {
57 pub fn scope<R>(
66 py: Python<'_>,
67 graphrecord: &mut PluginGraphRecord,
68 function: impl FnOnce(Python<'_>, &Py<Self>) -> GraphRecordResult<R>,
69 ) -> GraphRecordResult<R> {
70 struct PanicOnDrop(bool);
71 impl Drop for PanicOnDrop {
72 fn drop(&mut self) {
73 assert!(!self.0, "failed to clear PyGraphRecord borrow");
74 }
75 }
76
77 struct Guard<'py>(Python<'py>, Py<PyGraphRecord>, NonNull<PluginGraphRecord>);
78 impl Drop for Guard<'_> {
79 #[allow(clippy::significant_drop_tightening)]
80 fn drop(&mut self) {
81 let panic_on_drop = PanicOnDrop(true);
82 let py_graphrecord = self.1.bind(self.0).get();
83 match &py_graphrecord.inner {
84 PyGraphRecordInner::Borrowed(borrowed) => {
85 let mut guard = borrowed.write();
86 assert_eq!(
87 guard.take(),
88 Some(self.2),
89 "PyGraphRecord was tampered with"
90 );
91 }
92 PyGraphRecordInner::Owned(_) => {
93 panic!("PyGraphRecord was replaced with an owned variant");
94 }
95 }
96 std::mem::forget(panic_on_drop);
97 }
98 }
99
100 let pointer = NonNull::from(graphrecord);
101 let guard = Guard(
102 py,
103 Py::new(
104 py,
105 Self {
106 inner: PyGraphRecordInner::Borrowed(BorrowedGraphRecord(RwLock::new(Some(
107 pointer,
108 )))),
109 },
110 )
111 .map_err(|error| {
112 GraphRecordError::ConversionError(format!(
113 "Failed to create PyGraphRecord: {error}"
114 ))
115 })?,
116 pointer,
117 );
118 function(py, &guard.1)
119 }
120}