summaryrefslogtreecommitdiffstats
path: root/vendor/gix-fs/src/snapshot.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:42 +0000
commit837b550238aa671a591ccf282dddeab29cadb206 (patch)
tree914b6b8862bace72bd3245ca184d374b08d8a672 /vendor/gix-fs/src/snapshot.rs
parentAdding debian version 1.70.0+dfsg2-1. (diff)
downloadrustc-837b550238aa671a591ccf282dddeab29cadb206.tar.xz
rustc-837b550238aa671a591ccf282dddeab29cadb206.zip
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-fs/src/snapshot.rs')
-rw-r--r--vendor/gix-fs/src/snapshot.rs127
1 files changed, 127 insertions, 0 deletions
diff --git a/vendor/gix-fs/src/snapshot.rs b/vendor/gix-fs/src/snapshot.rs
new file mode 100644
index 000000000..02a0ec843
--- /dev/null
+++ b/vendor/gix-fs/src/snapshot.rs
@@ -0,0 +1,127 @@
+// TODO: tests
+use std::ops::Deref;
+
+use gix_features::threading::{get_mut, get_ref, MutableOnDemand, OwnShared};
+
+/// A structure holding enough information to reload a value if its on-disk representation changes as determined by its modified time.
+#[derive(Debug)]
+pub struct FileSnapshot<T: std::fmt::Debug> {
+ value: T,
+ modified: std::time::SystemTime,
+}
+
+impl<T: Clone + std::fmt::Debug> Clone for FileSnapshot<T> {
+ fn clone(&self) -> Self {
+ Self {
+ value: self.value.clone(),
+ modified: self.modified,
+ }
+ }
+}
+
+/// A snapshot of a resource which is up-to-date in the moment it is retrieved.
+pub type SharedFileSnapshot<T> = OwnShared<FileSnapshot<T>>;
+
+/// Use this type for fields in structs that are to store the [`FileSnapshot`], typically behind an [`OwnShared`].
+///
+/// Note that the resource itself is behind another [`OwnShared`] to allow it to be used without holding any kind of lock, hence
+/// without blocking updates while it is used.
+#[derive(Debug, Default)]
+pub struct SharedFileSnapshotMut<T: std::fmt::Debug>(pub MutableOnDemand<Option<SharedFileSnapshot<T>>>);
+
+impl<T: std::fmt::Debug> Deref for FileSnapshot<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.value
+ }
+}
+
+impl<T: std::fmt::Debug> Deref for SharedFileSnapshotMut<T> {
+ type Target = MutableOnDemand<Option<SharedFileSnapshot<T>>>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl<T: std::fmt::Debug> SharedFileSnapshotMut<T> {
+ /// Create a new instance of this type.
+ ///
+ /// Useful in case `Default::default()` isn't working for some reason.
+ pub fn new() -> Self {
+ SharedFileSnapshotMut(MutableOnDemand::new(None))
+ }
+
+ /// Refresh `state` forcefully by re-`open`ing the resource. Note that `open()` returns `None` if the resource isn't
+ /// present on disk, and that it's critical that the modified time is obtained _before_ opening the resource.
+ pub fn force_refresh<E>(
+ &self,
+ open: impl FnOnce() -> Result<Option<(std::time::SystemTime, T)>, E>,
+ ) -> Result<(), E> {
+ let mut state = get_mut(&self.0);
+ *state = open()?.map(|(modified, value)| OwnShared::new(FileSnapshot { value, modified }));
+ Ok(())
+ }
+
+ /// Assure that the resource in `state` is up-to-date by comparing the `current_modification_time` with the one we know in `state`
+ /// and by acting accordingly.
+ /// Returns the potentially updated/reloaded resource if it is still present on disk, which then represents a snapshot that is up-to-date
+ /// in that very moment, or `None` if the underlying file doesn't exist.
+ ///
+ /// Note that even though this is racy, each time a request is made there is a chance to see the actual state.
+ pub fn recent_snapshot<E>(
+ &self,
+ mut current_modification_time: impl FnMut() -> Option<std::time::SystemTime>,
+ open: impl FnOnce() -> Result<Option<T>, E>,
+ ) -> Result<Option<SharedFileSnapshot<T>>, E> {
+ let state = get_ref(self);
+ let recent_modification = current_modification_time();
+ let buffer = match (&*state, recent_modification) {
+ (None, None) => (*state).clone(),
+ (Some(_), None) => {
+ drop(state);
+ let mut state = get_mut(self);
+ *state = None;
+ (*state).clone()
+ }
+ (Some(snapshot), Some(modified_time)) => {
+ if snapshot.modified < modified_time {
+ drop(state);
+ let mut state = get_mut(self);
+
+ if let (Some(_snapshot), Some(modified_time)) = (&*state, current_modification_time()) {
+ *state = open()?.map(|value| {
+ OwnShared::new(FileSnapshot {
+ value,
+ modified: modified_time,
+ })
+ });
+ }
+
+ (*state).clone()
+ } else {
+ // Note that this relies on sub-section precision or else is a race when the packed file was just changed.
+ // It's nothing we can know though, so… up to the caller unfortunately.
+ Some(snapshot.clone())
+ }
+ }
+ (None, Some(_modified_time)) => {
+ drop(state);
+ let mut state = get_mut(self);
+ // Still in the same situation? If so, load the buffer. This compensates for the trampling herd
+ // during lazy-loading at the expense of another mtime check.
+ if let (None, Some(modified_time)) = (&*state, current_modification_time()) {
+ *state = open()?.map(|value| {
+ OwnShared::new(FileSnapshot {
+ value,
+ modified: modified_time,
+ })
+ });
+ }
+ (*state).clone()
+ }
+ };
+ Ok(buffer)
+ }
+}