8 |
8 |
9 use crate::{ |
9 use crate::{ |
10 conversion::{rev_pyiter_collect, rev_pyiter_collect_or_else}, |
10 conversion::{rev_pyiter_collect, rev_pyiter_collect_or_else}, |
11 pybytes_deref::{PyBufferDeref, PyBytesDeref}, |
11 pybytes_deref::{PyBufferDeref, PyBytesDeref}, |
12 utils::{node_from_py_bytes, node_from_py_object}, |
12 utils::{node_from_py_bytes, node_from_py_object}, |
13 vfs::PyVfs, |
|
14 PyRevision, |
13 PyRevision, |
15 }; |
14 }; |
16 use cpython::{ |
15 use cpython::{ |
17 buffer::{Element, PyBuffer}, |
16 buffer::{Element, PyBuffer}, |
18 exc::{IndexError, ValueError}, |
17 exc::{IndexError, ValueError}, |
20 PyModule, PyObject, PyResult, PySet, PyTuple, PyType, Python, |
19 PyModule, PyObject, PyResult, PySet, PyTuple, PyType, Python, |
21 PythonObject, ToPyObject, UnsafePyLeaked, |
20 PythonObject, ToPyObject, UnsafePyLeaked, |
22 }; |
21 }; |
23 use hg::{ |
22 use hg::{ |
24 errors::HgError, |
23 errors::HgError, |
|
24 fncache::FnCache, |
25 index::{Phase, RevisionDataParams, SnapshotsCache, INDEX_ENTRY_SIZE}, |
25 index::{Phase, RevisionDataParams, SnapshotsCache, INDEX_ENTRY_SIZE}, |
26 nodemap::{Block, NodeMapError, NodeTree as CoreNodeTree}, |
26 nodemap::{Block, NodeMapError, NodeTree as CoreNodeTree}, |
27 revlog::compression::CompressionConfig, |
27 revlog::{ |
28 revlog::inner_revlog::InnerRevlog as CoreInnerRevlog, |
28 compression::CompressionConfig, |
29 revlog::inner_revlog::RevisionBuffer, |
29 inner_revlog::{InnerRevlog as CoreInnerRevlog, RevisionBuffer}, |
30 revlog::options::{ |
30 nodemap::NodeMap, |
31 RevlogDataConfig, RevlogDeltaConfig, RevlogFeatureConfig, |
31 options::{ |
32 RevlogOpenOptions, |
32 RevlogDataConfig, RevlogDeltaConfig, RevlogFeatureConfig, |
|
33 RevlogOpenOptions, |
|
34 }, |
|
35 Graph, NodePrefix, RevlogError, RevlogIndex, |
33 }, |
36 }, |
34 revlog::{nodemap::NodeMap, Graph, NodePrefix, RevlogError, RevlogIndex}, |
|
35 transaction::Transaction, |
37 transaction::Transaction, |
36 utils::files::{get_bytes_from_path, get_path_from_bytes}, |
38 utils::files::{get_bytes_from_path, get_path_from_bytes}, |
|
39 vfs::FnCacheVfs, |
37 BaseRevision, Node, Revision, RevlogType, UncheckedRevision, |
40 BaseRevision, Node, Revision, RevlogType, UncheckedRevision, |
38 NULL_REVISION, |
41 NULL_REVISION, |
39 }; |
42 }; |
40 use std::{ |
43 use std::{ |
41 cell::{Cell, RefCell}, |
44 cell::{Cell, RefCell}, |
42 collections::{HashMap, HashSet}, |
45 collections::{HashMap, HashSet}, |
|
46 sync::atomic::{AtomicBool, Ordering}, |
43 sync::OnceLock, |
47 sync::OnceLock, |
44 }; |
48 }; |
45 use vcsgraph::graph::Graph as VCSGraph; |
49 use vcsgraph::graph::Graph as VCSGraph; |
46 |
50 |
47 pub struct PySharedIndex { |
51 pub struct PySharedIndex { |
645 .exit_writing_context(); |
649 .exit_writing_context(); |
646 Ok(py.None()) |
650 Ok(py.None()) |
647 } |
651 } |
648 }); |
652 }); |
649 |
653 |
|
654 struct PyFnCache { |
|
655 fncache: PyObject, |
|
656 } |
|
657 impl PyFnCache { |
|
658 fn new(fncache: PyObject) -> Self { |
|
659 Self { fncache } |
|
660 } |
|
661 } |
|
662 |
|
663 impl Clone for PyFnCache { |
|
664 fn clone(&self) -> Self { |
|
665 let gil = Python::acquire_gil(); |
|
666 let py = gil.python(); |
|
667 Self { |
|
668 fncache: self.fncache.clone_ref(py), |
|
669 } |
|
670 } |
|
671 } |
|
672 |
|
673 /// Cache whether the fncache is loaded to avoid Python round-trip every time. |
|
674 /// Once the fncache is loaded, it stays loaded unless we're in a very |
|
675 /// long-running process, none of which we actually support for now. |
|
676 static FN_CACHE_IS_LOADED: AtomicBool = AtomicBool::new(false); |
|
677 |
|
678 impl FnCache for PyFnCache { |
|
679 fn is_loaded(&self) -> bool { |
|
680 if FN_CACHE_IS_LOADED.load(Ordering::Relaxed) { |
|
681 return true; |
|
682 } |
|
683 let gil = Python::acquire_gil(); |
|
684 let py = gil.python(); |
|
685 // TODO raise in case of error? |
|
686 let is_loaded = self |
|
687 .fncache |
|
688 .getattr(py, "is_loaded") |
|
689 .ok() |
|
690 .map(|o| { |
|
691 o.extract::<bool>(py) |
|
692 .expect("is_loaded returned something other than a bool") |
|
693 }) |
|
694 .unwrap_or(false); |
|
695 if is_loaded { |
|
696 FN_CACHE_IS_LOADED.store(true, Ordering::Relaxed); |
|
697 } |
|
698 is_loaded |
|
699 } |
|
700 fn add(&self, path: &std::path::Path) { |
|
701 let gil = Python::acquire_gil(); |
|
702 let py = gil.python(); |
|
703 // TODO raise in case of error? |
|
704 self.fncache |
|
705 .call_method( |
|
706 py, |
|
707 "add", |
|
708 (PyBytes::new(py, &get_bytes_from_path(path)),), |
|
709 None, |
|
710 ) |
|
711 .ok(); |
|
712 } |
|
713 } |
|
714 |
650 py_class!(pub class InnerRevlog |py| { |
715 py_class!(pub class InnerRevlog |py| { |
651 @shared data inner: CoreInnerRevlog; |
716 @shared data inner: CoreInnerRevlog; |
652 data nt: RefCell<Option<CoreNodeTree>>; |
717 data nt: RefCell<Option<CoreNodeTree>>; |
653 data docket: RefCell<Option<PyObject>>; |
718 data docket: RefCell<Option<PyObject>>; |
654 // Holds a reference to the mmap'ed persistent nodemap data |
719 // Holds a reference to the mmap'ed persistent nodemap data |
1885 feature_config: PyObject, |
1956 feature_config: PyObject, |
1886 _chunk_cache: PyObject, |
1957 _chunk_cache: PyObject, |
1887 _default_compression_header: PyObject, |
1958 _default_compression_header: PyObject, |
1888 revlog_type: usize, |
1959 revlog_type: usize, |
1889 ) -> PyResult<Self> { |
1960 ) -> PyResult<Self> { |
1890 let vfs = Box::new(PyVfs::new(py, opener)?); |
|
1891 let index_file = |
1961 let index_file = |
1892 get_path_from_bytes(index_file.extract::<PyBytes>(py)?.data(py)) |
1962 get_path_from_bytes(index_file.extract::<PyBytes>(py)?.data(py)) |
1893 .to_owned(); |
1963 .to_owned(); |
1894 let data_file = |
1964 let data_file = |
1895 get_path_from_bytes(data_file.extract::<PyBytes>(py)?.data(py)) |
1965 get_path_from_bytes(data_file.extract::<PyBytes>(py)?.data(py)) |
1905 inline, |
1975 inline, |
1906 data_config, |
1976 data_config, |
1907 delta_config, |
1977 delta_config, |
1908 feature_config, |
1978 feature_config, |
1909 ); |
1979 ); |
|
1980 |
1910 // Safety: we keep the buffer around inside the class as `index_mmap` |
1981 // Safety: we keep the buffer around inside the class as `index_mmap` |
1911 let (buf, bytes) = unsafe { mmap_keeparound(py, index_data)? }; |
1982 let (buf, bytes) = unsafe { mmap_keeparound(py, index_data)? }; |
1912 let index = hg::index::Index::new(bytes, options.index_header()) |
1983 let index = hg::index::Index::new(bytes, options.index_header()) |
1913 .map_err(|e| revlog_error_from_msg(py, e))?; |
1984 .map_err(|e| revlog_error_from_msg(py, e))?; |
|
1985 |
|
1986 let base = &vfs_base.extract::<PyBytes>(py)?; |
|
1987 let base = get_path_from_bytes(base.data(py)).to_owned(); |
1914 let core = CoreInnerRevlog::new( |
1988 let core = CoreInnerRevlog::new( |
1915 vfs, |
1989 Box::new(FnCacheVfs::new( |
|
1990 base, |
|
1991 vfs_is_readonly, |
|
1992 Box::new(PyFnCache::new(fncache)), |
|
1993 )), |
1916 index, |
1994 index, |
1917 index_file, |
1995 index_file, |
1918 data_file, |
1996 data_file, |
1919 data_config, |
1997 data_config, |
1920 delta_config, |
1998 delta_config, |