use std::sync::{Arc, LazyLock};
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use super::native::Native;
use super::value::Value;
#[derive(Default)]
struct State {
shared: usize,
exclusive: bool,
}
static BORROWS: LazyLock<Mutex<FxHashMap<usize, State>>> = LazyLock::new(Mutex::default);
fn key(slot: &Arc<Mutex<Value>>) -> usize {
Arc::as_ptr(slot) as usize
}
pub struct BorrowGuard {
key: usize,
exclusive: bool,
}
impl BorrowGuard {
pub fn is_exclusive(&self) -> bool {
self.exclusive
}
}
impl Drop for BorrowGuard {
fn drop(&mut self) {
let mut table = BORROWS.lock();
let Some(state) = table.get_mut(&self.key) else {
return;
};
if self.exclusive {
state.exclusive = false;
} else {
state.shared = state.shared.saturating_sub(1);
}
if !state.exclusive && state.shared == 0 {
table.remove(&self.key);
}
}
}
pub struct BorrowFailure {
exclusive: bool,
}
impl BorrowFailure {
pub fn message(&self) -> String {
self.display().to_string()
}
fn display(&self) -> &'static str {
if self.exclusive {
"RefCell already borrowed"
} else {
"RefCell already mutably borrowed"
}
}
fn debug(&self) -> &'static str {
if self.exclusive {
"BorrowMutError"
} else {
"BorrowError"
}
}
pub fn value(&self) -> Value {
Native::ParseErr {
display: self.display().to_string(),
debug: self.debug().to_string(),
}
.wrap()
}
}
pub fn acquire(
slot: &Arc<Mutex<Value>>,
exclusive: bool,
) -> Result<Arc<BorrowGuard>, BorrowFailure> {
let key = key(slot);
let mut table = BORROWS.lock();
let state = table.entry(key).or_default();
let blocked = state.exclusive || (exclusive && state.shared > 0);
if blocked {
if !state.exclusive && state.shared == 0 {
table.remove(&key);
}
return Err(BorrowFailure { exclusive });
}
if exclusive {
state.exclusive = true;
} else {
state.shared += 1;
}
Ok(Arc::new(BorrowGuard { key, exclusive }))
}