comparison rust/hg-cpython/src/ref_sharing.rs @ 42849:8db8fa1de2ef

rust-cpython: introduce restricted variant of RefCell This should catch invalid borrow_mut() calls. Still the ref-sharing interface is unsafe.
author Yuya Nishihara <yuya@tcha.org>
date Sun, 01 Sep 2019 17:37:30 +0900
parents ee0f511b7a22
children 8f549c46bc64
comparison
equal deleted inserted replaced
42846:01d3ce3281cf 42849:8db8fa1de2ef
7 7
8 //! Macros for use in the `hg-cpython` bridge library. 8 //! Macros for use in the `hg-cpython` bridge library.
9 9
10 use crate::exceptions::AlreadyBorrowed; 10 use crate::exceptions::AlreadyBorrowed;
11 use cpython::{PyResult, Python}; 11 use cpython::{PyResult, Python};
12 use std::cell::{Cell, RefCell, RefMut}; 12 use std::cell::{Cell, Ref, RefCell, RefMut};
13 13
14 /// Manages the shared state between Python and Rust 14 /// Manages the shared state between Python and Rust
15 #[derive(Default)] 15 #[derive(Default)]
16 pub struct PySharedState { 16 pub struct PySharedState {
17 leak_count: Cell<usize>, 17 leak_count: Cell<usize>,
59 /// lifetime. 59 /// lifetime.
60 /// We need to be protected by the GIL for thread-safety. 60 /// We need to be protected by the GIL for thread-safety.
61 pub fn leak_immutable<T>( 61 pub fn leak_immutable<T>(
62 &self, 62 &self,
63 py: Python, 63 py: Python,
64 data: &RefCell<T>, 64 data: &PySharedRefCell<T>,
65 ) -> PyResult<&'static T> { 65 ) -> PyResult<&'static T> {
66 if self.mutably_borrowed.get() { 66 if self.mutably_borrowed.get() {
67 return Err(AlreadyBorrowed::new( 67 return Err(AlreadyBorrowed::new(
68 py, 68 py,
69 "Cannot borrow immutably while there is a \ 69 "Cannot borrow immutably while there is a \
79 self.leak_count 79 self.leak_count
80 .replace(self.leak_count.get().saturating_sub(1)); 80 .replace(self.leak_count.get().saturating_sub(1));
81 if mutable { 81 if mutable {
82 self.mutably_borrowed.replace(false); 82 self.mutably_borrowed.replace(false);
83 } 83 }
84 }
85 }
86
87 /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`.
88 ///
89 /// Only immutable operation is allowed through this interface.
90 #[derive(Debug)]
91 pub struct PySharedRefCell<T> {
92 inner: RefCell<T>,
93 }
94
95 impl<T> PySharedRefCell<T> {
96 pub const fn new(value: T) -> PySharedRefCell<T> {
97 Self {
98 inner: RefCell::new(value),
99 }
100 }
101
102 pub fn borrow(&self) -> Ref<T> {
103 // py_shared_state isn't involved since
104 // - inner.borrow() would fail if self is mutably borrowed,
105 // - and inner.borrow_mut() would fail while self is borrowed.
106 self.inner.borrow()
107 }
108
109 pub fn as_ptr(&self) -> *mut T {
110 self.inner.as_ptr()
111 }
112
113 pub unsafe fn borrow_mut(&self) -> RefMut<T> {
114 // must be borrowed by self.py_shared_state(py).borrow_mut().
115 self.inner.borrow_mut()
84 } 116 }
85 } 117 }
86 118
87 /// Holds a mutable reference to data shared between Python and Rust. 119 /// Holds a mutable reference to data shared between Python and Rust.
88 pub struct PyRefMut<'a, T> { 120 pub struct PyRefMut<'a, T> {
156 /// struct MyStruct { 188 /// struct MyStruct {
157 /// inner: Vec<u32>; 189 /// inner: Vec<u32>;
158 /// } 190 /// }
159 /// 191 ///
160 /// py_class!(pub class MyType |py| { 192 /// py_class!(pub class MyType |py| {
161 /// data inner: RefCell<MyStruct>; 193 /// data inner: PySharedRefCell<MyStruct>;
162 /// data py_shared_state: PySharedState; 194 /// data py_shared_state: PySharedState;
163 /// }); 195 /// });
164 /// 196 ///
165 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef); 197 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
166 /// ``` 198 /// ```
175 fn borrow_mut<'a>( 207 fn borrow_mut<'a>(
176 &'a self, 208 &'a self,
177 py: Python<'a>, 209 py: Python<'a>,
178 ) -> PyResult<crate::ref_sharing::PyRefMut<'a, $inner_struct>> 210 ) -> PyResult<crate::ref_sharing::PyRefMut<'a, $inner_struct>>
179 { 211 {
212 // assert $data_member type
213 use crate::ref_sharing::PySharedRefCell;
214 let data: &PySharedRefCell<_> = self.$data_member(py);
180 self.py_shared_state(py) 215 self.py_shared_state(py)
181 .borrow_mut(py, self.$data_member(py).borrow_mut()) 216 .borrow_mut(py, unsafe { data.borrow_mut() })
182 } 217 }
183 218
184 fn leak_immutable<'a>( 219 fn leak_immutable<'a>(
185 &'a self, 220 &'a self,
186 py: Python<'a>, 221 py: Python<'a>,
187 ) -> PyResult<&'static $inner_struct> { 222 ) -> PyResult<&'static $inner_struct> {
188 self.py_shared_state(py) 223 // assert $data_member type
189 .leak_immutable(py, self.$data_member(py)) 224 use crate::ref_sharing::PySharedRefCell;
225 let data: &PySharedRefCell<_> = self.$data_member(py);
226 self.py_shared_state(py).leak_immutable(py, data)
190 } 227 }
191 } 228 }
192 229
193 /// Manage immutable references to `$name` leaked into Python 230 /// Manage immutable references to `$name` leaked into Python
194 /// iterators. 231 /// iterators.
293 /// struct MyStruct { 330 /// struct MyStruct {
294 /// inner: HashMap<Vec<u8>, Vec<u8>>; 331 /// inner: HashMap<Vec<u8>, Vec<u8>>;
295 /// } 332 /// }
296 /// 333 ///
297 /// py_class!(pub class MyType |py| { 334 /// py_class!(pub class MyType |py| {
298 /// data inner: RefCell<MyStruct>; 335 /// data inner: PySharedRefCell<MyStruct>;
299 /// data py_shared_state: PySharedState; 336 /// data py_shared_state: PySharedState;
300 /// 337 ///
301 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> { 338 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
302 /// MyTypeItemsIterator::create_instance( 339 /// MyTypeItemsIterator::create_instance(
303 /// py, 340 /// py,