use super::*;
pub(in crate::daemon::server) fn write_cached_output(
out_path: &Path,
cache_file: &Path,
data: &[u8],
) -> std::io::Result<()> {
if std::fs::hard_link(cache_file, out_path).is_ok() {
touch_mtime(out_path);
return Ok(());
}
if same_file(out_path, cache_file) {
touch_mtime(out_path);
return Ok(());
}
let _ = std::fs::remove_file(out_path);
if std::fs::hard_link(cache_file, out_path).is_ok() {
touch_mtime(out_path);
return Ok(());
}
std::fs::write(out_path, data)
}
pub(in crate::daemon::server) fn write_cached_file(
out_path: &Path,
cache_file: &Path,
) -> std::io::Result<()> {
if std::fs::hard_link(cache_file, out_path).is_ok() {
touch_mtime(out_path);
return Ok(());
}
if same_file(out_path, cache_file) {
touch_mtime(out_path);
return Ok(());
}
let _ = std::fs::remove_file(out_path);
if std::fs::hard_link(cache_file, out_path).is_ok() {
touch_mtime(out_path);
return Ok(());
}
std::fs::copy(cache_file, out_path)?;
touch_mtime(out_path);
Ok(())
}
pub(in crate::daemon::server) fn write_cached_payload(
out_path: &Path,
cache_file: &Path,
payload: &CachedPayload,
) -> std::io::Result<()> {
match payload {
CachedPayload::Bytes(data) => write_cached_output(out_path, cache_file, data),
CachedPayload::File(path) => write_cached_file(out_path, path),
CachedPayload::PendingFile { source_path } => {
if cache_file.exists() {
write_cached_file(out_path, cache_file)
} else {
write_cached_file(out_path, source_path.as_path())
}
}
}
}
pub(in crate::daemon::server) const PAR_WRITE_THRESHOLD: usize = 4;
pub(in crate::daemon::server) fn write_payloads_par<P, Q>(
targets: &[(P, Q)],
payloads: &[CachedPayload],
) -> bool
where
P: AsRef<Path> + Sync,
Q: AsRef<Path> + Sync,
{
debug_assert_eq!(targets.len(), payloads.len());
let write_one = |out: &Path, cache: &Path, payload: &CachedPayload| -> bool {
if let Some(parent) = out.parent() {
let _ = std::fs::create_dir_all(parent);
}
write_cached_payload(out, cache, payload).is_ok()
};
if targets.len() < PAR_WRITE_THRESHOLD {
return targets
.iter()
.zip(payloads.iter())
.all(|((out, cache), payload)| write_one(out.as_ref(), cache.as_ref(), payload));
}
use rayon::prelude::*;
targets
.par_iter()
.zip(payloads.par_iter())
.all(|((out, cache), payload)| write_one(out.as_ref(), cache.as_ref(), payload))
}
pub(in crate::daemon::server) fn write_payloads_par_with_mtime_floor<P, Q, R>(
targets: &[(P, Q)],
payloads: &[CachedPayload],
floor_paths: &[R],
) -> bool
where
P: AsRef<Path> + Sync,
Q: AsRef<Path> + Sync,
R: AsRef<Path>,
{
if !write_payloads_par(targets, payloads) {
return false;
}
let batch_floor = std::time::SystemTime::now();
floor_materialized_outputs_to_input_max(
targets.iter().map(|(out, _)| out.as_ref()),
floor_paths.iter().map(|path| path.as_ref()),
batch_floor,
);
true
}