use std::fs::{self, File, OpenOptions};
use std::io::{self, Read, Seek, Write};
use std::path::{Path, PathBuf};
pub use crate::sync::FileGuard;
pub struct Mutex {
path: PathBuf,
}
impl Mutex {
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Mutex> {
fs::create_dir_all(&path)?;
Ok(Mutex {
path: path.as_ref().to_owned(),
})
}
pub async fn lock(&self) -> io::Result<MutexGuard> {
let file_guard = FileGuard::lock(self.path.join("lock")).await?;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(self.path.join("contents"))?;
Ok(MutexGuard {
_file_guard: file_guard,
file,
})
}
pub fn try_lock(&self) -> io::Result<Option<MutexGuard>> {
let file_guard = FileGuard::try_lock(self.path.join("lock"))?;
if let Some(file_guard) = file_guard {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(self.path.join("contents"))?;
Ok(Some(MutexGuard {
_file_guard: file_guard,
file,
}))
} else {
Ok(None)
}
}
}
pub struct MutexGuard {
_file_guard: FileGuard,
file: File,
}
impl MutexGuard {
pub fn read(&self) -> io::Result<Vec<u8>> {
(&self.file).seek(io::SeekFrom::Start(0))?;
(&self.file).bytes().collect::<io::Result<Vec<_>>>()
}
pub fn write<D: AsRef<[u8]>>(&self, data: D) -> io::Result<()> {
(&self.file).seek(io::SeekFrom::Start(0))?;
self.file.set_len(0)?;
(&self.file).write_all(data.as_ref())?;
(&self.file).flush()
}
pub fn file(&self) -> &File {
&self.file
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_mutex() {
futures::executor::block_on(async move {
let mutex = Mutex::open("data/mutex").unwrap();
let guard = mutex.lock().await.unwrap();
guard.write(b"some data").unwrap();
drop(guard);
let guard = mutex.lock().await.unwrap();
assert_eq!(
String::from_utf8_lossy(&*guard.read().unwrap()),
String::from_utf8_lossy(b"some data")
);
});
}
#[test]
fn test_mutex_longer_data_first() {
futures::executor::block_on(async move {
let mutex = Mutex::open("data/mutex-longer-data-first").unwrap();
let guard = mutex.lock().await.unwrap();
guard.write(b"some long data").unwrap();
drop(guard);
let guard = mutex.lock().await.unwrap();
guard.write(b"some data").unwrap();
drop(guard);
let guard = mutex.lock().await.unwrap();
assert_eq!(guard.read().unwrap(), b"some data");
});
}
}