use crate::error::{Error, Result};
use alloc::boxed::Box;
pub mod memory;
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub mod std_file;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OpenFlags {
pub write: bool,
pub create: bool,
}
impl OpenFlags {
pub const READ_ONLY: OpenFlags = OpenFlags {
write: false,
create: false,
};
pub const READ_WRITE: OpenFlags = OpenFlags {
write: true,
create: false,
};
pub const READ_WRITE_CREATE: OpenFlags = OpenFlags {
write: true,
create: true,
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum LockLevel {
Unlocked,
Shared,
Reserved,
Pending,
Exclusive,
}
#[derive(Debug, Default)]
pub struct LockState {
shared: usize,
reserved: bool,
pending: bool,
exclusive: bool,
}
impl LockState {
pub fn acquire(&mut self, from: LockLevel, to: LockLevel) -> Result<()> {
if to <= from {
return Ok(());
}
match to {
LockLevel::Unlocked => {}
LockLevel::Shared => {
if self.pending || self.exclusive {
return Err(Error::Busy);
}
self.shared += 1;
}
LockLevel::Reserved => {
if self.reserved || self.exclusive {
return Err(Error::Busy);
}
self.reserved = true;
}
LockLevel::Pending => {
if self.pending || self.exclusive {
return Err(Error::Busy);
}
self.pending = true;
}
LockLevel::Exclusive => {
if self.exclusive {
return Err(Error::Busy);
}
let own_shared = usize::from(from >= LockLevel::Shared);
if self.shared > own_shared {
return Err(Error::Busy);
}
self.pending = true;
self.exclusive = true;
}
}
Ok(())
}
pub fn release(&mut self, from: LockLevel, to: LockLevel) {
if from >= LockLevel::Exclusive && to < LockLevel::Exclusive {
self.exclusive = false;
}
if from >= LockLevel::Pending && to < LockLevel::Pending {
self.pending = false;
}
if from >= LockLevel::Reserved && to < LockLevel::Reserved {
self.reserved = false;
}
if from >= LockLevel::Shared && to < LockLevel::Shared {
self.shared = self.shared.saturating_sub(1);
}
}
}
pub trait File {
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> Result<()>;
fn write_all_at(&mut self, buf: &[u8], offset: u64) -> Result<()>;
fn truncate(&mut self, size: u64) -> Result<()>;
fn sync(&mut self) -> Result<()>;
fn size(&self) -> Result<u64>;
fn lock(&mut self, _level: LockLevel) -> Result<()> {
Ok(())
}
fn unlock(&mut self, _level: LockLevel) -> Result<()> {
Ok(())
}
}
pub trait Vfs {
fn open(&self, path: &str, flags: OpenFlags) -> Result<Box<dyn File>>;
fn delete(&self, path: &str) -> Result<()>;
fn exists(&self, path: &str) -> Result<bool>;
}
#[cfg(test)]
mod lock_tests {
use super::*;
#[test]
fn many_readers_coexist() {
let mut s = LockState::default();
s.acquire(LockLevel::Unlocked, LockLevel::Shared).unwrap();
s.acquire(LockLevel::Unlocked, LockLevel::Shared).unwrap();
assert_eq!(s.shared, 2);
}
#[test]
fn reserved_coexists_with_readers_but_is_exclusive_among_writers() {
let mut s = LockState::default();
s.acquire(LockLevel::Unlocked, LockLevel::Shared).unwrap();
s.acquire(LockLevel::Unlocked, LockLevel::Shared).unwrap();
s.acquire(LockLevel::Shared, LockLevel::Reserved).unwrap();
s.acquire(LockLevel::Unlocked, LockLevel::Shared).unwrap();
assert!(matches!(
s.acquire(LockLevel::Shared, LockLevel::Reserved),
Err(Error::Busy)
));
}
#[test]
fn exclusive_requires_other_readers_to_drain() {
let mut s = LockState::default();
s.acquire(LockLevel::Unlocked, LockLevel::Shared).unwrap(); s.acquire(LockLevel::Unlocked, LockLevel::Shared).unwrap(); s.acquire(LockLevel::Shared, LockLevel::Reserved).unwrap();
assert!(matches!(
s.acquire(LockLevel::Reserved, LockLevel::Exclusive),
Err(Error::Busy)
));
s.release(LockLevel::Shared, LockLevel::Unlocked);
s.acquire(LockLevel::Reserved, LockLevel::Exclusive)
.unwrap();
assert!(s.exclusive);
}
#[test]
fn pending_blocks_new_readers() {
let mut s = LockState::default();
s.acquire(LockLevel::Unlocked, LockLevel::Shared).unwrap();
s.acquire(LockLevel::Shared, LockLevel::Pending).unwrap();
assert!(matches!(
s.acquire(LockLevel::Unlocked, LockLevel::Shared),
Err(Error::Busy)
));
}
#[test]
fn release_clears_all_held_levels() {
let mut s = LockState::default();
s.acquire(LockLevel::Unlocked, LockLevel::Shared).unwrap();
s.acquire(LockLevel::Shared, LockLevel::Reserved).unwrap();
s.acquire(LockLevel::Reserved, LockLevel::Exclusive)
.unwrap();
s.release(LockLevel::Exclusive, LockLevel::Unlocked);
assert_eq!(s.shared, 0);
assert!(!s.reserved && !s.pending && !s.exclusive);
s.acquire(LockLevel::Unlocked, LockLevel::Shared).unwrap();
s.acquire(LockLevel::Shared, LockLevel::Exclusive).unwrap();
assert!(s.exclusive);
}
}