use crate::error::{JournalError, Result};
use crate::file::value_guard::ValueGuard;
use std::cell::{RefCell, UnsafeCell};
use std::num::NonZeroU64;
pub struct GuardedCell<T> {
value: UnsafeCell<T>,
guard: RefCell<bool>,
}
impl<T> GuardedCell<T> {
pub fn new(value: T) -> Self {
Self {
value: UnsafeCell::new(value),
guard: RefCell::new(false),
}
}
#[allow(dead_code)]
pub fn guard(&self) -> &RefCell<bool> {
&self.guard
}
#[allow(clippy::mut_from_ref)]
pub fn borrow_mut_checked(&self) -> Result<&mut T> {
let is_in_use = self.guard.borrow();
if *is_in_use {
return Err(JournalError::ValueGuardInUse);
}
drop(is_in_use);
unsafe { Ok(&mut *self.value.get()) }
}
pub fn with_mut<R, F>(&self, f: F) -> Result<R>
where
F: FnOnce(&mut T) -> Result<R>,
{
let mut is_in_use = self.guard.borrow_mut();
if *is_in_use {
return Err(JournalError::ValueGuardInUse);
}
*is_in_use = true;
drop(is_in_use);
struct GuardReset<'a>(&'a RefCell<bool>);
impl Drop for GuardReset<'_> {
fn drop(&mut self) {
*self.0.borrow_mut() = false;
}
}
let _reset = GuardReset(&self.guard);
let value_ref = unsafe { &mut *self.value.get() };
f(value_ref)
}
pub fn get_mut(&mut self) -> &mut T {
self.value.get_mut()
}
#[allow(dead_code)]
pub fn into_inner(self) -> T {
self.value.into_inner()
}
pub fn with_guarded<'a, R, F>(&'a self, offset: NonZeroU64, f: F) -> Result<ValueGuard<'a, R>>
where
F: FnOnce(&'a mut T) -> Result<R>,
{
let mut is_in_use = self.guard.borrow_mut();
if *is_in_use {
return Err(JournalError::ValueGuardInUse);
}
let value_ref = unsafe { &mut *self.value.get() };
let result = f(value_ref)?;
*is_in_use = true;
Ok(ValueGuard::new(offset, result, &self.guard))
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestData {
value: Vec<u8>,
}
impl TestData {
fn get_slice(&mut self, start: usize, len: usize) -> &[u8] {
&self.value[start..start + len]
}
}
#[test]
fn test_basic_borrow() {
let cell = GuardedCell::new(TestData {
value: vec![1, 2, 3, 4, 5],
});
{
{
let is_in_use = cell.guard().borrow();
assert!(!*is_in_use);
}
let data = cell.borrow_mut_checked().unwrap();
let slice = data.get_slice(0, 3);
assert_eq!(slice, &[1, 2, 3]);
*cell.guard().borrow_mut() = true;
}
*cell.guard().borrow_mut() = false;
{
{
let is_in_use = cell.guard().borrow();
assert!(!*is_in_use);
}
let data = cell.borrow_mut_checked().unwrap();
let slice = data.get_slice(2, 3);
assert_eq!(slice, &[3, 4, 5]);
*cell.guard().borrow_mut() = true;
}
}
#[test]
fn test_guard_prevents_double_borrow() {
let cell = GuardedCell::new(TestData {
value: vec![1, 2, 3],
});
*cell.guard().borrow_mut() = true;
let result = cell.borrow_mut_checked();
assert!(matches!(result, Err(JournalError::ValueGuardInUse)));
}
#[test]
fn test_get_mut() {
let mut cell = GuardedCell::new(TestData {
value: vec![1, 2, 3],
});
let data = cell.get_mut();
data.value.push(4);
assert_eq!(data.value, vec![1, 2, 3, 4]);
}
#[test]
fn test_into_inner() {
let cell = GuardedCell::new(TestData {
value: vec![1, 2, 3],
});
let data = cell.into_inner();
assert_eq!(data.value, vec![1, 2, 3]);
}
}