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.
--- 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();
+ })
+ }
+}