Mercurial > hg
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(); |