changeset 52776:6fa23eed335b

rust-pyo3: PyFnCache This is a translitteration of the `PyFnCache` from `hg-cpython/src/revlog.rs`. To match the Python code organization, we put it its own top-level Rust module. Our immediate use case is for the `revlog` module, but we believe it will eventually be useful elsewhere.
author Georges Racinet <georges.racinet@cloudcrane.io>
date Tue, 24 Dec 2024 18:16:38 +0100
parents 264047bf4b9b
children 32008b1e7104
files rust/hg-pyo3/src/lib.rs rust/hg-pyo3/src/store.rs
diffstat 2 files changed, 78 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/rust/hg-pyo3/src/lib.rs	Tue Dec 24 18:02:11 2024 +0100
+++ b/rust/hg-pyo3/src/lib.rs	Tue Dec 24 18:16:38 2024 +0100
@@ -6,6 +6,7 @@
 mod exceptions;
 mod revision;
 mod revlog;
+mod store;
 mod util;
 
 #[pymodule]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-pyo3/src/store.rs	Tue Dec 24 18:16:38 2024 +0100
@@ -0,0 +1,77 @@
+// store.rs
+//
+// Copyright 2020-2024 Raphaël Gomès <raphael.gomes@octobus.net>
+//           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.
+#![allow(unused)] // for now
+use pyo3::prelude::*;
+use pyo3::types::PyBytes;
+
+use std::sync::atomic::{AtomicBool, Ordering};
+
+use hg::{fncache::FnCache, utils::files::get_bytes_from_path};
+
+pub struct PyFnCache {
+    fncache: PyObject,
+}
+
+impl PyFnCache {
+    pub fn new(fncache: PyObject) -> Self {
+        Self { fncache }
+    }
+}
+
+impl Clone for PyFnCache {
+    fn clone(&self) -> Self {
+        Python::with_gil(|py| 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);
+
+// TODO perhaps a bit of magic with `Bound<'_, PyFnCache>` would spare us
+// the GIL reacquisitions
+impl FnCache for PyFnCache {
+    fn is_loaded(&self) -> bool {
+        if FN_CACHE_IS_LOADED.load(Ordering::Relaxed) {
+            return true;
+        }
+        Python::with_gil(|py| {
+            // TODO raise in case of error?
+            let is_loaded = self
+                .fncache
+                .getattr(py, "is_loaded")
+                .ok()
+                .map(|o| {
+                    o.extract::<bool>(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) {
+        Python::with_gil(|py| {
+            // TODO raise in case of error?
+            self.fncache
+                .call_method(
+                    py,
+                    "add",
+                    (PyBytes::new(py, &get_bytes_from_path(path)),),
+                    None,
+                )
+                .ok();
+        })
+    }
+}