##// END OF EJS Templates
phabricator: use .arcconfig for the callsign if not set locally (issue6243)...
phabricator: use .arcconfig for the callsign if not set locally (issue6243) This makes things easier for people working with more than one repository because this file can be committed to each repository. The bug report asks to read <repo>/.arcrc, but AFAICT, that file lives in ~/ and holds the credentials. And we already track an .arcconfig file. Any callsign set globally is still used if that is all that is present, but .arcconfig will override it if available. The idea behind letting the local hgrc override .arcconfig is that the developer may need to do testing against another server, and not dirty the working directory. Originally I was going to just try to read the callsign in `getrepophid()` if it wasn't present in the hg config. That works fine, but I think it also makes sense to read the URL from this file too. That would have worked less well because `readurltoken()` doesn't have access to the repo object to know where to find the file. Supplimenting the config mechanism is less magical because it reports the source and value of the properties used, and it doesn't need to read the file twice. Invalid hgrc files generally cause the program to abort. I only flagged it as a warning here because it's not our config file, not crucial to the whole program operating, and really shouldn't be corrupt in the typical case where it is checked into the repo. Differential Revision: https://phab.mercurial-scm.org/D7934

File last commit:

r43611:8418b771 default
r44586:59b3fe1e default
Show More
ref_sharing.rs
636 lines | 20.5 KiB | application/rls-services+xml | RustLexer
Yuya Nishihara
rust-cpython: change license of ref_sharing.rs to MIT...
r43352 // ref_sharing.rs
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 //
// Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
//
Yuya Nishihara
rust-cpython: change license of ref_sharing.rs to MIT...
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
rust-cpython: add macro for sharing references...
r42997
//! Macros for use in the `hg-cpython` bridge library.
use crate::exceptions::AlreadyBorrowed;
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
r43605 use cpython::{exc, PyClone, PyErr, PyObject, PyResult, Python};
Yuya Nishihara
rust-cpython: drop manual management of mutably_borrowed...
r43609 use std::cell::{Ref, RefCell, RefMut};
Yuya Nishihara
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
r43604 use std::ops::{Deref, DerefMut};
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
r43605 use std::sync::atomic::{AtomicUsize, Ordering};
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997
/// Manages the shared state between Python and Rust
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
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
rust-cpython: leverage RefCell::borrow() to guarantee there's no mutable ref...
r43608 /// - The underlying `RefCell`, which prevents `PySharedRefCell` data from
/// being directly borrowed or leaked while it is mutably borrowed.
Yuya Nishihara
rust-cpython: allow mutation unless leaked reference is borrowed...
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
rust-cpython: add generation counter to leaked reference...
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
rust-cpython: move py_shared_state to PySharedRefCell object...
r43443 #[derive(Debug, Default)]
Yuya Nishihara
rust-cpython: make inner functions and structs of ref_sharing private...
r43582 struct PySharedState {
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
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
rust-cpython: allow mutation unless leaked reference is borrowed...
r43606 borrow_count: AtomicUsize,
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
r43605 generation: AtomicUsize,
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 }
impl PySharedState {
Yuya Nishihara
rust-cpython: make inner functions and structs of ref_sharing private...
r43582 fn borrow_mut<'a, T>(
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 &'a self,
py: Python<'a>,
pyrefmut: RefMut<'a, T>,
Yuya Nishihara
rust-cpython: remove useless PyRefMut wrapper
r43610 ) -> PyResult<RefMut<'a, T>> {
Yuya Nishihara
rust-cpython: allow mutation unless leaked reference is borrowed...
r43606 match self.current_borrow_count(py) {
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 0 => {
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
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
rust-cpython: remove useless PyRefMut wrapper
r43610 Ok(pyrefmut)
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 }
_ => Err(AlreadyBorrowed::new(
py,
Yuya Nishihara
rust-cpython: allow mutation unless leaked reference is borrowed...
r43606 "Cannot borrow mutably while immutably borrowed",
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 )),
}
}
Yuya Nishihara
rust-cpython: store leaked reference to PySharedState in $leaked struct...
r43446 /// Return a reference to the wrapped data and its state with an
/// artificial static lifetime.
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 /// We need to be protected by the GIL for thread-safety.
Yuya Nishihara
rust-cpython: mark unsafe functions as such...
r43117 ///
/// # Safety
///
/// This is highly unsafe since the lifetime of the given data can be
/// extended. Do not call this function directly.
Yuya Nishihara
rust-cpython: make inner functions and structs of ref_sharing private...
r43582 unsafe fn leak_immutable<T>(
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 &self,
Yuya Nishihara
rust-cpython: drop manual management of mutably_borrowed...
r43609 _py: Python,
Yuya Nishihara
rust-cpython: leverage RefCell::borrow() to guarantee there's no mutable ref...
r43608 data: Ref<T>,
Yuya Nishihara
rust-cpython: remove useless PyResult<> from leak_immutable()...
r43611 ) -> (&'static T, &'static PySharedState) {
Yuya Nishihara
rust-cpython: leverage RefCell::borrow() to guarantee there's no mutable ref...
r43608 let ptr: *const T = &*data;
let state_ptr: *const PySharedState = self;
Yuya Nishihara
rust-cpython: remove useless PyResult<> from leak_immutable()...
r43611 (&*ptr, &*state_ptr)
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 }
Yuya Nishihara
rust-cpython: allow mutation unless leaked reference is borrowed...
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
rust-cpython: add generation counter to leaked reference...
r43605 fn current_generation(&self, _py: Python) -> usize {
self.generation.load(Ordering::Relaxed)
}
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 }
Yuya Nishihara
rust-cpython: allow mutation unless leaked reference is borrowed...
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
rust-cpython: introduce restricted variant of RefCell...
r43115 /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`.
///
Yuya Nishihara
rust-cpython: make inner functions and structs of ref_sharing private...
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
rust-cpython: introduce restricted variant of RefCell...
r43115 #[derive(Debug)]
pub struct PySharedRefCell<T> {
inner: RefCell<T>,
Yuya Nishihara
rust-cpython: add safe wrapper representing shared data borrowed from PyObject...
r43448 py_shared_state: PySharedState,
Yuya Nishihara
rust-cpython: introduce restricted variant of RefCell...
r43115 }
impl<T> PySharedRefCell<T> {
Yuya Nishihara
rust-cpython: move py_shared_state to PySharedRefCell object...
r43443 pub fn new(value: T) -> PySharedRefCell<T> {
Yuya Nishihara
rust-cpython: introduce restricted variant of RefCell...
r43115 Self {
inner: RefCell::new(value),
Yuya Nishihara
rust-cpython: move py_shared_state to PySharedRefCell object...
r43443 py_shared_state: PySharedState::default(),
Yuya Nishihara
rust-cpython: introduce restricted variant of RefCell...
r43115 }
}
Yuya Nishihara
rust-cpython: make inner functions and structs of ref_sharing private...
r43582 fn borrow<'a>(&'a self, _py: Python<'a>) -> Ref<'a, T> {
Yuya Nishihara
rust-cpython: introduce restricted variant of RefCell...
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
rust-cpython: move borrow_mut() to PySharedRefCell...
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
rust-cpython: remove useless PyRefMut wrapper
r43610 fn borrow_mut<'a>(&'a self, py: Python<'a>) -> PyResult<RefMut<'a, T>> {
Yuya Nishihara
rust-cpython: move borrow_mut() to PySharedRefCell...
r43444 self.py_shared_state.borrow_mut(py, self.inner.borrow_mut())
Yuya Nishihara
rust-cpython: introduce restricted variant of RefCell...
r43115 }
}
Yuya Nishihara
rust-cpython: add safe wrapper representing shared data borrowed from PyObject...
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
rust-refsharing: add missing lifetime parameter in ref_sharing...
r43566 pub fn borrow(&self) -> Ref<'a, T> {
Yuya Nishihara
rust-cpython: require GIL to borrow immutable reference from PySharedRefCell...
r43580 self.data.borrow(self.py)
Yuya Nishihara
rust-cpython: add safe wrapper representing shared data borrowed from PyObject...
r43448 }
Yuya Nishihara
rust-cpython: remove useless PyRefMut wrapper
r43610 pub fn borrow_mut(&self) -> PyResult<RefMut<'a, T>> {
Yuya Nishihara
rust-cpython: add safe wrapper representing shared data borrowed from PyObject...
r43448 self.data.borrow_mut(self.py)
}
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
r43579 /// Returns a leaked reference.
Yuya Nishihara
rust-cpython: leverage RefCell::borrow() to guarantee there's no mutable ref...
r43608 ///
/// # Panics
///
/// Panics if this is mutably borrowed.
Yuya Nishihara
rust-cpython: remove useless PyResult<> from leak_immutable()...
r43611 pub fn leak_immutable(&self) -> PyLeaked<&'static T> {
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
r43579 let state = &self.data.py_shared_state;
Yuya Nishihara
rust-cpython: leverage RefCell::borrow() to guarantee there's no mutable ref...
r43608 // make sure self.data isn't mutably borrowed; otherwise the
// generation number can't be trusted.
let data_ref = self.borrow();
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
r43579 unsafe {
let (static_ref, static_state_ref) =
Yuya Nishihara
rust-cpython: remove useless PyResult<> from leak_immutable()...
r43611 state.leak_immutable(self.py, data_ref);
PyLeaked::new(self.py, self.owner, static_ref, static_state_ref)
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
r43579 }
Yuya Nishihara
rust-cpython: add safe wrapper representing shared data borrowed from PyObject...
r43448 }
}
Raphaël Gomès
rust-cpython: add macro for sharing references...
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
rust-cpython: add safe wrapper representing shared data borrowed from PyObject...
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
rust-cpython: add macro for sharing references...
r42997 ///
/// # Example
///
/// ```
/// struct MyStruct {
/// inner: Vec<u32>;
/// }
///
/// py_class!(pub class MyType |py| {
Yuya Nishihara
rust-cpython: introduce restricted variant of RefCell...
r43115 /// data inner: PySharedRefCell<MyStruct>;
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 /// });
///
Yuya Nishihara
rust-cpython: add safe wrapper representing shared data borrowed from PyObject...
r43448 /// py_shared_ref!(MyType, MyStruct, inner, inner_shared);
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 /// ```
macro_rules! py_shared_ref {
(
$name: ident,
$inner_struct: ident,
Yuya Nishihara
rust-cpython: add safe wrapper representing shared data borrowed from PyObject...
r43448 $data_member: ident,
$shared_accessor: ident
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 ) => {
impl $name {
Yuya Nishihara
rust-cpython: add safe wrapper representing shared data borrowed from PyObject...
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
rust-cpython: add macro for sharing references...
r42997 }
Yuya Nishihara
rust-cpython: move $leaked struct out of macro...
r43447 };
}
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997
Yuya Nishihara
rust-cpython: move $leaked struct out of macro...
r43447 /// Manage immutable references to `PyObject` leaked into Python iterators.
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
r43605 ///
/// This reference will be invalidated once the original value is mutably
/// borrowed.
Yuya Nishihara
rust-cpython: rename PyLeakedRef to PyLeaked...
r43603 pub struct PyLeaked<T> {
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
r43579 inner: PyObject,
data: Option<T>,
Yuya Nishihara
rust-cpython: move $leaked struct out of macro...
r43447 py_shared_state: &'static PySharedState,
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
r43605 /// Generation counter of data `T` captured when PyLeaked is created.
generation: usize,
Yuya Nishihara
rust-cpython: move $leaked struct out of macro...
r43447 }
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997
Yuya Nishihara
rust-cpython: rename PyLeakedRef to PyLeaked...
r43603 // DO NOT implement Deref for PyLeaked<T>! Dereferencing PyLeaked
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
r43605 // without taking Python GIL wouldn't be safe. Also, the underling reference
// is invalid if generation != py_shared_state.generation.
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
r43579
Yuya Nishihara
rust-cpython: rename PyLeakedRef to PyLeaked...
r43603 impl<T> PyLeaked<T> {
Yuya Nishihara
rust-cpython: move $leaked struct out of macro...
r43447 /// # Safety
///
/// The `py_shared_state` must be owned by the `inner` Python object.
Yuya Nishihara
rust-cpython: make inner functions and structs of ref_sharing private...
r43582 fn new(
Yuya Nishihara
rust-cpython: move $leaked struct out of macro...
r43447 py: Python,
inner: &PyObject,
Yuya Nishihara
rust-cpython: put leaked reference in PyLeakedRef...
r43578 data: T,
Yuya Nishihara
rust-cpython: move $leaked struct out of macro...
r43447 py_shared_state: &'static PySharedState,
) -> Self {
Self {
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
r43579 inner: inner.clone_ref(py),
Yuya Nishihara
rust-cpython: put leaked reference in PyLeakedRef...
r43578 data: Some(data),
Yuya Nishihara
rust-cpython: move $leaked struct out of macro...
r43447 py_shared_state,
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
r43605 generation: py_shared_state.current_generation(py),
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 }
Yuya Nishihara
rust-cpython: move $leaked struct out of macro...
r43447 }
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
r43579
Yuya Nishihara
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
r43604 /// Immutably borrows the wrapped value.
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
r43605 ///
/// Borrowing fails if the underlying reference has been invalidated.
Yuya Nishihara
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
r43604 pub fn try_borrow<'a>(
&'a self,
py: Python<'a>,
) -> PyResult<PyLeakedRef<'a, T>> {
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
r43605 self.validate_generation(py)?;
Yuya Nishihara
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
r43604 Ok(PyLeakedRef {
Yuya Nishihara
rust-cpython: allow mutation unless leaked reference is borrowed...
r43606 _borrow: BorrowPyShared::new(py, self.py_shared_state),
Yuya Nishihara
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
r43604 data: self.data.as_ref().unwrap(),
})
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
r43579 }
Yuya Nishihara
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
r43604 /// Mutably borrows the wrapped value.
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
r43579 ///
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
r43605 /// Borrowing fails if the underlying reference has been invalidated.
///
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
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
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
r43604 pub fn try_borrow_mut<'a>(
&'a mut self,
py: Python<'a>,
) -> PyResult<PyLeakedRefMut<'a, T>> {
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
r43605 self.validate_generation(py)?;
Yuya Nishihara
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
r43604 Ok(PyLeakedRefMut {
Yuya Nishihara
rust-cpython: allow mutation unless leaked reference is borrowed...
r43606 _borrow: BorrowPyShared::new(py, self.py_shared_state),
Yuya Nishihara
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
r43604 data: self.data.as_mut().unwrap(),
})
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
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
rust-cpython: add generation counter to leaked reference...
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
rust-cpython: make PyLeakedRef operations relatively safe...
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
rust-cpython: rename PyLeakedRef to PyLeaked...
r43603 /// corresponding `PyLeaked` is alive. Do not copy it out of the
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
r43579 /// function call.
pub unsafe fn map<U>(
mut self,
py: Python,
f: impl FnOnce(T) -> U,
Yuya Nishihara
rust-cpython: rename PyLeakedRef to PyLeaked...
r43603 ) -> PyLeaked<U> {
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
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
rust-cpython: make PyLeakedRef operations relatively safe...
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
rust-cpython: rename PyLeakedRef to PyLeaked...
r43603 PyLeaked {
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
r43579 inner: self.inner.clone_ref(py),
data: Some(new_data),
py_shared_state: self.py_shared_state,
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
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
rust-cpython: make PyLeakedRef operations relatively safe...
r43579 }
}
Yuya Nishihara
rust-cpython: move $leaked struct out of macro...
r43447 }
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997
Yuya Nishihara
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
r43604 /// Immutably borrowed reference to a leaked value.
pub struct PyLeakedRef<'a, T> {
Yuya Nishihara
rust-cpython: allow mutation unless leaked reference is borrowed...
r43606 _borrow: BorrowPyShared<'a>,
Yuya Nishihara
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
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
rust-cpython: allow mutation unless leaked reference is borrowed...
r43606 _borrow: BorrowPyShared<'a>,
Yuya Nishihara
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
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
rust-cpython: add macro for sharing references...
r42997 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
Yuya Nishihara
rust-cpython: replace dyn Iterator<..> of mapping with concrete type...
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
rust-cpython: remove useless PyResult<> from leak_immutable()...
r43611 /// let leaked_ref = self.inner_shared(py).leak_immutable();
Yuya Nishihara
rust-cpython: leverage py_shared_iterator::from_inner() where appropriate
r43161 /// MyTypeItemsIterator::from_inner(
Yuya Nishihara
rust-cpython: replace dyn Iterator<..> of mapping with concrete type...
r43158 /// py,
Yuya Nishihara
rust-cpython: make PyLeakedRef operations relatively safe...
r43579 /// unsafe { leaked_ref.map(py, |o| o.iter()) },
Yuya Nishihara
rust-cpython: replace dyn Iterator<..> of mapping with concrete type...
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
rust-cpython: rename py_shared_iterator_impl to py_shared_iterator...
r43159 /// py_shared_iterator!(
Yuya Nishihara
rust-cpython: replace dyn Iterator<..> of mapping with concrete type...
r43158 /// MyTypeItemsIterator,
Yuya Nishihara
rust-cpython: rename PyLeakedRef to PyLeaked...
r43603 /// PyLeaked<HashMap<'static, Vec<u8>, Vec<u8>>>,
Yuya Nishihara
rust-cpython: replace dyn Iterator<..> of mapping with concrete type...
r43158 /// MyType::translate_key_value,
/// Option<(PyBytes, PyBytes)>
/// );
/// ```
Yuya Nishihara
rust-cpython: rename py_shared_iterator_impl to py_shared_iterator...
r43159 macro_rules! py_shared_iterator {
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 (
$name: ident,
Yuya Nishihara
rust-cpython: move $leaked struct out of macro...
r43447 $leaked: ty,
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 $success_func: expr,
$success_type: ty
) => {
py_class!(pub class $name |py| {
Yuya Nishihara
rust-cpython: remove useless Option<$leaked> from py_shared_iterator...
r43607 data inner: RefCell<$leaked>;
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997
def __next__(&self) -> PyResult<$success_type> {
Yuya Nishihara
rust-cpython: remove useless Option<$leaked> from py_shared_iterator...
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
rust-cpython: add macro for sharing references...
r42997 }
}
def __iter__(&self) -> PyResult<Self> {
Ok(self.clone_ref(py))
}
});
impl $name {
pub fn from_inner(
py: Python,
Yuya Nishihara
rust-cpython: remove Option<_> from interface of py_shared_iterator...
r43160 leaked: $leaked,
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 ) -> PyResult<Self> {
Self::create_instance(
py,
Yuya Nishihara
rust-cpython: remove useless Option<$leaked> from py_shared_iterator...
r43607 RefCell::new(leaked),
Raphaël Gomès
rust-cpython: add macro for sharing references...
r42997 )
}
}
};
}
Yuya Nishihara
rust-cpython: prepare for writing tests that require libpython...
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
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
r43604 fn test_leaked_borrow() {
let (gil, owner) = prepare_env();
let py = gil.python();
Yuya Nishihara
rust-cpython: remove useless PyResult<> from leak_immutable()...
r43611 let leaked = owner.string_shared(py).leak_immutable();
Yuya Nishihara
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
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
rust-cpython: remove useless PyResult<> from leak_immutable()...
r43611 let leaked = owner.string_shared(py).leak_immutable();
Yuya Nishihara
rust-cpython: add stub wrapper that'll prevent leaked data from being mutated...
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
rust-cpython: add generation counter to leaked reference...
r43605 fn test_leaked_borrow_after_mut() {
let (gil, owner) = prepare_env();
let py = gil.python();
Yuya Nishihara
rust-cpython: remove useless PyResult<> from leak_immutable()...
r43611 let leaked = owner.string_shared(py).leak_immutable();
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
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
rust-cpython: remove useless PyResult<> from leak_immutable()...
r43611 let leaked = owner.string_shared(py).leak_immutable();
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
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
rust-cpython: remove useless PyResult<> from leak_immutable()...
r43611 let leaked = owner.string_shared(py).leak_immutable();
Yuya Nishihara
rust-cpython: add generation counter to leaked reference...
r43605 owner.string_shared(py).borrow_mut().unwrap().clear();
let _leaked_iter = unsafe { leaked.map(py, |s| s.chars()) };
}
#[test]
Yuya Nishihara
rust-cpython: allow mutation unless leaked reference is borrowed...
r43606 fn test_borrow_mut_while_leaked_ref() {
Yuya Nishihara
rust-cpython: prepare for writing tests that require libpython...
r43583 let (gil, owner) = prepare_env();
let py = gil.python();
assert!(owner.string_shared(py).borrow_mut().is_ok());
Yuya Nishihara
rust-cpython: remove useless PyResult<> from leak_immutable()...
r43611 let leaked = owner.string_shared(py).leak_immutable();
Yuya Nishihara
rust-cpython: allow mutation unless leaked reference is borrowed...
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
rust-cpython: remove useless PyResult<> from leak_immutable()...
r43611 let leaked = owner.string_shared(py).leak_immutable();
Yuya Nishihara
rust-cpython: allow mutation unless leaked reference is borrowed...
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
rust-cpython: prepare for writing tests that require libpython...
r43583 }
Yuya Nishihara
rust-cpython: leverage RefCell::borrow() to guarantee there's no mutable ref...
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
rust-cpython: remove useless PyResult<> from leak_immutable()...
r43611 owner.string_shared(py).leak_immutable();
Yuya Nishihara
rust-cpython: leverage RefCell::borrow() to guarantee there's no mutable ref...
r43608 }
Yuya Nishihara
rust-cpython: prepare for writing tests that require libpython...
r43583 }