use crate::lockfree::OnceSlot;
use std::future::Future;
use std::io;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::sync::{Arc, Mutex, OnceLock, mpsc};
use std::task::{Context, Poll};
type Job = Box<dyn FnOnce() + Send + 'static>;
#[repr(align(64))]
struct FsPool {
sender: mpsc::Sender<Job>,
}
#[repr(align(64))]
static FS_POOL: OnceLock<FsPool> = OnceLock::new();
pub fn init(workers: usize) {
FS_POOL.get_or_init(|| {
let (tx, rx) = mpsc::channel::<Job>();
let rx = Arc::new(Mutex::new(rx));
for _ in 0..workers.max(1) {
let rx = Arc::clone(&rx);
std::thread::Builder::new()
.name("dtact-fs-worker".into())
.spawn(move || {
loop {
let job = { rx.lock().unwrap().recv() };
match job {
Ok(job) => job(),
Err(_) => break,
}
}
})
.expect("failed to spawn dtact-fs worker thread");
}
FsPool { sender: tx }
});
}
pub fn init_fs(
workers: usize,
_ring_depth: u32,
_buffer_pool_size: usize,
_chunk_size: usize,
_pin_cpus: &[usize],
) {
init(workers);
}
pub struct BlockingOp<T> {
slot: Arc<OnceSlot<T>>,
}
impl<T: Send + 'static> Future for BlockingOp<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
self.slot.poll(cx)
}
}
fn spawn_blocking<T, F>(f: F) -> BlockingOp<T>
where
T: Send + 'static,
F: FnOnce() -> T + Send + 'static,
{
if FS_POOL.get().is_none() {
init(4);
}
let slot = Arc::new(OnceSlot::new());
let slot2 = Arc::clone(&slot);
let job: Job = Box::new(move || {
let result = f();
slot2.set(result);
});
let _ = FS_POOL.get().unwrap().sender.send(job);
BlockingOp { slot }
}
pub struct DtactFile {
inner: Arc<Mutex<Option<std::fs::File>>>,
}
impl DtactFile {
pub async fn open(path: impl Into<PathBuf>) -> io::Result<Self> {
let path = path.into();
let file = spawn_blocking(move || std::fs::File::open(&path)).await?;
Ok(Self {
inner: Arc::new(Mutex::new(Some(file))),
})
}
pub async fn create(path: impl Into<PathBuf>) -> io::Result<Self> {
let path = path.into();
let file = spawn_blocking(move || std::fs::File::create(&path)).await?;
Ok(Self {
inner: Arc::new(Mutex::new(Some(file))),
})
}
pub async fn open_with(
path: impl Into<PathBuf>,
opts: std::fs::OpenOptions,
) -> io::Result<Self> {
let path = path.into();
let file = spawn_blocking(move || opts.open(&path)).await?;
Ok(Self {
inner: Arc::new(Mutex::new(Some(file))),
})
}
pub async fn read(&self, mut buf: Vec<u8>) -> io::Result<(usize, Vec<u8>)> {
let inner = Arc::clone(&self.inner);
spawn_blocking(move || {
use std::io::Read;
let mut guard = inner.lock().unwrap();
let file = guard
.as_mut()
.ok_or_else(|| io::Error::other("dtact-fs: file already closed"))?;
let n = file.read(&mut buf)?;
Ok((n, buf))
})
.await
}
pub async fn write(&self, buf: Vec<u8>) -> io::Result<(usize, Vec<u8>)> {
let inner = Arc::clone(&self.inner);
spawn_blocking(move || {
use std::io::Write;
let mut guard = inner.lock().unwrap();
let file = guard
.as_mut()
.ok_or_else(|| io::Error::other("dtact-fs: file already closed"))?;
let n = file.write(&buf)?;
Ok((n, buf))
})
.await
}
pub async fn read_at(&self, mut buf: Vec<u8>, offset: u64) -> io::Result<(usize, Vec<u8>)> {
let inner = Arc::clone(&self.inner);
spawn_blocking(move || {
let guard = inner.lock().unwrap();
let file = guard
.as_ref()
.ok_or_else(|| io::Error::other("dtact-fs: file already closed"))?;
let n = read_at_impl(file, &mut buf, offset)?;
Ok((n, buf))
})
.await
}
pub async fn write_at(&self, buf: Vec<u8>, offset: u64) -> io::Result<(usize, Vec<u8>)> {
let inner = Arc::clone(&self.inner);
spawn_blocking(move || {
let guard = inner.lock().unwrap();
let file = guard
.as_ref()
.ok_or_else(|| io::Error::other("dtact-fs: file already closed"))?;
let n = write_at_impl(file, &buf, offset)?;
Ok((n, buf))
})
.await
}
pub async fn sync_all(&self) -> io::Result<()> {
let inner = Arc::clone(&self.inner);
spawn_blocking(move || {
let guard = inner.lock().unwrap();
let file = guard
.as_ref()
.ok_or_else(|| io::Error::other("dtact-fs: file already closed"))?;
file.sync_all()
})
.await
}
pub async fn metadata(&self) -> io::Result<std::fs::Metadata> {
let inner = Arc::clone(&self.inner);
spawn_blocking(move || {
let guard = inner.lock().unwrap();
let file = guard
.as_ref()
.ok_or_else(|| io::Error::other("dtact-fs: file already closed"))?;
file.metadata()
})
.await
}
pub async fn close(self) -> io::Result<()> {
let inner = Arc::clone(&self.inner);
spawn_blocking(move || {
inner.lock().unwrap().take();
Ok(())
})
.await
}
}
#[cfg(unix)]
fn read_at_impl(file: &std::fs::File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
use std::os::unix::fs::FileExt;
file.read_at(buf, offset)
}
#[cfg(unix)]
fn write_at_impl(file: &std::fs::File, buf: &[u8], offset: u64) -> io::Result<usize> {
use std::os::unix::fs::FileExt;
file.write_at(buf, offset)
}
#[cfg(windows)]
fn read_at_impl(file: &std::fs::File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
use std::os::windows::fs::FileExt;
file.seek_read(buf, offset)
}
#[cfg(windows)]
fn write_at_impl(file: &std::fs::File, buf: &[u8], offset: u64) -> io::Result<usize> {
use std::os::windows::fs::FileExt;
file.seek_write(buf, offset)
}
pub async fn metadata(path: impl Into<PathBuf>) -> io::Result<std::fs::Metadata> {
let path = path.into();
spawn_blocking(move || std::fs::metadata(&path)).await
}
pub async fn read_dir(path: impl Into<PathBuf>) -> io::Result<Vec<std::fs::DirEntry>> {
let path: PathBuf = path.into();
spawn_blocking(move || -> io::Result<Vec<std::fs::DirEntry>> {
std::fs::read_dir(&path)?.collect()
})
.await
}
pub async fn create_dir_all(path: impl Into<PathBuf>) -> io::Result<()> {
let path = path.into();
spawn_blocking(move || std::fs::create_dir_all(&path)).await
}
pub async fn remove_file(path: impl Into<PathBuf>) -> io::Result<()> {
let path = path.into();
spawn_blocking(move || std::fs::remove_file(&path)).await
}
pub async fn canonicalize(path: impl Into<PathBuf>) -> io::Result<PathBuf> {
let path = path.into();
spawn_blocking(move || std::fs::canonicalize(&path)).await
}
pub async fn copy(from: impl Into<PathBuf>, to: impl Into<PathBuf>) -> io::Result<u64> {
let from = from.into();
let to = to.into();
spawn_blocking(move || std::fs::copy(&from, &to)).await
}
pub async fn create_dir(path: impl Into<PathBuf>) -> io::Result<()> {
let path = path.into();
spawn_blocking(move || std::fs::create_dir(&path)).await
}
pub async fn hard_link(src: impl Into<PathBuf>, dst: impl Into<PathBuf>) -> io::Result<()> {
let src = src.into();
let dst = dst.into();
spawn_blocking(move || std::fs::hard_link(&src, &dst)).await
}
pub async fn read(path: impl Into<PathBuf>) -> io::Result<Vec<u8>> {
let path = path.into();
spawn_blocking(move || std::fs::read(&path)).await
}
pub async fn read_link(path: impl Into<PathBuf>) -> io::Result<PathBuf> {
let path = path.into();
spawn_blocking(move || std::fs::read_link(&path)).await
}
pub async fn read_to_string(path: impl Into<PathBuf>) -> io::Result<String> {
let path = path.into();
spawn_blocking(move || std::fs::read_to_string(&path)).await
}
pub async fn remove_dir(path: impl Into<PathBuf>) -> io::Result<()> {
let path = path.into();
spawn_blocking(move || std::fs::remove_dir(&path)).await
}
pub async fn remove_dir_all(path: impl Into<PathBuf>) -> io::Result<()> {
let path = path.into();
spawn_blocking(move || std::fs::remove_dir_all(&path)).await
}
pub async fn rename(from: impl Into<PathBuf>, to: impl Into<PathBuf>) -> io::Result<()> {
let from = from.into();
let to = to.into();
spawn_blocking(move || std::fs::rename(&from, &to)).await
}
pub async fn set_permissions(
path: impl Into<PathBuf>,
perm: std::fs::Permissions,
) -> io::Result<()> {
let path = path.into();
spawn_blocking(move || std::fs::set_permissions(&path, perm)).await
}
pub async fn symlink(src: impl Into<PathBuf>, dst: impl Into<PathBuf>) -> io::Result<()> {
let src = src.into();
let dst = dst.into();
spawn_blocking(move || std::os::unix::fs::symlink(&src, &dst)).await
}
pub async fn symlink_metadata(path: impl Into<PathBuf>) -> io::Result<std::fs::Metadata> {
let path = path.into();
spawn_blocking(move || std::fs::symlink_metadata(&path)).await
}
pub async fn try_exists(path: impl Into<PathBuf>) -> io::Result<bool> {
let path = path.into();
spawn_blocking(move || std::fs::exists(&path)).await
}
pub async fn write(
path: impl Into<PathBuf>,
contents: impl AsRef<[u8]> + Send + 'static,
) -> io::Result<()> {
let path = path.into();
spawn_blocking(move || std::fs::write(&path, contents)).await
}
#[allow(dead_code)]
fn _assert_path_bound(_: &Path) {}