diff -r bd43465af568 -r 72bc29f01570 rust/hg-cpython/src/revlog.rs --- a/rust/hg-cpython/src/revlog.rs Mon Jul 29 20:35:44 2024 +0200 +++ b/rust/hg-cpython/src/revlog.rs Mon Jul 29 20:39:34 2024 +0200 @@ -10,7 +10,6 @@ conversion::{rev_pyiter_collect, rev_pyiter_collect_or_else}, pybytes_deref::{PyBufferDeref, PyBytesDeref}, utils::{node_from_py_bytes, node_from_py_object}, - vfs::PyVfs, PyRevision, }; use cpython::{ @@ -22,24 +21,29 @@ }; use hg::{ errors::HgError, + fncache::FnCache, index::{Phase, RevisionDataParams, SnapshotsCache, INDEX_ENTRY_SIZE}, nodemap::{Block, NodeMapError, NodeTree as CoreNodeTree}, - revlog::compression::CompressionConfig, - revlog::inner_revlog::InnerRevlog as CoreInnerRevlog, - revlog::inner_revlog::RevisionBuffer, - revlog::options::{ - RevlogDataConfig, RevlogDeltaConfig, RevlogFeatureConfig, - RevlogOpenOptions, + revlog::{ + compression::CompressionConfig, + inner_revlog::{InnerRevlog as CoreInnerRevlog, RevisionBuffer}, + nodemap::NodeMap, + options::{ + RevlogDataConfig, RevlogDeltaConfig, RevlogFeatureConfig, + RevlogOpenOptions, + }, + Graph, NodePrefix, RevlogError, RevlogIndex, }, - revlog::{nodemap::NodeMap, Graph, NodePrefix, RevlogError, RevlogIndex}, transaction::Transaction, utils::files::{get_bytes_from_path, get_path_from_bytes}, + vfs::FnCacheVfs, BaseRevision, Node, Revision, RevlogType, UncheckedRevision, NULL_REVISION, }; use std::{ cell::{Cell, RefCell}, collections::{HashMap, HashSet}, + sync::atomic::{AtomicBool, Ordering}, sync::OnceLock, }; use vcsgraph::graph::Graph as VCSGraph; @@ -647,6 +651,67 @@ } }); +struct PyFnCache { + fncache: PyObject, +} +impl PyFnCache { + fn new(fncache: PyObject) -> Self { + Self { fncache } + } +} + +impl Clone for PyFnCache { + fn clone(&self) -> Self { + let gil = Python::acquire_gil(); + let py = gil.python(); + Self { + fncache: self.fncache.clone_ref(py), + } + } +} + +/// Cache whether the fncache is loaded to avoid Python round-trip every time. +/// Once the fncache is loaded, it stays loaded unless we're in a very +/// long-running process, none of which we actually support for now. +static FN_CACHE_IS_LOADED: AtomicBool = AtomicBool::new(false); + +impl FnCache for PyFnCache { + fn is_loaded(&self) -> bool { + if FN_CACHE_IS_LOADED.load(Ordering::Relaxed) { + return true; + } + let gil = Python::acquire_gil(); + let py = gil.python(); + // TODO raise in case of error? + let is_loaded = self + .fncache + .getattr(py, "is_loaded") + .ok() + .map(|o| { + o.extract::(py) + .expect("is_loaded returned something other than a bool") + }) + .unwrap_or(false); + if is_loaded { + FN_CACHE_IS_LOADED.store(true, Ordering::Relaxed); + } + is_loaded + } + fn add(&self, path: &std::path::Path) { + let gil = Python::acquire_gil(); + let py = gil.python(); + // TODO raise in case of error? + self.fncache + .call_method( + py, + "add", + (PyBytes::new(py, &get_bytes_from_path(path)),), + None, + ) + .ok(); + } +} + py_class!(pub class InnerRevlog |py| { @shared data inner: CoreInnerRevlog; data nt: RefCell>; @@ -661,7 +726,9 @@ def __new__( _cls, - opener: PyObject, + vfs_base: PyObject, + fncache: PyObject, + vfs_is_readonly: bool, index_data: PyObject, index_file: PyObject, data_file: PyObject, @@ -676,7 +743,9 @@ ) -> PyResult { Self::inner_new( py, - opener, + vfs_base, + fncache, + vfs_is_readonly, index_data, index_file, data_file, @@ -1874,7 +1943,9 @@ impl InnerRevlog { pub fn inner_new( py: Python, - opener: PyObject, + vfs_base: PyObject, + fncache: PyObject, + vfs_is_readonly: bool, index_data: PyObject, index_file: PyObject, data_file: PyObject, @@ -1887,7 +1958,6 @@ _default_compression_header: PyObject, revlog_type: usize, ) -> PyResult { - let vfs = Box::new(PyVfs::new(py, opener)?); let index_file = get_path_from_bytes(index_file.extract::(py)?.data(py)) .to_owned(); @@ -1907,12 +1977,20 @@ delta_config, feature_config, ); + // Safety: we keep the buffer around inside the class as `index_mmap` let (buf, bytes) = unsafe { mmap_keeparound(py, index_data)? }; let index = hg::index::Index::new(bytes, options.index_header()) .map_err(|e| revlog_error_from_msg(py, e))?; + + let base = &vfs_base.extract::(py)?; + let base = get_path_from_bytes(base.data(py)).to_owned(); let core = CoreInnerRevlog::new( - vfs, + Box::new(FnCacheVfs::new( + base, + vfs_is_readonly, + Box::new(PyFnCache::new(fncache)), + )), index, index_file, data_file,