use std::fmt;
use super::tracker::BorrowTracker;
use crate::engine::error::{ExecutionError, InvalidAccessReason};
use crate::engine::types::ComponentID;
pub struct BorrowGuard<'a> {
tracker: &'a BorrowTracker,
reads: Vec<ComponentID>,
writes: Vec<ComponentID>,
}
impl fmt::Debug for BorrowGuard<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BorrowGuard")
.field("reads", &self.reads)
.field("writes", &self.writes)
.finish()
}
}
impl<'a> BorrowGuard<'a> {
pub fn new(
tracker: &'a BorrowTracker,
reads: &[ComponentID],
writes: &[ComponentID],
) -> Result<Self, ExecutionError> {
let mut r = reads.to_vec();
let mut w = writes.to_vec();
r.sort_unstable();
w.sort_unstable();
r.dedup();
w.dedup();
for component_id in &r {
if w.binary_search(component_id).is_ok() {
return Err(ExecutionError::InvalidQueryAccess {
component_id: *component_id,
reason: InvalidAccessReason::ReadAndWrite,
});
}
}
let mut acquired_writes: Vec<ComponentID> = Vec::with_capacity(w.len());
let mut acquired_reads: Vec<ComponentID> = Vec::with_capacity(r.len());
let rollback = |aw: &[ComponentID], ar: &[ComponentID]| {
for &id in ar.iter().rev() {
tracker.release_read(id);
}
for &id in aw.iter().rev() {
tracker.release_write(id);
}
};
for &component_id in &w {
if let Err(e) = tracker.acquire_write(component_id) {
rollback(&acquired_writes, &acquired_reads);
return Err(e);
}
acquired_writes.push(component_id);
}
for &component_id in &r {
if let Err(e) = tracker.acquire_read(component_id) {
rollback(&acquired_writes, &acquired_reads);
return Err(e);
}
acquired_reads.push(component_id);
}
Ok(Self {
tracker,
reads: r,
writes: w,
})
}
}
impl Drop for BorrowGuard<'_> {
fn drop(&mut self) {
for &component_id in self.reads.iter().rev() {
self.tracker.release_read(component_id);
}
for &component_id in self.writes.iter().rev() {
self.tracker.release_write(component_id);
}
}
}