ref_sharing.rs
636 lines
| 20.5 KiB
| application/rls-services+xml
|
RustLexer
Yuya Nishihara
|
r43352 | // ref_sharing.rs | ||
Raphaël Gomès
|
r42997 | // | ||
// Copyright 2019 Raphaël Gomès <rgomes@octobus.net> | ||||
// | ||||
Yuya Nishihara
|
r43352 | // Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// of this software and associated documentation files (the "Software"), to | ||||
// deal in the Software without restriction, including without limitation the | ||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
// sell copies of the Software, and to permit persons to whom the Software is | ||||
// furnished to do so, subject to the following conditions: | ||||
// | ||||
// The above copyright notice and this permission notice shall be included in | ||||
// all copies or substantial portions of the Software. | ||||
// | ||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
// IN THE SOFTWARE. | ||||
Raphaël Gomès
|
r42997 | |||
//! Macros for use in the `hg-cpython` bridge library. | ||||
use crate::exceptions::AlreadyBorrowed; | ||||
Yuya Nishihara
|
r43605 | use cpython::{exc, PyClone, PyErr, PyObject, PyResult, Python}; | ||
Yuya Nishihara
|
r43609 | use std::cell::{Ref, RefCell, RefMut}; | ||
Yuya Nishihara
|
r43604 | use std::ops::{Deref, DerefMut}; | ||
Yuya Nishihara
|
r43605 | use std::sync::atomic::{AtomicUsize, Ordering}; | ||
Raphaël Gomès
|
r42997 | |||
/// Manages the shared state between Python and Rust | ||||
Yuya Nishihara
|
r43605 | /// | ||
/// `PySharedState` is owned by `PySharedRefCell`, and is shared across its | ||||
/// derived references. The consistency of these references are guaranteed | ||||
/// as follows: | ||||
/// | ||||
/// - The immutability of `py_class!` object fields. Any mutation of | ||||
/// `PySharedRefCell` is allowed only through its `borrow_mut()`. | ||||
/// - The `py: Python<'_>` token, which makes sure that any data access is | ||||
/// synchronized by the GIL. | ||||
Yuya Nishihara
|
r43608 | /// - The underlying `RefCell`, which prevents `PySharedRefCell` data from | ||
/// being directly borrowed or leaked while it is mutably borrowed. | ||||
Yuya Nishihara
|
r43606 | /// - The `borrow_count`, which is the number of references borrowed from | ||
/// `PyLeaked`. Just like `RefCell`, mutation is prohibited while `PyLeaked` | ||||
/// is borrowed. | ||||
Yuya Nishihara
|
r43605 | /// - The `generation` counter, which increments on `borrow_mut()`. `PyLeaked` | ||
/// reference is valid only if the `current_generation()` equals to the | ||||
/// `generation` at the time of `leak_immutable()`. | ||||
Yuya Nishihara
|
r43443 | #[derive(Debug, Default)] | ||
Yuya Nishihara
|
r43582 | struct PySharedState { | ||
Yuya Nishihara
|
r43605 | // The counter variable could be Cell<usize> since any operation on | ||
// PySharedState is synchronized by the GIL, but being "atomic" makes | ||||
// PySharedState inherently Sync. The ordering requirement doesn't | ||||
// matter thanks to the GIL. | ||||
Yuya Nishihara
|
r43606 | borrow_count: AtomicUsize, | ||
Yuya Nishihara
|
r43605 | generation: AtomicUsize, | ||
Raphaël Gomès
|
r42997 | } | ||
impl PySharedState { | ||||
Yuya Nishihara
|
r43582 | fn borrow_mut<'a, T>( | ||
Raphaël Gomès
|
r42997 | &'a self, | ||
py: Python<'a>, | ||||
pyrefmut: RefMut<'a, T>, | ||||
Yuya Nishihara
|
r43610 | ) -> PyResult<RefMut<'a, T>> { | ||
Yuya Nishihara
|
r43606 | match self.current_borrow_count(py) { | ||
Raphaël Gomès
|
r42997 | 0 => { | ||
Yuya Nishihara
|
r43605 | // Note that this wraps around to the same value if mutably | ||
// borrowed more than usize::MAX times, which wouldn't happen | ||||
// in practice. | ||||
self.generation.fetch_add(1, Ordering::Relaxed); | ||||
Yuya Nishihara
|
r43610 | Ok(pyrefmut) | ||
Raphaël Gomès
|
r42997 | } | ||
_ => Err(AlreadyBorrowed::new( | ||||
py, | ||||
Yuya Nishihara
|
r43606 | "Cannot borrow mutably while immutably borrowed", | ||
Raphaël Gomès
|
r42997 | )), | ||
} | ||||
} | ||||
Yuya Nishihara
|
r43446 | /// Return a reference to the wrapped data and its state with an | ||
/// artificial static lifetime. | ||||
Raphaël Gomès
|
r42997 | /// We need to be protected by the GIL for thread-safety. | ||
Yuya Nishihara
|
r43117 | /// | ||
/// # Safety | ||||
/// | ||||
/// This is highly unsafe since the lifetime of the given data can be | ||||
/// extended. Do not call this function directly. | ||||
Yuya Nishihara
|
r43582 | unsafe fn leak_immutable<T>( | ||
Raphaël Gomès
|
r42997 | &self, | ||
Yuya Nishihara
|
r43609 | _py: Python, | ||
Yuya Nishihara
|
r43608 | data: Ref<T>, | ||
Yuya Nishihara
|
r43611 | ) -> (&'static T, &'static PySharedState) { | ||
Yuya Nishihara
|
r43608 | let ptr: *const T = &*data; | ||
let state_ptr: *const PySharedState = self; | ||||
Yuya Nishihara
|
r43611 | (&*ptr, &*state_ptr) | ||
Raphaël Gomès
|
r42997 | } | ||
Yuya Nishihara
|
r43606 | fn current_borrow_count(&self, _py: Python) -> usize { | ||
self.borrow_count.load(Ordering::Relaxed) | ||||
} | ||||
fn increase_borrow_count(&self, _py: Python) { | ||||
// Note that this wraps around if there are more than usize::MAX | ||||
// borrowed references, which shouldn't happen due to memory limit. | ||||
self.borrow_count.fetch_add(1, Ordering::Relaxed); | ||||
} | ||||
fn decrease_borrow_count(&self, _py: Python) { | ||||
let prev_count = self.borrow_count.fetch_sub(1, Ordering::Relaxed); | ||||
assert!(prev_count > 0); | ||||
} | ||||
Yuya Nishihara
|
r43605 | fn current_generation(&self, _py: Python) -> usize { | ||
self.generation.load(Ordering::Relaxed) | ||||
} | ||||
Raphaël Gomès
|
r42997 | } | ||
Yuya Nishihara
|
r43606 | /// Helper to keep the borrow count updated while the shared object is | ||
/// immutably borrowed without using the `RefCell` interface. | ||||
struct BorrowPyShared<'a> { | ||||
py: Python<'a>, | ||||
py_shared_state: &'a PySharedState, | ||||
} | ||||
impl<'a> BorrowPyShared<'a> { | ||||
fn new( | ||||
py: Python<'a>, | ||||
py_shared_state: &'a PySharedState, | ||||
) -> BorrowPyShared<'a> { | ||||
py_shared_state.increase_borrow_count(py); | ||||
BorrowPyShared { | ||||
py, | ||||
py_shared_state, | ||||
} | ||||
} | ||||
} | ||||
impl Drop for BorrowPyShared<'_> { | ||||
fn drop(&mut self) { | ||||
self.py_shared_state.decrease_borrow_count(self.py); | ||||
} | ||||
} | ||||
Yuya Nishihara
|
r43115 | /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`. | ||
/// | ||||
Yuya Nishihara
|
r43582 | /// This object can be stored in a `py_class!` object as a data field. Any | ||
/// operation is allowed through the `PySharedRef` interface. | ||||
Yuya Nishihara
|
r43115 | #[derive(Debug)] | ||
pub struct PySharedRefCell<T> { | ||||
inner: RefCell<T>, | ||||
Yuya Nishihara
|
r43448 | py_shared_state: PySharedState, | ||
Yuya Nishihara
|
r43115 | } | ||
impl<T> PySharedRefCell<T> { | ||||
Yuya Nishihara
|
r43443 | pub fn new(value: T) -> PySharedRefCell<T> { | ||
Yuya Nishihara
|
r43115 | Self { | ||
inner: RefCell::new(value), | ||||
Yuya Nishihara
|
r43443 | py_shared_state: PySharedState::default(), | ||
Yuya Nishihara
|
r43115 | } | ||
} | ||||
Yuya Nishihara
|
r43582 | fn borrow<'a>(&'a self, _py: Python<'a>) -> Ref<'a, T> { | ||
Yuya Nishihara
|
r43115 | // py_shared_state isn't involved since | ||
// - inner.borrow() would fail if self is mutably borrowed, | ||||
// - and inner.borrow_mut() would fail while self is borrowed. | ||||
self.inner.borrow() | ||||
} | ||||
Yuya Nishihara
|
r43444 | // TODO: maybe this should be named as try_borrow_mut(), and use | ||
// inner.try_borrow_mut(). The current implementation panics if | ||||
// self.inner has been borrowed, but returns error if py_shared_state | ||||
// refuses to borrow. | ||||
Yuya Nishihara
|
r43610 | fn borrow_mut<'a>(&'a self, py: Python<'a>) -> PyResult<RefMut<'a, T>> { | ||
Yuya Nishihara
|
r43444 | self.py_shared_state.borrow_mut(py, self.inner.borrow_mut()) | ||
Yuya Nishihara
|
r43115 | } | ||
} | ||||
Yuya Nishihara
|
r43448 | /// Sharable data member of type `T` borrowed from the `PyObject`. | ||
pub struct PySharedRef<'a, T> { | ||||
py: Python<'a>, | ||||
owner: &'a PyObject, | ||||
data: &'a PySharedRefCell<T>, | ||||
} | ||||
impl<'a, T> PySharedRef<'a, T> { | ||||
/// # Safety | ||||
/// | ||||
/// The `data` must be owned by the `owner`. Otherwise, the leak count | ||||
/// would get wrong. | ||||
pub unsafe fn new( | ||||
py: Python<'a>, | ||||
owner: &'a PyObject, | ||||
data: &'a PySharedRefCell<T>, | ||||
) -> Self { | ||||
Self { py, owner, data } | ||||
} | ||||
Raphaël Gomès
|
r43566 | pub fn borrow(&self) -> Ref<'a, T> { | ||
Yuya Nishihara
|
r43580 | self.data.borrow(self.py) | ||
Yuya Nishihara
|
r43448 | } | ||
Yuya Nishihara
|
r43610 | pub fn borrow_mut(&self) -> PyResult<RefMut<'a, T>> { | ||
Yuya Nishihara
|
r43448 | self.data.borrow_mut(self.py) | ||
} | ||||
Yuya Nishihara
|
r43579 | /// Returns a leaked reference. | ||
Yuya Nishihara
|
r43608 | /// | ||
/// # Panics | ||||
/// | ||||
/// Panics if this is mutably borrowed. | ||||
Yuya Nishihara
|
r43611 | pub fn leak_immutable(&self) -> PyLeaked<&'static T> { | ||
Yuya Nishihara
|
r43579 | let state = &self.data.py_shared_state; | ||
Yuya Nishihara
|
r43608 | // make sure self.data isn't mutably borrowed; otherwise the | ||
// generation number can't be trusted. | ||||
let data_ref = self.borrow(); | ||||
Yuya Nishihara
|
r43579 | unsafe { | ||
let (static_ref, static_state_ref) = | ||||
Yuya Nishihara
|
r43611 | state.leak_immutable(self.py, data_ref); | ||
PyLeaked::new(self.py, self.owner, static_ref, static_state_ref) | ||||
Yuya Nishihara
|
r43579 | } | ||
Yuya Nishihara
|
r43448 | } | ||
} | ||||
Raphaël Gomès
|
r42997 | /// Allows a `py_class!` generated struct to share references to one of its | ||
/// data members with Python. | ||||
/// | ||||
/// # Parameters | ||||
/// | ||||
/// * `$name` is the same identifier used in for `py_class!` macro call. | ||||
/// * `$inner_struct` is the identifier of the underlying Rust struct | ||||
/// * `$data_member` is the identifier of the data member of `$inner_struct` | ||||
/// that will be shared. | ||||
Yuya Nishihara
|
r43448 | /// * `$shared_accessor` is the function name to be generated, which allows | ||
/// safe access to the data member. | ||||
/// | ||||
/// # Safety | ||||
/// | ||||
/// `$data_member` must persist while the `$name` object is alive. In other | ||||
/// words, it must be an accessor to a data field of the Python object. | ||||
Raphaël Gomès
|
r42997 | /// | ||
/// # Example | ||||
/// | ||||
/// ``` | ||||
/// struct MyStruct { | ||||
/// inner: Vec<u32>; | ||||
/// } | ||||
/// | ||||
/// py_class!(pub class MyType |py| { | ||||
Yuya Nishihara
|
r43115 | /// data inner: PySharedRefCell<MyStruct>; | ||
Raphaël Gomès
|
r42997 | /// }); | ||
/// | ||||
Yuya Nishihara
|
r43448 | /// py_shared_ref!(MyType, MyStruct, inner, inner_shared); | ||
Raphaël Gomès
|
r42997 | /// ``` | ||
macro_rules! py_shared_ref { | ||||
( | ||||
$name: ident, | ||||
$inner_struct: ident, | ||||
Yuya Nishihara
|
r43448 | $data_member: ident, | ||
$shared_accessor: ident | ||||
Raphaël Gomès
|
r42997 | ) => { | ||
impl $name { | ||||
Yuya Nishihara
|
r43448 | /// Returns a safe reference to the shared `$data_member`. | ||
/// | ||||
/// This function guarantees that `PySharedRef` is created with | ||||
/// the valid `self` and `self.$data_member(py)` pair. | ||||
fn $shared_accessor<'a>( | ||||
&'a self, | ||||
py: Python<'a>, | ||||
) -> $crate::ref_sharing::PySharedRef<'a, $inner_struct> { | ||||
use cpython::PythonObject; | ||||
use $crate::ref_sharing::PySharedRef; | ||||
let owner = self.as_object(); | ||||
let data = self.$data_member(py); | ||||
unsafe { PySharedRef::new(py, owner, data) } | ||||
} | ||||
Raphaël Gomès
|
r42997 | } | ||
Yuya Nishihara
|
r43447 | }; | ||
} | ||||
Raphaël Gomès
|
r42997 | |||
Yuya Nishihara
|
r43447 | /// Manage immutable references to `PyObject` leaked into Python iterators. | ||
Yuya Nishihara
|
r43605 | /// | ||
/// This reference will be invalidated once the original value is mutably | ||||
/// borrowed. | ||||
Yuya Nishihara
|
r43603 | pub struct PyLeaked<T> { | ||
Yuya Nishihara
|
r43579 | inner: PyObject, | ||
data: Option<T>, | ||||
Yuya Nishihara
|
r43447 | py_shared_state: &'static PySharedState, | ||
Yuya Nishihara
|
r43605 | /// Generation counter of data `T` captured when PyLeaked is created. | ||
generation: usize, | ||||
Yuya Nishihara
|
r43447 | } | ||
Raphaël Gomès
|
r42997 | |||
Yuya Nishihara
|
r43603 | // DO NOT implement Deref for PyLeaked<T>! Dereferencing PyLeaked | ||
Yuya Nishihara
|
r43605 | // without taking Python GIL wouldn't be safe. Also, the underling reference | ||
// is invalid if generation != py_shared_state.generation. | ||||
Yuya Nishihara
|
r43579 | |||
Yuya Nishihara
|
r43603 | impl<T> PyLeaked<T> { | ||
Yuya Nishihara
|
r43447 | /// # Safety | ||
/// | ||||
/// The `py_shared_state` must be owned by the `inner` Python object. | ||||
Yuya Nishihara
|
r43582 | fn new( | ||
Yuya Nishihara
|
r43447 | py: Python, | ||
inner: &PyObject, | ||||
Yuya Nishihara
|
r43578 | data: T, | ||
Yuya Nishihara
|
r43447 | py_shared_state: &'static PySharedState, | ||
) -> Self { | ||||
Self { | ||||
Yuya Nishihara
|
r43579 | inner: inner.clone_ref(py), | ||
Yuya Nishihara
|
r43578 | data: Some(data), | ||
Yuya Nishihara
|
r43447 | py_shared_state, | ||
Yuya Nishihara
|
r43605 | generation: py_shared_state.current_generation(py), | ||
Raphaël Gomès
|
r42997 | } | ||
Yuya Nishihara
|
r43447 | } | ||
Yuya Nishihara
|
r43579 | |||
Yuya Nishihara
|
r43604 | /// Immutably borrows the wrapped value. | ||
Yuya Nishihara
|
r43605 | /// | ||
/// Borrowing fails if the underlying reference has been invalidated. | ||||
Yuya Nishihara
|
r43604 | pub fn try_borrow<'a>( | ||
&'a self, | ||||
py: Python<'a>, | ||||
) -> PyResult<PyLeakedRef<'a, T>> { | ||||
Yuya Nishihara
|
r43605 | self.validate_generation(py)?; | ||
Yuya Nishihara
|
r43604 | Ok(PyLeakedRef { | ||
Yuya Nishihara
|
r43606 | _borrow: BorrowPyShared::new(py, self.py_shared_state), | ||
Yuya Nishihara
|
r43604 | data: self.data.as_ref().unwrap(), | ||
}) | ||||
Yuya Nishihara
|
r43579 | } | ||
Yuya Nishihara
|
r43604 | /// Mutably borrows the wrapped value. | ||
Yuya Nishihara
|
r43579 | /// | ||
Yuya Nishihara
|
r43605 | /// Borrowing fails if the underlying reference has been invalidated. | ||
/// | ||||
Yuya Nishihara
|
r43579 | /// Typically `T` is an iterator. If `T` is an immutable reference, | ||
/// `get_mut()` is useless since the inner value can't be mutated. | ||||
Yuya Nishihara
|
r43604 | pub fn try_borrow_mut<'a>( | ||
&'a mut self, | ||||
py: Python<'a>, | ||||
) -> PyResult<PyLeakedRefMut<'a, T>> { | ||||
Yuya Nishihara
|
r43605 | self.validate_generation(py)?; | ||
Yuya Nishihara
|
r43604 | Ok(PyLeakedRefMut { | ||
Yuya Nishihara
|
r43606 | _borrow: BorrowPyShared::new(py, self.py_shared_state), | ||
Yuya Nishihara
|
r43604 | data: self.data.as_mut().unwrap(), | ||
}) | ||||
Yuya Nishihara
|
r43579 | } | ||
/// Converts the inner value by the given function. | ||||
/// | ||||
/// Typically `T` is a static reference to a container, and `U` is an | ||||
/// iterator of that container. | ||||
/// | ||||
Yuya Nishihara
|
r43605 | /// # Panics | ||
/// | ||||
/// Panics if the underlying reference has been invalidated. | ||||
/// | ||||
/// This is typically called immediately after the `PyLeaked` is obtained. | ||||
/// In which case, the reference must be valid and no panic would occur. | ||||
/// | ||||
Yuya Nishihara
|
r43579 | /// # Safety | ||
/// | ||||
/// The lifetime of the object passed in to the function `f` is cheated. | ||||
/// It's typically a static reference, but is valid only while the | ||||
Yuya Nishihara
|
r43603 | /// corresponding `PyLeaked` is alive. Do not copy it out of the | ||
Yuya Nishihara
|
r43579 | /// function call. | ||
pub unsafe fn map<U>( | ||||
mut self, | ||||
py: Python, | ||||
f: impl FnOnce(T) -> U, | ||||
Yuya Nishihara
|
r43603 | ) -> PyLeaked<U> { | ||
Yuya Nishihara
|
r43605 | // Needs to test the generation value to make sure self.data reference | ||
// is still intact. | ||||
self.validate_generation(py) | ||||
.expect("map() over invalidated leaked reference"); | ||||
Yuya Nishihara
|
r43579 | // f() could make the self.data outlive. That's why map() is unsafe. | ||
// In order to make this function safe, maybe we'll need a way to | ||||
// temporarily restrict the lifetime of self.data and translate the | ||||
// returned object back to Something<'static>. | ||||
let new_data = f(self.data.take().unwrap()); | ||||
Yuya Nishihara
|
r43603 | PyLeaked { | ||
Yuya Nishihara
|
r43579 | inner: self.inner.clone_ref(py), | ||
data: Some(new_data), | ||||
py_shared_state: self.py_shared_state, | ||||
Yuya Nishihara
|
r43605 | generation: self.generation, | ||
} | ||||
} | ||||
fn validate_generation(&self, py: Python) -> PyResult<()> { | ||||
if self.py_shared_state.current_generation(py) == self.generation { | ||||
Ok(()) | ||||
} else { | ||||
Err(PyErr::new::<exc::RuntimeError, _>( | ||||
py, | ||||
"Cannot access to leaked reference after mutation", | ||||
)) | ||||
Yuya Nishihara
|
r43579 | } | ||
} | ||||
Yuya Nishihara
|
r43447 | } | ||
Raphaël Gomès
|
r42997 | |||
Yuya Nishihara
|
r43604 | /// Immutably borrowed reference to a leaked value. | ||
pub struct PyLeakedRef<'a, T> { | ||||
Yuya Nishihara
|
r43606 | _borrow: BorrowPyShared<'a>, | ||
Yuya Nishihara
|
r43604 | data: &'a T, | ||
} | ||||
impl<T> Deref for PyLeakedRef<'_, T> { | ||||
type Target = T; | ||||
fn deref(&self) -> &T { | ||||
self.data | ||||
} | ||||
} | ||||
/// Mutably borrowed reference to a leaked value. | ||||
pub struct PyLeakedRefMut<'a, T> { | ||||
Yuya Nishihara
|
r43606 | _borrow: BorrowPyShared<'a>, | ||
Yuya Nishihara
|
r43604 | data: &'a mut T, | ||
} | ||||
impl<T> Deref for PyLeakedRefMut<'_, T> { | ||||
type Target = T; | ||||
fn deref(&self) -> &T { | ||||
self.data | ||||
} | ||||
} | ||||
impl<T> DerefMut for PyLeakedRefMut<'_, T> { | ||||
fn deref_mut(&mut self) -> &mut T { | ||||
self.data | ||||
} | ||||
} | ||||
Raphaël Gomès
|
r42997 | /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator. | ||
Yuya Nishihara
|
r43158 | /// | ||
/// TODO: this is a bit awkward to use, and a better (more complicated) | ||||
/// procedural macro would simplify the interface a lot. | ||||
/// | ||||
/// # Parameters | ||||
/// | ||||
/// * `$name` is the identifier to give to the resulting Rust struct. | ||||
/// * `$leaked` corresponds to `$leaked` in the matching `py_shared_ref!` call. | ||||
/// * `$iterator_type` is the type of the Rust iterator. | ||||
/// * `$success_func` is a function for processing the Rust `(key, value)` | ||||
/// tuple on iteration success, turning it into something Python understands. | ||||
/// * `$success_func` is the return type of `$success_func` | ||||
/// | ||||
/// # Example | ||||
/// | ||||
/// ``` | ||||
/// struct MyStruct { | ||||
/// inner: HashMap<Vec<u8>, Vec<u8>>; | ||||
/// } | ||||
/// | ||||
/// py_class!(pub class MyType |py| { | ||||
/// data inner: PySharedRefCell<MyStruct>; | ||||
/// | ||||
/// def __iter__(&self) -> PyResult<MyTypeItemsIterator> { | ||||
Yuya Nishihara
|
r43611 | /// let leaked_ref = self.inner_shared(py).leak_immutable(); | ||
Yuya Nishihara
|
r43161 | /// MyTypeItemsIterator::from_inner( | ||
Yuya Nishihara
|
r43158 | /// py, | ||
Yuya Nishihara
|
r43579 | /// unsafe { leaked_ref.map(py, |o| o.iter()) }, | ||
Yuya Nishihara
|
r43158 | /// ) | ||
/// } | ||||
/// }); | ||||
/// | ||||
/// impl MyType { | ||||
/// fn translate_key_value( | ||||
/// py: Python, | ||||
/// res: (&Vec<u8>, &Vec<u8>), | ||||
/// ) -> PyResult<Option<(PyBytes, PyBytes)>> { | ||||
/// let (f, entry) = res; | ||||
/// Ok(Some(( | ||||
/// PyBytes::new(py, f), | ||||
/// PyBytes::new(py, entry), | ||||
/// ))) | ||||
/// } | ||||
/// } | ||||
/// | ||||
/// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef); | ||||
/// | ||||
Yuya Nishihara
|
r43159 | /// py_shared_iterator!( | ||
Yuya Nishihara
|
r43158 | /// MyTypeItemsIterator, | ||
Yuya Nishihara
|
r43603 | /// PyLeaked<HashMap<'static, Vec<u8>, Vec<u8>>>, | ||
Yuya Nishihara
|
r43158 | /// MyType::translate_key_value, | ||
/// Option<(PyBytes, PyBytes)> | ||||
/// ); | ||||
/// ``` | ||||
Yuya Nishihara
|
r43159 | macro_rules! py_shared_iterator { | ||
Raphaël Gomès
|
r42997 | ( | ||
$name: ident, | ||||
Yuya Nishihara
|
r43447 | $leaked: ty, | ||
Raphaël Gomès
|
r42997 | $success_func: expr, | ||
$success_type: ty | ||||
) => { | ||||
py_class!(pub class $name |py| { | ||||
Yuya Nishihara
|
r43607 | data inner: RefCell<$leaked>; | ||
Raphaël Gomès
|
r42997 | |||
def __next__(&self) -> PyResult<$success_type> { | ||||
Yuya Nishihara
|
r43607 | let mut leaked = self.inner(py).borrow_mut(); | ||
let mut iter = leaked.try_borrow_mut(py)?; | ||||
match iter.next() { | ||||
None => Ok(None), | ||||
Some(res) => $success_func(py, res), | ||||
Raphaël Gomès
|
r42997 | } | ||
} | ||||
def __iter__(&self) -> PyResult<Self> { | ||||
Ok(self.clone_ref(py)) | ||||
} | ||||
}); | ||||
impl $name { | ||||
pub fn from_inner( | ||||
py: Python, | ||||
Yuya Nishihara
|
r43160 | leaked: $leaked, | ||
Raphaël Gomès
|
r42997 | ) -> PyResult<Self> { | ||
Self::create_instance( | ||||
py, | ||||
Yuya Nishihara
|
r43607 | RefCell::new(leaked), | ||
Raphaël Gomès
|
r42997 | ) | ||
} | ||||
} | ||||
}; | ||||
} | ||||
Yuya Nishihara
|
r43583 | |||
#[cfg(test)] | ||||
#[cfg(any(feature = "python27-bin", feature = "python3-bin"))] | ||||
mod test { | ||||
use super::*; | ||||
use cpython::{GILGuard, Python}; | ||||
py_class!(class Owner |py| { | ||||
data string: PySharedRefCell<String>; | ||||
}); | ||||
py_shared_ref!(Owner, String, string, string_shared); | ||||
fn prepare_env() -> (GILGuard, Owner) { | ||||
let gil = Python::acquire_gil(); | ||||
let py = gil.python(); | ||||
let owner = | ||||
Owner::create_instance(py, PySharedRefCell::new("new".to_owned())) | ||||
.unwrap(); | ||||
(gil, owner) | ||||
} | ||||
#[test] | ||||
Yuya Nishihara
|
r43604 | fn test_leaked_borrow() { | ||
let (gil, owner) = prepare_env(); | ||||
let py = gil.python(); | ||||
Yuya Nishihara
|
r43611 | let leaked = owner.string_shared(py).leak_immutable(); | ||
Yuya Nishihara
|
r43604 | let leaked_ref = leaked.try_borrow(py).unwrap(); | ||
assert_eq!(*leaked_ref, "new"); | ||||
} | ||||
#[test] | ||||
fn test_leaked_borrow_mut() { | ||||
let (gil, owner) = prepare_env(); | ||||
let py = gil.python(); | ||||
Yuya Nishihara
|
r43611 | let leaked = owner.string_shared(py).leak_immutable(); | ||
Yuya Nishihara
|
r43604 | let mut leaked_iter = unsafe { leaked.map(py, |s| s.chars()) }; | ||
let mut leaked_ref = leaked_iter.try_borrow_mut(py).unwrap(); | ||||
assert_eq!(leaked_ref.next(), Some('n')); | ||||
assert_eq!(leaked_ref.next(), Some('e')); | ||||
assert_eq!(leaked_ref.next(), Some('w')); | ||||
assert_eq!(leaked_ref.next(), None); | ||||
} | ||||
#[test] | ||||
Yuya Nishihara
|
r43605 | fn test_leaked_borrow_after_mut() { | ||
let (gil, owner) = prepare_env(); | ||||
let py = gil.python(); | ||||
Yuya Nishihara
|
r43611 | let leaked = owner.string_shared(py).leak_immutable(); | ||
Yuya Nishihara
|
r43605 | owner.string_shared(py).borrow_mut().unwrap().clear(); | ||
assert!(leaked.try_borrow(py).is_err()); | ||||
} | ||||
#[test] | ||||
fn test_leaked_borrow_mut_after_mut() { | ||||
let (gil, owner) = prepare_env(); | ||||
let py = gil.python(); | ||||
Yuya Nishihara
|
r43611 | let leaked = owner.string_shared(py).leak_immutable(); | ||
Yuya Nishihara
|
r43605 | let mut leaked_iter = unsafe { leaked.map(py, |s| s.chars()) }; | ||
owner.string_shared(py).borrow_mut().unwrap().clear(); | ||||
assert!(leaked_iter.try_borrow_mut(py).is_err()); | ||||
} | ||||
#[test] | ||||
#[should_panic(expected = "map() over invalidated leaked reference")] | ||||
fn test_leaked_map_after_mut() { | ||||
let (gil, owner) = prepare_env(); | ||||
let py = gil.python(); | ||||
Yuya Nishihara
|
r43611 | let leaked = owner.string_shared(py).leak_immutable(); | ||
Yuya Nishihara
|
r43605 | owner.string_shared(py).borrow_mut().unwrap().clear(); | ||
let _leaked_iter = unsafe { leaked.map(py, |s| s.chars()) }; | ||||
} | ||||
#[test] | ||||
Yuya Nishihara
|
r43606 | fn test_borrow_mut_while_leaked_ref() { | ||
Yuya Nishihara
|
r43583 | let (gil, owner) = prepare_env(); | ||
let py = gil.python(); | ||||
assert!(owner.string_shared(py).borrow_mut().is_ok()); | ||||
Yuya Nishihara
|
r43611 | let leaked = owner.string_shared(py).leak_immutable(); | ||
Yuya Nishihara
|
r43606 | { | ||
let _leaked_ref = leaked.try_borrow(py).unwrap(); | ||||
assert!(owner.string_shared(py).borrow_mut().is_err()); | ||||
{ | ||||
let _leaked_ref2 = leaked.try_borrow(py).unwrap(); | ||||
assert!(owner.string_shared(py).borrow_mut().is_err()); | ||||
} | ||||
assert!(owner.string_shared(py).borrow_mut().is_err()); | ||||
} | ||||
assert!(owner.string_shared(py).borrow_mut().is_ok()); | ||||
} | ||||
#[test] | ||||
fn test_borrow_mut_while_leaked_ref_mut() { | ||||
let (gil, owner) = prepare_env(); | ||||
let py = gil.python(); | ||||
assert!(owner.string_shared(py).borrow_mut().is_ok()); | ||||
Yuya Nishihara
|
r43611 | let leaked = owner.string_shared(py).leak_immutable(); | ||
Yuya Nishihara
|
r43606 | let mut leaked_iter = unsafe { leaked.map(py, |s| s.chars()) }; | ||
{ | ||||
let _leaked_ref = leaked_iter.try_borrow_mut(py).unwrap(); | ||||
assert!(owner.string_shared(py).borrow_mut().is_err()); | ||||
} | ||||
assert!(owner.string_shared(py).borrow_mut().is_ok()); | ||||
Yuya Nishihara
|
r43583 | } | ||
Yuya Nishihara
|
r43608 | |||
#[test] | ||||
#[should_panic(expected = "mutably borrowed")] | ||||
fn test_leak_while_borrow_mut() { | ||||
let (gil, owner) = prepare_env(); | ||||
let py = gil.python(); | ||||
let _mut_ref = owner.string_shared(py).borrow_mut(); | ||||
Yuya Nishihara
|
r43611 | owner.string_shared(py).leak_immutable(); | ||
Yuya Nishihara
|
r43608 | } | ||
Yuya Nishihara
|
r43583 | } | ||