Skip to content

Commit 6d30363

Browse files
committed
Simplify control flow in env_universal_common::save()
1 parent 58a6eb6 commit 6d30363

File tree

2 files changed

+57
-58
lines changed

2 files changed

+57
-58
lines changed

src/common.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1814,6 +1814,12 @@ impl<T, F: FnOnce(&mut T)> ScopeGuard<T, F> {
18141814
on_drop(&mut value);
18151815
value
18161816
}
1817+
1818+
/// Cancels the invocation of the callback, returning the original wrapped value.
1819+
pub fn cancel(mut guard: Self) -> T {
1820+
let (value, _) = guard.0.take().expect("Should always have Some value");
1821+
value
1822+
}
18171823
}
18181824

18191825
impl<T, F: FnOnce(&mut T)> Deref for ScopeGuard<T, F> {

src/env_universal_common.rs

Lines changed: 51 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -748,82 +748,75 @@ impl EnvUniversal {
748748
// Write our file contents.
749749
// \return true on success, false on failure.
750750
fn save(&mut self, directory: &wstr) -> bool {
751+
use crate::common::ScopeGuard;
751752
assert!(self.ok_to_save, "It's not OK to save");
752753

753-
let mut private_file_path = WString::new();
754-
755754
// Open adjacent temporary file.
755+
let mut private_file_path = WString::new();
756756
let private_fd = self.open_temporary_file(directory, &mut private_file_path);
757+
// unlink pfp upon failure. In case of success, it (already) won't exist.
758+
let delete_pfp = ScopeGuard::new(private_file_path, |path| {
759+
wunlink(path);
760+
});
761+
let private_file_path = &delete_pfp;
757762

758763
// Write to it.
759-
let mut success = self.write_to_fd(private_fd.as_raw_fd(), &private_file_path);
760-
if !success {
764+
if !self.write_to_fd(private_fd.as_raw_fd(), &private_file_path) {
761765
FLOG!(uvar_file, "universal log write_to_fd() failed");
766+
return false;
762767
}
763768

764-
if success {
765-
let real_path = wrealpath(&self.vars_path).unwrap_or_else(|| self.vars_path.clone());
769+
let real_path = wrealpath(&self.vars_path).unwrap_or_else(|| self.vars_path.clone());
766770

767-
// Ensure we maintain ownership and permissions (#2176).
768-
// let mut sbuf : libc::stat = MaybeUninit::uninit();
769-
if let Ok(md) = wstat(&real_path) {
770-
if unsafe { libc::fchown(private_fd.as_raw_fd(), md.uid(), md.gid()) } == -1 {
771-
FLOG!(uvar_file, "universal log fchown() failed");
772-
}
773-
#[allow(clippy::useless_conversion)]
774-
let mode: libc::mode_t = md.mode().try_into().unwrap();
775-
if unsafe { libc::fchmod(private_fd.as_raw_fd(), mode) } == -1 {
776-
FLOG!(uvar_file, "universal log fchmod() failed");
777-
}
771+
// Ensure we maintain ownership and permissions (#2176).
772+
// let mut sbuf : libc::stat = MaybeUninit::uninit();
773+
if let Ok(md) = wstat(&real_path) {
774+
if unsafe { libc::fchown(private_fd.as_raw_fd(), md.uid(), md.gid()) } == -1 {
775+
FLOG!(uvar_file, "universal log fchown() failed");
778776
}
779-
780-
// Linux by default stores the mtime with low precision, low enough that updates that occur
781-
// in quick succession may result in the same mtime (even the nanoseconds field). So
782-
// manually set the mtime of the new file to a high-precision clock. Note that this is only
783-
// necessary because Linux aggressively reuses inodes, causing the ABA problem; on other
784-
// platforms we tend to notice the file has changed due to a different inode (or file size!)
785-
//
786-
// The current time within the Linux kernel is cached, and generally only updated on a timer
787-
// interrupt. So if the timer interrupt is running at 10 milliseconds, the cached time will
788-
// only be updated once every 10 milliseconds.
789-
//
790-
// It's probably worth finding a simpler solution to this. The tests ran into this, but it's
791-
// unlikely to affect users.
792-
#[cfg(any(target_os = "linux", target_os = "android"))]
793-
{
794-
let mut times: [libc::timespec; 2] = unsafe { std::mem::zeroed() };
795-
times[0].tv_nsec = libc::UTIME_OMIT; // don't change ctime
796-
if unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut times[1]) } != 0 {
797-
unsafe {
798-
libc::futimens(private_fd.as_raw_fd(), &times[0]);
799-
}
800-
}
777+
#[allow(clippy::useless_conversion)]
778+
let mode: libc::mode_t = md.mode().try_into().unwrap();
779+
if unsafe { libc::fchmod(private_fd.as_raw_fd(), mode) } == -1 {
780+
FLOG!(uvar_file, "universal log fchmod() failed");
801781
}
782+
}
802783

803-
// Apply new file.
804-
success = self.move_new_vars_file_into_place(&private_file_path, &real_path);
805-
if !success {
806-
FLOG!(
807-
uvar_file,
808-
"universal log move_new_vars_file_into_place() failed"
809-
);
784+
// Linux by default stores the mtime with low precision, low enough that updates that occur
785+
// in quick succession may result in the same mtime (even the nanoseconds field). So
786+
// manually set the mtime of the new file to a high-precision clock. Note that this is only
787+
// necessary because Linux aggressively reuses inodes, causing the ABA problem; on other
788+
// platforms we tend to notice the file has changed due to a different inode (or file size!)
789+
//
790+
// The current time within the Linux kernel is cached, and generally only updated on a timer
791+
// interrupt. So if the timer interrupt is running at 10 milliseconds, the cached time will
792+
// only be updated once every 10 milliseconds.
793+
//
794+
// It's probably worth finding a simpler solution to this. The tests ran into this, but it's
795+
// unlikely to affect users.
796+
#[cfg(any(target_os = "linux", target_os = "android"))]
797+
{
798+
let mut times: [libc::timespec; 2] = unsafe { std::mem::zeroed() };
799+
times[0].tv_nsec = libc::UTIME_OMIT; // don't change ctime
800+
if unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut times[1]) } != 0 {
801+
unsafe {
802+
libc::futimens(private_fd.as_raw_fd(), &times[0]);
803+
}
810804
}
811805
}
812806

813-
if success {
814-
// Since we moved the new file into place, clear the path so we don't try to unlink it.
815-
private_file_path.clear();
807+
// Apply new file.
808+
if !self.move_new_vars_file_into_place(&private_file_path, &real_path) {
809+
FLOG!(
810+
uvar_file,
811+
"universal log move_new_vars_file_into_place() failed"
812+
);
813+
return false;
816814
}
817815

818-
// Clean up.
819-
if !private_file_path.is_empty() {
820-
wunlink(&private_file_path);
821-
}
822-
if success {
823-
// All of our modified variables have now been written out.
824-
self.modified.clear();
825-
}
826-
success
816+
// Success at last. All of our modified variables have now been written out.
817+
self.modified.clear();
818+
ScopeGuard::cancel(delete_pfp);
819+
true
827820
}
828821
}
829822

0 commit comments

Comments
 (0)