changeset 43965:1b2200bd06b6

rust-cpython: add safe wrapper representing shared data borrowed from PyObject PySharedRef is a tempoary wrapper around PySharedRefCell. It provides safe functions for each shared data. $shared_accessor implements a safe method to construct PySharedRefCell. This allows us to add more than once PySharedRefCell to a Python object.
author Yuya Nishihara <yuya@tcha.org>
date Sun, 15 Sep 2019 16:50:48 +0900
parents 5cb8867c9e2b
children 7d6758f2d50c
files rust/hg-cpython/src/dirstate/dirs_multiset.rs rust/hg-cpython/src/dirstate/dirstate_map.rs rust/hg-cpython/src/ref_sharing.rs
diffstat 3 files changed, 80 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/rust/hg-cpython/src/dirstate/dirs_multiset.rs
+++ b/rust/hg-cpython/src/dirstate/dirs_multiset.rs
@@ -107,7 +107,7 @@
     }
 });
 
-py_shared_ref!(Dirs, DirsMultiset, inner);
+py_shared_ref!(Dirs, DirsMultiset, inner, inner_shared);
 
 impl Dirs {
     pub fn from_inner(py: Python, d: DirsMultiset) -> PyResult<Self> {
--- a/rust/hg-cpython/src/dirstate/dirstate_map.rs
+++ b/rust/hg-cpython/src/dirstate/dirstate_map.rs
@@ -501,7 +501,7 @@
     }
 }
 
-py_shared_ref!(DirstateMap, RustDirstateMap, inner);
+py_shared_ref!(DirstateMap, RustDirstateMap, inner, inner_shared);
 
 py_shared_iterator!(
     DirstateMapKeysIterator,
--- a/rust/hg-cpython/src/ref_sharing.rs
+++ b/rust/hg-cpython/src/ref_sharing.rs
@@ -125,7 +125,7 @@
 #[derive(Debug)]
 pub struct PySharedRefCell<T> {
     inner: RefCell<T>,
-    pub py_shared_state: PySharedState, // TODO: remove pub
+    py_shared_state: PySharedState,
 }
 
 impl<T> PySharedRefCell<T> {
@@ -159,6 +159,54 @@
     }
 }
 
+/// Sharable data member of type `T` borrowed from the `PyObject`.
+pub struct PySharedRef<'a, T> {
+    py: Python<'a>,
+    owner: &'a PyObject,
+    data: &'a PySharedRefCell<T>,
+}
+
+impl<'a, T> PySharedRef<'a, T> {
+    /// # Safety
+    ///
+    /// The `data` must be owned by the `owner`. Otherwise, the leak count
+    /// would get wrong.
+    pub unsafe fn new(
+        py: Python<'a>,
+        owner: &'a PyObject,
+        data: &'a PySharedRefCell<T>,
+    ) -> Self {
+        Self { py, owner, data }
+    }
+
+    pub fn borrow(&self) -> Ref<T> {
+        self.data.borrow()
+    }
+
+    pub fn borrow_mut(&self) -> PyResult<PyRefMut<'a, T>> {
+        self.data.borrow_mut(self.py)
+    }
+
+    /// Returns a leaked reference and its management object.
+    ///
+    /// # Safety
+    ///
+    /// It's up to you to make sure that the management object lives
+    /// longer than the leaked reference. Otherwise, you'll get a
+    /// dangling reference.
+    pub unsafe fn leak_immutable(
+        &self,
+    ) -> PyResult<(PyLeakedRef, &'static T)> {
+        let (static_ref, static_state_ref) = self
+            .data
+            .py_shared_state
+            .leak_immutable(self.py, self.data)?;
+        let leak_handle =
+            PyLeakedRef::new(self.py, self.owner, static_state_ref);
+        Ok((leak_handle, static_ref))
+    }
+}
+
 /// Holds a mutable reference to data shared between Python and Rust.
 pub struct PyRefMut<'a, T> {
     inner: RefMut<'a, T>,
@@ -219,6 +267,13 @@
 /// * `$inner_struct` is the identifier of the underlying Rust struct
 /// * `$data_member` is the identifier of the data member of `$inner_struct`
 /// that will be shared.
+/// * `$shared_accessor` is the function name to be generated, which allows
+/// safe access to the data member.
+///
+/// # Safety
+///
+/// `$data_member` must persist while the `$name` object is alive. In other
+/// words, it must be an accessor to a data field of the Python object.
 ///
 /// # Example
 ///
@@ -231,47 +286,46 @@
 ///     data inner: PySharedRefCell<MyStruct>;
 /// });
 ///
-/// py_shared_ref!(MyType, MyStruct, inner);
+/// py_shared_ref!(MyType, MyStruct, inner, inner_shared);
 /// ```
 macro_rules! py_shared_ref {
     (
         $name: ident,
         $inner_struct: ident,
-        $data_member: ident
+        $data_member: ident,
+        $shared_accessor: ident
     ) => {
         impl $name {
-            // TODO: remove this function in favor of inner(py).borrow_mut()
+            /// Returns a safe reference to the shared `$data_member`.
+            ///
+            /// This function guarantees that `PySharedRef` is created with
+            /// the valid `self` and `self.$data_member(py)` pair.
+            fn $shared_accessor<'a>(
+                &'a self,
+                py: Python<'a>,
+            ) -> $crate::ref_sharing::PySharedRef<'a, $inner_struct> {
+                use cpython::PythonObject;
+                use $crate::ref_sharing::PySharedRef;
+                let owner = self.as_object();
+                let data = self.$data_member(py);
+                unsafe { PySharedRef::new(py, owner, data) }
+            }
+
+            // TODO: remove this function in favor of $shared_accessor(py)
             fn borrow_mut<'a>(
                 &'a self,
                 py: Python<'a>,
             ) -> PyResult<crate::ref_sharing::PyRefMut<'a, $inner_struct>>
             {
-                // assert $data_member type
-                use crate::ref_sharing::PySharedRefCell;
-                let data: &PySharedRefCell<_> = self.$data_member(py);
-                data.borrow_mut(py)
+                self.$shared_accessor(py).borrow_mut()
             }
 
-            /// Returns a leaked reference and its management object.
-            ///
-            /// # Safety
-            ///
-            /// It's up to you to make sure that the management object lives
-            /// longer than the leaked reference. Otherwise, you'll get a
-            /// dangling reference.
+            // TODO: remove this function in favor of $shared_accessor(py)
             unsafe fn leak_immutable<'a>(
                 &'a self,
                 py: Python<'a>,
             ) -> PyResult<(PyLeakedRef, &'static $inner_struct)> {
-                use cpython::PythonObject;
-                // assert $data_member type
-                use crate::ref_sharing::PySharedRefCell;
-                let data: &PySharedRefCell<_> = self.$data_member(py);
-                let (static_ref, static_state_ref) =
-                    data.py_shared_state.leak_immutable(py, data)?;
-                let leak_handle =
-                    PyLeakedRef::new(py, self.as_object(), static_state_ref);
-                Ok((leak_handle, static_ref))
+                self.$shared_accessor(py).leak_immutable()
             }
         }
     };