Mercurial > public > mercurial-scm > hg
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, |