Mercurial > public > mercurial-scm > hg-stable
view rust/hg-pyo3/src/ancestors.rs @ 52558:4c9e31984b3a
rust-pyo3: exposition of AncestorsIterator
Compared to the early experiments, we have one less `RwLock` in the
wrapping. Still that is somewhat redundant with `UnsafePyLeaked` being
itself some kind of lock.
In the original rust-cpython code, we were borrowing the `RefCell`
with a method that can panic. Instead we are now converting the
`PoisonError` that unlocking a `RwLock` can produce.
Since all methods acquiring the `RwLock` are themselves protected
by the GIL and do not release it before returning, nor do they
leak RwLock guards, there is no risk of contention on these locks
themselves.
author | Georges Racinet <georges.racinet@cloudcrane.io> |
---|---|
date | Sat, 07 Dec 2024 18:38:37 +0100 |
parents | |
children | 6b694bdf752a |
line wrap: on
line source
// ancestors.rs // // Copyright 2024 Georges Racinet <georges.racinet@cloudcrane.io> // // This software may be used and distributed according to the terms of the // GNU General Public License version 2 or any later version. //! Bindings for the `hg::ancestors` module provided by the //! `hg-core` crate. From Python, this will be seen as `pyo3_rustext.ancestor` //! and can be used as replacement for the the pure `ancestor` Python module. use cpython::UnsafePyLeaked; use pyo3::prelude::*; use std::sync::RwLock; use vcsgraph::lazy_ancestors::AncestorsIterator as VCGAncestorsIterator; use crate::convert_cpython::{ proxy_index_extract, proxy_index_py_leak, py_leaked_borrow_mut, py_leaked_or_map_err, }; use crate::exceptions::{map_lock_error, GraphError}; use crate::revision::{rev_pyiter_collect, PyRevision}; use crate::util::new_submodule; use rusthg::revlog::PySharedIndex; #[pyclass] struct AncestorsIterator { inner: RwLock<UnsafePyLeaked<VCGAncestorsIterator<PySharedIndex>>>, } #[pymethods] impl AncestorsIterator { #[new] fn new( index_proxy: &Bound<'_, PyAny>, initrevs: &Bound<'_, PyAny>, stoprev: PyRevision, inclusive: bool, ) -> PyResult<Self> { // Safety: we don't leak the "faked" reference out of // `UnsafePyLeaked` let initvec: Vec<_> = { let borrowed_idx = unsafe { proxy_index_extract(index_proxy)? }; rev_pyiter_collect(initrevs, borrowed_idx)? }; let (py, leaked_idx) = proxy_index_py_leak(index_proxy)?; let res_ait = unsafe { leaked_idx.map(py, |idx| { VCGAncestorsIterator::new( idx, initvec.into_iter().map(|r| r.0), stoprev.0, inclusive, ) }) }; let ait = py_leaked_or_map_err(py, res_ait, GraphError::from_vcsgraph)?; let inner = ait.into(); Ok(Self { inner }) } fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { slf } fn __next__(slf: PyRefMut<'_, Self>) -> PyResult<Option<PyRevision>> { let mut leaked = slf.inner.write().map_err(map_lock_error)?; // Safety: we don't leak the inner 'static ref out of UnsafePyLeaked let mut inner = unsafe { py_leaked_borrow_mut(&slf, &mut leaked)? }; match inner.next() { Some(Err(e)) => Err(GraphError::from_vcsgraph(e)), None => Ok(None), Some(Ok(r)) => Ok(Some(PyRevision(r))), } } } pub fn init_module<'py>( py: Python<'py>, package: &str, ) -> PyResult<Bound<'py, PyModule>> { let m = new_submodule(py, package, "ancestor")?; m.add_class::<AncestorsIterator>()?; Ok(m) }