4 use gix_features
::threading
::{get_mut, get_ref, MutableOnDemand, OwnShared}
;
6 /// A structure holding enough information to reload a value if its on-disk representation changes as determined by its modified time.
8 pub struct FileSnapshot
<T
: std
::fmt
::Debug
> {
10 modified
: std
::time
::SystemTime
,
13 impl<T
: Clone
+ std
::fmt
::Debug
> Clone
for FileSnapshot
<T
> {
14 fn clone(&self) -> Self {
16 value
: self.value
.clone(),
17 modified
: self.modified
,
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
>>;
25 /// Use this type for fields in structs that are to store the [`FileSnapshot`], typically behind an [`OwnShared`].
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
>>>);
32 impl<T
: std
::fmt
::Debug
> Deref
for FileSnapshot
<T
> {
35 fn deref(&self) -> &Self::Target
{
40 impl<T
: std
::fmt
::Debug
> Deref
for SharedFileSnapshotMut
<T
> {
41 type Target
= MutableOnDemand
<Option
<SharedFileSnapshot
<T
>>>;
43 fn deref(&self) -> &Self::Target
{
48 impl<T
: std
::fmt
::Debug
> SharedFileSnapshotMut
<T
> {
49 /// Create a new instance of this type.
51 /// Useful in case `Default::default()` isn't working for some reason.
52 pub fn new() -> Self {
53 SharedFileSnapshotMut(MutableOnDemand
::new(None
))
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
>(
60 open
: impl FnOnce() -> Result
<Option
<(std
::time
::SystemTime
, T
)>, E
>,
62 let mut state
= get_mut(&self.0);
63 *state
= open()?
.map(|(modified
, value
)| OwnShared
::new(FileSnapshot { value, modified }
));
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.
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
>(
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(),
84 let mut state
= get_mut(self);
88 (Some(snapshot
), Some(modified_time
)) => {
89 if snapshot
.modified
< modified_time
{
91 let mut state
= get_mut(self);
93 if let (Some(_snapshot
), Some(modified_time
)) = (&*state
, current_modification_time()) {
94 *state
= open()?
.map(|value
| {
95 OwnShared
::new(FileSnapshot
{
97 modified
: modified_time
,
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())
109 (None
, Some(_modified_time
)) => {
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
{
118 modified
: modified_time
,