diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:42 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:42 +0000 |
commit | 837b550238aa671a591ccf282dddeab29cadb206 (patch) | |
tree | 914b6b8862bace72bd3245ca184d374b08d8a672 /vendor/gix-fs/src/snapshot.rs | |
parent | Adding debian version 1.70.0+dfsg2-1. (diff) | |
download | rustc-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.rs | 127 |
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) + } +} |