use anyhow as ah;
use std::fs::File;
use std::path::Path;
#[cfg(target_os="linux")]
fn os_drop_file_caches(file: File,
_path: &Path,
offset: u64,
size: u64) -> ah::Result<()> {
use libc::{posix_fadvise, POSIX_FADV_DONTNEED, off_t};
use std::fs::OpenOptions;
use std::io::Write;
use std::os::unix::io::AsRawFd;
file.sync_all().ok();
let ret = unsafe { posix_fadvise(file.as_raw_fd(),
offset as off_t,
size as off_t,
POSIX_FADV_DONTNEED) };
if ret == 0 {
Ok(())
} else {
drop(file);
let proc_file = "/proc/sys/vm/drop_caches";
let proc_value = "3\n";
match OpenOptions::new().write(true).open(proc_file) {
Ok(mut file) => {
match file.write_all(proc_value.as_bytes()) {
Ok(_) => Ok(()),
Err(e) => Err(ah::format_err!("{}", e)),
}
},
Err(e) => Err(ah::format_err!("{}", e)),
}
}
}
#[cfg(target_os="windows")]
fn os_drop_file_caches(file: File,
path: &Path,
_offset: u64,
_size: u64) -> ah::Result<()> {
use winapi::um::{
fileapi::{CreateFileA, OPEN_EXISTING},
handleapi::{CloseHandle, INVALID_HANDLE_VALUE},
winbase::FILE_FLAG_NO_BUFFERING,
winnt::{GENERIC_READ, FILE_SHARE_READ},
};
use std::{
ptr::null_mut,
ffi::CString,
};
file.sync_all().ok();
drop(file);
if let Some(path) = path.to_str() {
if let Ok(path) = CString::new(path) {
let h = unsafe { CreateFileA(path.as_ptr(),
GENERIC_READ,
FILE_SHARE_READ,
null_mut(),
OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING,
null_mut()) };
if h == INVALID_HANDLE_VALUE {
Err(ah::format_err!("Failed to acquire file handle."))
} else {
unsafe { CloseHandle(h) };
Ok(())
}
} else {
Err(ah::format_err!("Failed to convert file name (CString)."))
}
} else {
Err(ah::format_err!("Failed to convert file name (str)."))
}
}
#[cfg(not(any(target_os="linux", target_os="windows")))]
fn os_drop_file_caches(_file: File,
_path: &Path,
_offset: u64,
_size: u64) -> ah::Result<()> {
Err(ah::format_err!("Not supported on this operating system."))
}
pub fn drop_file_caches(file: File,
path: &Path,
offset: u64,
size: u64) -> ah::Result<()> {
os_drop_file_caches(file, path, offset, size)
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
use std::fs::File;
use std::io::Write;
#[test]
fn test_drop_file_caches() {
let tdir = tempdir().unwrap();
let path = tdir.path().join("test_drop_file_caches");
let mut file = File::create(&path).unwrap();
file.write_all(&[42u8; 4096]).unwrap();
drop_file_caches(file, &path, 0, 4096).unwrap();
}
}