use std::{
cell::UnsafeCell,
fmt::{Debug, Formatter},
hint::spin_loop,
mem::replace,
ops::{Deref, DerefMut},
sync::atomic::{AtomicBool, Ordering},
};
use crate::{
report::{debug_unreachable, system_panic},
runtime::{Origin, RuntimeError, RuntimeResult},
};
const BORROW_LIMIT: u32 = 64;
#[repr(transparent)]
pub(super) struct BorrowTable(SpinMutex<BorrowTableInner>);
impl Debug for BorrowTable {
#[inline]
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
let access = self.0.lock();
let mut debug_struct = formatter.debug_struct("BorrowTable");
unsafe {
let value_refs = &*access.value_refs.get();
debug_struct.field("value_refs", value_refs);
}
unsafe {
let value_mut = &*access.value_mut.get();
match value_mut {
None => debug_struct.field("value_mut", &[] as &[&Origin; 0]),
Some(origin) => debug_struct.field("value_mut", &[origin]),
};
}
unsafe {
let place_refs = &*access.place_refs.get();
debug_struct.field("place_refs", place_refs);
}
unsafe {
let place_muts = &*access.place_muts.get();
debug_struct.field("place_muts", place_muts);
}
debug_struct.finish()
}
}
impl BorrowTable {
#[inline(always)]
pub(super) fn new() -> Self {
Self(SpinMutex::new(BorrowTableInner {
value_refs: UnsafeCell::new(BorrowStack::new()),
value_mut: UnsafeCell::new(None),
place_refs: UnsafeCell::new(BorrowStack::new()),
place_muts: UnsafeCell::new(BorrowStack::new()),
}))
}
#[inline(always)]
pub(super) fn access(&self) -> BorrowTableAccess {
BorrowTableAccess(self.0.lock())
}
}
#[repr(transparent)]
pub(super) struct BorrowTableAccess<'a>(SpinGuard<'a, BorrowTableInner>);
impl BorrowTableAccess<'_> {
#[inline(always)]
pub(super) fn grant_value_ref(&self, origin: Origin) -> RuntimeResult<u32> {
{
let value_mut = unsafe { &*self.0.value_mut.get() };
if let Some(cause) = value_mut {
return Err(RuntimeError::WriteToRead {
access_origin: origin,
borrow_origin: *cause,
});
}
}
{
let place_muts = unsafe { &*self.0.place_muts.get() };
if let Some(cause) = place_muts.last() {
return Err(RuntimeError::WriteToRead {
access_origin: origin,
borrow_origin: *cause,
});
}
}
let index = {
let value_refs = unsafe { &mut *self.0.value_refs.get() };
value_refs.occupy(origin)?
};
Ok(index)
}
#[inline(always)]
pub(super) fn grant_value_mut(&self, origin: Origin) -> RuntimeResult<u32> {
{
let value_refs = unsafe { &*self.0.value_refs.get() };
if let Some(cause) = value_refs.last() {
return Err(RuntimeError::ReadToWrite {
access_origin: origin,
borrow_origin: *cause,
});
}
}
{
let place_refs = unsafe { &*self.0.place_refs.get() };
if let Some(cause) = place_refs.last() {
return Err(RuntimeError::ReadToWrite {
access_origin: origin,
borrow_origin: *cause,
});
}
}
{
let place_muts = unsafe { &*self.0.place_muts.get() };
if let Some(cause) = place_muts.last() {
return Err(RuntimeError::WriteToWrite {
access_origin: origin,
borrow_origin: *cause,
});
}
}
{
let value_mut = unsafe { &mut *self.0.value_mut.get() };
if let Some(cause) = value_mut {
return Err(RuntimeError::WriteToWrite {
access_origin: origin,
borrow_origin: *cause,
});
}
*value_mut = Some(origin);
};
Ok(0)
}
#[inline(always)]
pub(super) fn grant_place_ref(&self, origin: Origin) -> RuntimeResult<u32> {
{
let value_mut = unsafe { &*self.0.value_mut.get() };
if let Some(cause) = value_mut {
return Err(RuntimeError::WriteToRead {
access_origin: origin,
borrow_origin: *cause,
});
}
}
let index = {
let place_refs = unsafe { &mut *self.0.place_refs.get() };
place_refs.occupy(origin)?
};
Ok(index)
}
#[inline(always)]
pub(super) fn grant_place_mut(&self, origin: Origin) -> RuntimeResult<u32> {
{
let value_mut = unsafe { &*self.0.value_mut.get() };
if let Some(cause) = value_mut {
return Err(RuntimeError::WriteToWrite {
access_origin: origin,
borrow_origin: *cause,
});
}
}
{
let value_refs = unsafe { &*self.0.value_refs.get() };
if let Some(cause) = value_refs.last() {
return Err(RuntimeError::ReadToWrite {
access_origin: origin,
borrow_origin: *cause,
});
}
}
let index = {
let place_muts = unsafe { &mut *self.0.place_muts.get() };
place_muts.occupy(origin)?
};
Ok(index)
}
#[inline(always)]
pub(super) unsafe fn release_value_ref(&self, index: u32) {
let value_refs = unsafe { &mut *self.0.value_refs.get() };
let _ = unsafe { value_refs.release(index) };
}
#[inline(always)]
pub(super) unsafe fn release_value_mut(&self, _index: u32) {
let value_mut = unsafe { &mut *self.0.value_mut.get() };
if replace(value_mut, None).is_none() {
unsafe { debug_unreachable!("ValueMut releasing failure.") }
}
}
#[inline(always)]
pub(super) unsafe fn release_place_ref(&self, index: u32) {
let place_refs = unsafe { &mut *self.0.place_refs.get() };
let _ = unsafe { place_refs.release(index) };
}
#[inline(always)]
pub(super) unsafe fn release_place_mut(&self, index: u32) {
let place_muts = unsafe { &mut *self.0.place_muts.get() };
let _ = unsafe { place_muts.release(index) };
}
#[inline(always)]
pub(super) unsafe fn get_value_ref(&self, index: u32) -> Origin {
let value_refs = unsafe { &*self.0.value_refs.get() };
*unsafe { value_refs.get(index) }
}
#[inline(always)]
pub(super) unsafe fn get_value_mut(&self, _index: u32) -> Origin {
let value_mut = unsafe { &*self.0.value_mut.get() };
match value_mut {
Some(origin) => *origin,
None => {
unsafe { debug_unreachable!("ValueMut releasing failure.") }
}
}
}
#[inline(always)]
pub(super) unsafe fn get_place_ref(&self, index: u32) -> Origin {
let place_refs = unsafe { &*self.0.place_refs.get() };
*unsafe { place_refs.get(index) }
}
#[inline(always)]
pub(super) unsafe fn get_place_mut(&self, index: u32) -> Origin {
let place_muts = unsafe { &*self.0.place_muts.get() };
*unsafe { place_muts.get(index) }
}
#[cfg(debug_assertions)]
#[inline(always)]
pub(super) fn assert_value(&self) {
let value_refs = unsafe { &*self.0.value_refs.get() };
let value_mut = unsafe { &*self.0.value_mut.get() };
if value_refs.is_empty() && value_mut.is_none() {
system_panic!("Neither ValueRef nor ValueMut access granted.")
}
}
#[cfg(debug_assertions)]
#[inline(always)]
pub(super) fn assert_value_mut(&self) {
let value_mut = unsafe { &*self.0.value_mut.get() };
if value_mut.is_none() {
system_panic!("ValueMut access is not granted.")
}
}
#[cfg(debug_assertions)]
#[inline(always)]
pub(super) fn assert_place(&self) {
let place_refs = unsafe { &*self.0.place_refs.get() };
let place_muts = unsafe { &*self.0.place_muts.get() };
if place_refs.is_empty() && place_muts.is_empty() {
system_panic!("Neither PlaceRef nor PlaceMut access granted.")
}
}
#[cfg(debug_assertions)]
#[inline(always)]
pub(super) fn assert_place_mut(&self) {
let place_muts = unsafe { &*self.0.place_muts.get() };
if place_muts.is_empty() {
system_panic!("PlaceMut access is not granted.")
}
}
#[cfg(debug_assertions)]
#[inline(always)]
pub(super) fn assert_empty(&self) {
{
let value_refs = unsafe { &*self.0.value_refs.get() };
if let Some(origin) = value_refs.last() {
system_panic!("Unreleased ValueRef access {:?}.", origin,)
}
}
{
let value_mut = unsafe { &*self.0.value_mut.get() };
if let Some(origin) = value_mut {
system_panic!("Unreleased ValueMut access {:?}.", origin,)
}
}
{
let place_refs = unsafe { &*self.0.place_refs.get() };
if let Some(origin) = place_refs.last() {
system_panic!("Unreleased PlaceRef access {:?}.", origin,)
}
}
{
let place_muts = unsafe { &*self.0.place_muts.get() };
if let Some(origin) = place_muts.last() {
system_panic!("Unreleased PlaceMut access {:?}.", origin,)
}
}
}
}
struct BorrowTableInner {
value_refs: UnsafeCell<BorrowStack>,
value_mut: UnsafeCell<Option<Origin>>,
place_refs: UnsafeCell<BorrowStack>,
place_muts: UnsafeCell<BorrowStack>,
}
pub(super) struct BorrowStack {
entries: Vec<BorrowEntry>,
next: u32,
last: u32,
}
impl Debug for BorrowStack {
#[inline]
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
let mut debug_list = formatter.debug_list();
let mut index = self.last as usize;
loop {
let occupied = match self.entries.get(index) {
None => break,
Some(occupied) => occupied,
};
match occupied {
BorrowEntry::Occupied {
previous, origin, ..
} => {
debug_list.entry(origin);
index = *previous as usize;
}
BorrowEntry::Vacant(..) => {
unsafe { debug_unreachable!("Invalid BorrowStack entry index.") }
}
}
}
debug_list.finish()
}
}
impl BorrowStack {
#[inline(always)]
fn new() -> Self {
Self {
entries: Vec::new(),
next: 0,
last: u32::MAX,
}
}
#[must_use]
#[inline]
fn last(&self) -> Option<&Origin> {
let occupied = self.entries.get(self.last as usize)?;
match occupied {
BorrowEntry::Occupied { origin, .. } => Some(origin),
BorrowEntry::Vacant(..) => {
unsafe { debug_unreachable!("Last index points to vacant entry.") }
}
}
}
#[cfg(debug_assertions)]
#[inline(always)]
fn is_empty(&self) -> bool {
self.last >= self.entries.len() as u32
}
#[inline]
fn occupy(&mut self, origin: Origin) -> RuntimeResult<u32> {
let index = self.next;
if index == BORROW_LIMIT {
return Err(RuntimeError::BorrowLimit {
access_origin: origin,
limit: BORROW_LIMIT as usize,
});
}
match self.entries.get_mut(self.next as usize) {
None => {
let previous = self.last;
self.last = index;
self.next += 1;
self.entries.push(BorrowEntry::Occupied {
previous,
next: self.next,
origin,
});
}
Some(vacant) => {
debug_assert!(
matches!(vacant, BorrowEntry::Vacant(..)),
"Occupied entry in the next position.",
);
let previous = self.last;
self.last = index;
self.next = match vacant {
BorrowEntry::Vacant(next) => *next,
_ => {
unsafe { debug_unreachable!("Occupied entry in the next position") }
}
};
*vacant = BorrowEntry::Occupied {
previous,
next: self.next,
origin,
}
}
}
Ok(index)
}
#[must_use]
#[inline]
unsafe fn get(&self, index: u32) -> &Origin {
debug_assert!(
(index as usize) < self.entries.len(),
"Index out of bounds."
);
match self.entries.get(index as usize) {
Some(occupied) => {
match occupied {
BorrowEntry::Occupied { origin, .. } => origin,
BorrowEntry::Vacant(..) => {
unsafe { debug_unreachable!("Vacant entry in occupied position.") }
}
}
}
None => {
unsafe { debug_unreachable!("Index out of bounds.") }
}
}
}
#[must_use]
#[inline]
unsafe fn release(&mut self, index: u32) -> Origin {
debug_assert!(
(index as usize) < self.entries.len(),
"Index out of bounds."
);
match self.entries.get_mut(index as usize) {
Some(occupied) => {
let origin = match replace(occupied, BorrowEntry::Vacant(self.next)) {
BorrowEntry::Occupied {
previous,
next,
origin,
} => {
if index == self.last {
self.last = previous;
}
match self.entries.get_mut(previous as usize) {
Some(BorrowEntry::Occupied {
next: previous_next,
..
}) => {
*previous_next = next;
}
_ => (),
}
match self.entries.get_mut(next as usize) {
Some(BorrowEntry::Occupied {
previous: next_previous,
..
}) => {
*next_previous = previous;
}
_ => (),
}
origin
}
BorrowEntry::Vacant(..) => {
unsafe { debug_unreachable!("Vacant entry in occupied position.") }
}
};
self.next = index;
origin
}
None => {
unsafe { debug_unreachable!("Index out of bounds.") }
}
}
}
}
enum BorrowEntry {
Occupied {
previous: u32,
next: u32,
origin: Origin,
},
Vacant(u32),
}
struct SpinMutex<T> {
lock: AtomicBool,
data: UnsafeCell<T>,
}
impl<T> SpinMutex<T> {
#[inline(always)]
fn new(data: T) -> Self {
Self {
lock: AtomicBool::new(false),
data: UnsafeCell::new(data),
}
}
#[inline(always)]
fn lock(&self) -> SpinGuard<T> {
if self
.lock
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
while self
.lock
.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
spin_loop()
}
}
SpinGuard { mutex: &self }
}
}
#[repr(transparent)]
struct SpinGuard<'a, T> {
mutex: &'a SpinMutex<T>,
}
impl<'a, T> Drop for SpinGuard<'a, T> {
fn drop(&mut self) {
self.mutex.lock.store(false, Ordering::Release);
}
}
impl<'a, T> Deref for SpinGuard<'a, T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target {
unsafe { &*self.mutex.data.get() }
}
}
impl<'a, T> DerefMut for SpinGuard<'a, T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.mutex.data.get() }
}
}