comparison rust/hg-cpython/src/ref_sharing.rs @ 43177:5cb8867c9e2b

rust-cpython: move $leaked struct out of macro It wasn't easy to hack the $leaked struct since errors in macro would generate lots of compile errors. Let's make it a plain struct so we can easily extend it. PyLeakedRef keeps a more generic PyObject instead of the $name struct since it no longer has to call any specific methods implemented by the $name class. $leaked parameter in py_shared_iterator!() is kept for future change.
author Yuya Nishihara <yuya@tcha.org>
date Sun, 08 Sep 2019 20:26:55 +0900
parents aaec70a5f9a8
children 1b2200bd06b6
comparison
equal deleted inserted replaced
43176:aaec70a5f9a8 43177:5cb8867c9e2b
21 // IN THE SOFTWARE. 21 // IN THE SOFTWARE.
22 22
23 //! Macros for use in the `hg-cpython` bridge library. 23 //! Macros for use in the `hg-cpython` bridge library.
24 24
25 use crate::exceptions::AlreadyBorrowed; 25 use crate::exceptions::AlreadyBorrowed;
26 use cpython::{PyResult, Python}; 26 use cpython::{PyClone, PyObject, PyResult, Python};
27 use std::cell::{Cell, Ref, RefCell, RefMut}; 27 use std::cell::{Cell, Ref, RefCell, RefMut};
28 28
29 /// Manages the shared state between Python and Rust 29 /// Manages the shared state between Python and Rust
30 #[derive(Debug, Default)] 30 #[derive(Debug, Default)]
31 pub struct PySharedState { 31 pub struct PySharedState {
217 /// 217 ///
218 /// * `$name` is the same identifier used in for `py_class!` macro call. 218 /// * `$name` is the same identifier used in for `py_class!` macro call.
219 /// * `$inner_struct` is the identifier of the underlying Rust struct 219 /// * `$inner_struct` is the identifier of the underlying Rust struct
220 /// * `$data_member` is the identifier of the data member of `$inner_struct` 220 /// * `$data_member` is the identifier of the data member of `$inner_struct`
221 /// that will be shared. 221 /// that will be shared.
222 /// * `$leaked` is the identifier to give to the struct that will manage
223 /// references to `$name`, to be used for example in other macros like
224 /// `py_shared_iterator`.
225 /// 222 ///
226 /// # Example 223 /// # Example
227 /// 224 ///
228 /// ``` 225 /// ```
229 /// struct MyStruct { 226 /// struct MyStruct {
232 /// 229 ///
233 /// py_class!(pub class MyType |py| { 230 /// py_class!(pub class MyType |py| {
234 /// data inner: PySharedRefCell<MyStruct>; 231 /// data inner: PySharedRefCell<MyStruct>;
235 /// }); 232 /// });
236 /// 233 ///
237 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef); 234 /// py_shared_ref!(MyType, MyStruct, inner);
238 /// ``` 235 /// ```
239 macro_rules! py_shared_ref { 236 macro_rules! py_shared_ref {
240 ( 237 (
241 $name: ident, 238 $name: ident,
242 $inner_struct: ident, 239 $inner_struct: ident,
243 $data_member: ident, 240 $data_member: ident
244 $leaked: ident,
245 ) => { 241 ) => {
246 impl $name { 242 impl $name {
247 // TODO: remove this function in favor of inner(py).borrow_mut() 243 // TODO: remove this function in favor of inner(py).borrow_mut()
248 fn borrow_mut<'a>( 244 fn borrow_mut<'a>(
249 &'a self, 245 &'a self,
264 /// longer than the leaked reference. Otherwise, you'll get a 260 /// longer than the leaked reference. Otherwise, you'll get a
265 /// dangling reference. 261 /// dangling reference.
266 unsafe fn leak_immutable<'a>( 262 unsafe fn leak_immutable<'a>(
267 &'a self, 263 &'a self,
268 py: Python<'a>, 264 py: Python<'a>,
269 ) -> PyResult<($leaked, &'static $inner_struct)> { 265 ) -> PyResult<(PyLeakedRef, &'static $inner_struct)> {
266 use cpython::PythonObject;
270 // assert $data_member type 267 // assert $data_member type
271 use crate::ref_sharing::PySharedRefCell; 268 use crate::ref_sharing::PySharedRefCell;
272 let data: &PySharedRefCell<_> = self.$data_member(py); 269 let data: &PySharedRefCell<_> = self.$data_member(py);
273 let (static_ref, static_state_ref) = 270 let (static_ref, static_state_ref) =
274 data.py_shared_state.leak_immutable(py, data)?; 271 data.py_shared_state.leak_immutable(py, data)?;
275 let leak_handle = $leaked::new(py, self, static_state_ref); 272 let leak_handle =
273 PyLeakedRef::new(py, self.as_object(), static_state_ref);
276 Ok((leak_handle, static_ref)) 274 Ok((leak_handle, static_ref))
277 } 275 }
278 } 276 }
279
280 /// Manage immutable references to `$name` leaked into Python
281 /// iterators.
282 ///
283 /// In truth, this does not represent leaked references themselves;
284 /// it is instead useful alongside them to manage them.
285 pub struct $leaked {
286 _inner: $name,
287 py_shared_state: &'static crate::ref_sharing::PySharedState,
288 }
289
290 impl $leaked {
291 /// # Safety
292 ///
293 /// The `py_shared_state` must be owned by the `inner` Python
294 /// object.
295 // Marked as unsafe so client code wouldn't construct $leaked
296 // struct by mistake. Its drop() is unsafe.
297 unsafe fn new(
298 py: Python,
299 inner: &$name,
300 py_shared_state: &'static crate::ref_sharing::PySharedState,
301 ) -> Self {
302 Self {
303 _inner: inner.clone_ref(py),
304 py_shared_state,
305 }
306 }
307 }
308
309 impl Drop for $leaked {
310 fn drop(&mut self) {
311 // py_shared_state should be alive since we do have
312 // a Python reference to the owner object. Taking GIL makes
313 // sure that the state is only accessed by this thread.
314 let gil = Python::acquire_gil();
315 let py = gil.python();
316 unsafe {
317 self.py_shared_state.decrease_leak_count(py, false);
318 }
319 }
320 }
321 }; 277 };
278 }
279
280 /// Manage immutable references to `PyObject` leaked into Python iterators.
281 ///
282 /// In truth, this does not represent leaked references themselves;
283 /// it is instead useful alongside them to manage them.
284 pub struct PyLeakedRef {
285 _inner: PyObject,
286 py_shared_state: &'static PySharedState,
287 }
288
289 impl PyLeakedRef {
290 /// # Safety
291 ///
292 /// The `py_shared_state` must be owned by the `inner` Python object.
293 // Marked as unsafe so client code wouldn't construct PyLeakedRef
294 // struct by mistake. Its drop() is unsafe.
295 pub unsafe fn new(
296 py: Python,
297 inner: &PyObject,
298 py_shared_state: &'static PySharedState,
299 ) -> Self {
300 Self {
301 _inner: inner.clone_ref(py),
302 py_shared_state,
303 }
304 }
305 }
306
307 impl Drop for PyLeakedRef {
308 fn drop(&mut self) {
309 // py_shared_state should be alive since we do have
310 // a Python reference to the owner object. Taking GIL makes
311 // sure that the state is only accessed by this thread.
312 let gil = Python::acquire_gil();
313 let py = gil.python();
314 unsafe {
315 self.py_shared_state.decrease_leak_count(py, false);
316 }
317 }
322 } 318 }
323 319
324 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator. 320 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
325 /// 321 ///
326 /// TODO: this is a bit awkward to use, and a better (more complicated) 322 /// TODO: this is a bit awkward to use, and a better (more complicated)
370 /// 366 ///
371 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef); 367 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
372 /// 368 ///
373 /// py_shared_iterator!( 369 /// py_shared_iterator!(
374 /// MyTypeItemsIterator, 370 /// MyTypeItemsIterator,
375 /// MyTypeLeakedRef, 371 /// PyLeakedRef,
376 /// HashMap<'static, Vec<u8>, Vec<u8>>, 372 /// HashMap<'static, Vec<u8>, Vec<u8>>,
377 /// MyType::translate_key_value, 373 /// MyType::translate_key_value,
378 /// Option<(PyBytes, PyBytes)> 374 /// Option<(PyBytes, PyBytes)>
379 /// ); 375 /// );
380 /// ``` 376 /// ```
381 macro_rules! py_shared_iterator { 377 macro_rules! py_shared_iterator {
382 ( 378 (
383 $name: ident, 379 $name: ident,
384 $leaked: ident, 380 $leaked: ty,
385 $iterator_type: ty, 381 $iterator_type: ty,
386 $success_func: expr, 382 $success_func: expr,
387 $success_type: ty 383 $success_type: ty
388 ) => { 384 ) => {
389 py_class!(pub class $name |py| { 385 py_class!(pub class $name |py| {