Skip to content

Commit

Permalink
stack: add convenience functions to publish NamedTempFiles
Browse files Browse the repository at this point in the history
Getting ownership over the file also means we can safely handle fsync
failures: bubbling the failure up will also delete the temporary file,
which shouldn't leave any residual clean-but-never-synced page behind.

TESTED=tweaked smoke test.
  • Loading branch information
pkhuong committed Sep 12, 2021
1 parent 8618dc1 commit 2a26142
Showing 1 changed file with 36 additions and 2 deletions.
38 changes: 36 additions & 2 deletions src/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,8 @@ impl Cache {
/// Inserts or overwrites the file at `value` as `key` in the
/// write cache directory. This will always fail with
/// [`ErrorKind::Unsupported`] if no write cache was defined.
/// The path at `value` must be in the same filesystem as the
/// write cache directory: we rely on atomic file renames.
///
/// Fails with [`ErrorKind::InvalidInput`] if `key.name` is invalid
/// (empty, or starts with a dot or a forward or back slash).
Expand Down Expand Up @@ -511,6 +513,20 @@ impl Cache {
doit(self, key.into(), value.as_ref())
}

/// Invokes [`Cache::set`] on a [`tempfile::NamedTempFile`].
///
/// See [`Cache::set`] for more details. The only difference is
/// that `set_temp_file` does not panic when `auto_sync` is enabled
/// and we fail to [`File::sync_all`] the [`NamedTempFile`] value.
pub fn set_temp_file<'a>(&self, key: impl Into<Key<'a>>, value: NamedTempFile) -> Result<()> {
fn doit(this: &Cache, key: Key, value: NamedTempFile) -> Result<()> {
this.maybe_sync(value.as_file())?;
this.set_impl(key, value.path())
}

doit(self, key.into(), value)
}

fn put_impl(&self, key: Key, value: &Path) -> Result<()> {
match self.write_side.as_ref() {
Some(write) => write.put(key, value),
Expand All @@ -525,6 +541,8 @@ impl Cache {
/// there is no such cached entry already, or touches the cached
/// file if it already exists. This will always fail with
/// [`ErrorKind::Unsupported`] if no write cache was defined.
/// The path at `value` must be in the same filesystem as the
/// write cache directory: we rely on atomic file hard linkage.
///
/// Fails with [`ErrorKind::InvalidInput`] if `key.name` is invalid
/// (empty, or starts with a dot or a forward or back slash).
Expand Down Expand Up @@ -552,6 +570,20 @@ impl Cache {
doit(self, key.into(), value.as_ref())
}

/// Invokes [`Cache::put`] on a [`tempfile::NamedTempFile`].
///
/// See [`Cache::put`] for more details. The only difference is
/// that `put_temp_file` does not panic when `auto_sync` is enabled
/// and we fail to [`File::sync_all`] the [`NamedTempFile`] value.
pub fn put_temp_file<'a>(&self, key: impl Into<Key<'a>>, value: NamedTempFile) -> Result<()> {
fn doit(this: &Cache, key: Key, value: NamedTempFile) -> Result<()> {
this.maybe_sync(value.as_file())?;
this.put_impl(key, value.path())
}

doit(self, key.into(), value)
}

/// Marks a cache entry for `key` as accessed (read). The [`Cache`]
/// will touch the same file that would be returned by `get`.
///
Expand Down Expand Up @@ -1107,8 +1139,9 @@ mod test {
tmp.as_file()
.write_all(b"write2")
.expect("write must succeed");
// Exercise put_temp_file as well.
cache
.put(&TestKey::new("b"), tmp.path())
.put_temp_file(&TestKey::new("b"), tmp)
.expect("put must succeed");
}

Expand Down Expand Up @@ -1261,8 +1294,9 @@ mod test {
tmp.as_file()
.write_all(b"write2")
.expect("write must succeed");
// Exercise set_temp_file.
cache
.set(&TestKey::new("b"), tmp.path())
.set_temp_file(&TestKey::new("b"), tmp)
.expect("set must succeed");
}

Expand Down

0 comments on commit 2a26142

Please sign in to comment.