]> git.proxmox.com Git - rustc.git/blob - vendor/gix-fs/src/snapshot.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / vendor / gix-fs / src / snapshot.rs
1 // TODO: tests
2 use std::ops::Deref;
3
4 use gix_features::threading::{get_mut, get_ref, MutableOnDemand, OwnShared};
5
6 /// A structure holding enough information to reload a value if its on-disk representation changes as determined by its modified time.
7 #[derive(Debug)]
8 pub struct FileSnapshot<T: std::fmt::Debug> {
9 value: T,
10 modified: std::time::SystemTime,
11 }
12
13 impl<T: Clone + std::fmt::Debug> Clone for FileSnapshot<T> {
14 fn clone(&self) -> Self {
15 Self {
16 value: self.value.clone(),
17 modified: self.modified,
18 }
19 }
20 }
21
22 /// A snapshot of a resource which is up-to-date in the moment it is retrieved.
23 pub type SharedFileSnapshot<T> = OwnShared<FileSnapshot<T>>;
24
25 /// Use this type for fields in structs that are to store the [`FileSnapshot`], typically behind an [`OwnShared`].
26 ///
27 /// Note that the resource itself is behind another [`OwnShared`] to allow it to be used without holding any kind of lock, hence
28 /// without blocking updates while it is used.
29 #[derive(Debug, Default)]
30 pub struct SharedFileSnapshotMut<T: std::fmt::Debug>(pub MutableOnDemand<Option<SharedFileSnapshot<T>>>);
31
32 impl<T: std::fmt::Debug> Deref for FileSnapshot<T> {
33 type Target = T;
34
35 fn deref(&self) -> &Self::Target {
36 &self.value
37 }
38 }
39
40 impl<T: std::fmt::Debug> Deref for SharedFileSnapshotMut<T> {
41 type Target = MutableOnDemand<Option<SharedFileSnapshot<T>>>;
42
43 fn deref(&self) -> &Self::Target {
44 &self.0
45 }
46 }
47
48 impl<T: std::fmt::Debug> SharedFileSnapshotMut<T> {
49 /// Create a new instance of this type.
50 ///
51 /// Useful in case `Default::default()` isn't working for some reason.
52 pub fn new() -> Self {
53 SharedFileSnapshotMut(MutableOnDemand::new(None))
54 }
55
56 /// Refresh `state` forcefully by re-`open`ing the resource. Note that `open()` returns `None` if the resource isn't
57 /// present on disk, and that it's critical that the modified time is obtained _before_ opening the resource.
58 pub fn force_refresh<E>(
59 &self,
60 open: impl FnOnce() -> Result<Option<(std::time::SystemTime, T)>, E>,
61 ) -> Result<(), E> {
62 let mut state = get_mut(&self.0);
63 *state = open()?.map(|(modified, value)| OwnShared::new(FileSnapshot { value, modified }));
64 Ok(())
65 }
66
67 /// Assure that the resource in `state` is up-to-date by comparing the `current_modification_time` with the one we know in `state`
68 /// and by acting accordingly.
69 /// Returns the potentially updated/reloaded resource if it is still present on disk, which then represents a snapshot that is up-to-date
70 /// in that very moment, or `None` if the underlying file doesn't exist.
71 ///
72 /// Note that even though this is racy, each time a request is made there is a chance to see the actual state.
73 pub fn recent_snapshot<E>(
74 &self,
75 mut current_modification_time: impl FnMut() -> Option<std::time::SystemTime>,
76 open: impl FnOnce() -> Result<Option<T>, E>,
77 ) -> Result<Option<SharedFileSnapshot<T>>, E> {
78 let state = get_ref(self);
79 let recent_modification = current_modification_time();
80 let buffer = match (&*state, recent_modification) {
81 (None, None) => (*state).clone(),
82 (Some(_), None) => {
83 drop(state);
84 let mut state = get_mut(self);
85 *state = None;
86 (*state).clone()
87 }
88 (Some(snapshot), Some(modified_time)) => {
89 if snapshot.modified < modified_time {
90 drop(state);
91 let mut state = get_mut(self);
92
93 if let (Some(_snapshot), Some(modified_time)) = (&*state, current_modification_time()) {
94 *state = open()?.map(|value| {
95 OwnShared::new(FileSnapshot {
96 value,
97 modified: modified_time,
98 })
99 });
100 }
101
102 (*state).clone()
103 } else {
104 // Note that this relies on sub-section precision or else is a race when the packed file was just changed.
105 // It's nothing we can know though, so… up to the caller unfortunately.
106 Some(snapshot.clone())
107 }
108 }
109 (None, Some(_modified_time)) => {
110 drop(state);
111 let mut state = get_mut(self);
112 // Still in the same situation? If so, load the buffer. This compensates for the trampling herd
113 // during lazy-loading at the expense of another mtime check.
114 if let (None, Some(modified_time)) = (&*state, current_modification_time()) {
115 *state = open()?.map(|value| {
116 OwnShared::new(FileSnapshot {
117 value,
118 modified: modified_time,
119 })
120 });
121 }
122 (*state).clone()
123 }
124 };
125 Ok(buffer)
126 }
127 }