comparison rust/hg-cpython/src/ref_sharing.rs @ 43423:945d4dba5e78

rust-cpython: add stub wrapper that'll prevent leaked data from being mutated In order to allow mutation of PySharedRefCell value while PyLeaked reference exists, we need yet another "borrow" scope where mutation is prohibited. try_borrow<'a> and try_borrow_mut<'a> defines the "borrow" scope <'a>. The subsequent patches will implement leak counter based on this scope. PyLeakedRef<T> and PyLeakedRefMut<T> could be unified to PyLeakedRef<&T> and PyLeakedRef<&mut T> respectively, but I didn't do that since it seemed a bit weird that deref_mut() would return a mutable reference to an immutable reference.
author Yuya Nishihara <yuya@tcha.org>
date Sat, 12 Oct 2019 19:26:23 +0900
parents b9f791090211
children 0836efe4967b
comparison
equal deleted inserted replaced
43422:b9f791090211 43423:945d4dba5e78
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::{PyClone, PyObject, 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 use std::ops::{Deref, DerefMut};
28 29
29 /// Manages the shared state between Python and Rust 30 /// Manages the shared state between Python and Rust
30 #[derive(Debug, Default)] 31 #[derive(Debug, Default)]
31 struct PySharedState { 32 struct PySharedState {
32 leak_count: Cell<usize>, 33 leak_count: Cell<usize>,
331 data: Some(data), 332 data: Some(data),
332 py_shared_state, 333 py_shared_state,
333 } 334 }
334 } 335 }
335 336
336 /// Returns an immutable reference to the inner value. 337 /// Immutably borrows the wrapped value.
337 pub fn get_ref<'a>(&'a self, _py: Python<'a>) -> &'a T { 338 pub fn try_borrow<'a>(
338 self.data.as_ref().unwrap() 339 &'a self,
339 } 340 py: Python<'a>,
340 341 ) -> PyResult<PyLeakedRef<'a, T>> {
341 /// Returns a mutable reference to the inner value. 342 Ok(PyLeakedRef {
343 _py: py,
344 data: self.data.as_ref().unwrap(),
345 })
346 }
347
348 /// Mutably borrows the wrapped value.
342 /// 349 ///
343 /// Typically `T` is an iterator. If `T` is an immutable reference, 350 /// Typically `T` is an iterator. If `T` is an immutable reference,
344 /// `get_mut()` is useless since the inner value can't be mutated. 351 /// `get_mut()` is useless since the inner value can't be mutated.
345 pub fn get_mut<'a>(&'a mut self, _py: Python<'a>) -> &'a mut T { 352 pub fn try_borrow_mut<'a>(
346 self.data.as_mut().unwrap() 353 &'a mut self,
354 py: Python<'a>,
355 ) -> PyResult<PyLeakedRefMut<'a, T>> {
356 Ok(PyLeakedRefMut {
357 _py: py,
358 data: self.data.as_mut().unwrap(),
359 })
347 } 360 }
348 361
349 /// Converts the inner value by the given function. 362 /// Converts the inner value by the given function.
350 /// 363 ///
351 /// Typically `T` is a static reference to a container, and `U` is an 364 /// Typically `T` is a static reference to a container, and `U` is an
387 } 400 }
388 self.py_shared_state.decrease_leak_count(py, false); 401 self.py_shared_state.decrease_leak_count(py, false);
389 } 402 }
390 } 403 }
391 404
405 /// Immutably borrowed reference to a leaked value.
406 pub struct PyLeakedRef<'a, T> {
407 _py: Python<'a>,
408 data: &'a T,
409 }
410
411 impl<T> Deref for PyLeakedRef<'_, T> {
412 type Target = T;
413
414 fn deref(&self) -> &T {
415 self.data
416 }
417 }
418
419 /// Mutably borrowed reference to a leaked value.
420 pub struct PyLeakedRefMut<'a, T> {
421 _py: Python<'a>,
422 data: &'a mut T,
423 }
424
425 impl<T> Deref for PyLeakedRefMut<'_, T> {
426 type Target = T;
427
428 fn deref(&self) -> &T {
429 self.data
430 }
431 }
432
433 impl<T> DerefMut for PyLeakedRefMut<'_, T> {
434 fn deref_mut(&mut self) -> &mut T {
435 self.data
436 }
437 }
438
392 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator. 439 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
393 /// 440 ///
394 /// TODO: this is a bit awkward to use, and a better (more complicated) 441 /// TODO: this is a bit awkward to use, and a better (more complicated)
395 /// procedural macro would simplify the interface a lot. 442 /// procedural macro would simplify the interface a lot.
396 /// 443 ///
455 data inner: RefCell<Option<$leaked>>; 502 data inner: RefCell<Option<$leaked>>;
456 503
457 def __next__(&self) -> PyResult<$success_type> { 504 def __next__(&self) -> PyResult<$success_type> {
458 let mut inner_opt = self.inner(py).borrow_mut(); 505 let mut inner_opt = self.inner(py).borrow_mut();
459 if let Some(leaked) = inner_opt.as_mut() { 506 if let Some(leaked) = inner_opt.as_mut() {
460 match leaked.get_mut(py).next() { 507 let mut iter = leaked.try_borrow_mut(py)?;
508 match iter.next() {
461 None => { 509 None => {
462 // replace Some(inner) by None, drop $leaked 510 // replace Some(inner) by None, drop $leaked
463 inner_opt.take(); 511 inner_opt.take();
464 Ok(None) 512 Ok(None)
465 } 513 }
510 .unwrap(); 558 .unwrap();
511 (gil, owner) 559 (gil, owner)
512 } 560 }
513 561
514 #[test] 562 #[test]
563 fn test_leaked_borrow() {
564 let (gil, owner) = prepare_env();
565 let py = gil.python();
566 let leaked = owner.string_shared(py).leak_immutable().unwrap();
567 let leaked_ref = leaked.try_borrow(py).unwrap();
568 assert_eq!(*leaked_ref, "new");
569 }
570
571 #[test]
572 fn test_leaked_borrow_mut() {
573 let (gil, owner) = prepare_env();
574 let py = gil.python();
575 let leaked = owner.string_shared(py).leak_immutable().unwrap();
576 let mut leaked_iter = unsafe { leaked.map(py, |s| s.chars()) };
577 let mut leaked_ref = leaked_iter.try_borrow_mut(py).unwrap();
578 assert_eq!(leaked_ref.next(), Some('n'));
579 assert_eq!(leaked_ref.next(), Some('e'));
580 assert_eq!(leaked_ref.next(), Some('w'));
581 assert_eq!(leaked_ref.next(), None);
582 }
583
584 #[test]
515 fn test_borrow_mut_while_leaked() { 585 fn test_borrow_mut_while_leaked() {
516 let (gil, owner) = prepare_env(); 586 let (gil, owner) = prepare_env();
517 let py = gil.python(); 587 let py = gil.python();
518 assert!(owner.string_shared(py).borrow_mut().is_ok()); 588 assert!(owner.string_shared(py).borrow_mut().is_ok());
519 let _leaked = owner.string_shared(py).leak_immutable().unwrap(); 589 let _leaked = owner.string_shared(py).leak_immutable().unwrap();