use crate::rt::object;
use crate::rt::{self, Access, Location, Synchronize, VersionVec};
use std::sync::atomic::Ordering::{Acquire, Release};
#[derive(Debug)]
pub(crate) struct Arc {
state: object::Ref<State>,
}
#[derive(Debug)]
pub(super) struct State {
ref_cnt: usize,
allocated: Location,
synchronize: Synchronize,
last_ref_inc: Option<Access>,
last_ref_dec: Option<Access>,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub(super) enum Action {
RefInc,
RefDec,
}
impl Arc {
pub(crate) fn new(location: Location) -> Arc {
rt::execution(|execution| {
let state = execution.objects.insert(State {
ref_cnt: 1,
allocated: location,
synchronize: Synchronize::new(),
last_ref_inc: None,
last_ref_dec: None,
});
Arc { state }
})
}
pub(crate) fn ref_inc(&self) {
self.branch(Action::RefInc);
rt::execution(|execution| {
let state = self.state.get_mut(&mut execution.objects);
state.ref_cnt = state.ref_cnt.checked_add(1).expect("overflow");
})
}
pub(crate) fn get_mut(&self) -> bool {
self.branch(Action::RefDec);
rt::execution(|execution| {
let state = self.state.get_mut(&mut execution.objects);
assert!(state.ref_cnt >= 1, "Arc is released");
state.synchronize.sync_load(&mut execution.threads, Acquire);
if state.ref_cnt == 1 {
true
} else {
false
}
})
}
pub(crate) fn ref_dec(&self) -> bool {
self.branch(Action::RefDec);
rt::execution(|execution| {
let state = self.state.get_mut(&mut execution.objects);
assert!(state.ref_cnt >= 1, "Arc is already released");
state.ref_cnt -= 1;
state
.synchronize
.sync_store(&mut execution.threads, Release);
if state.ref_cnt == 0 {
state.synchronize.sync_load(&mut execution.threads, Acquire);
true
} else {
false
}
})
}
fn branch(&self, action: Action) {
let r = self.state;
r.branch_action(action);
assert!(
r.ref_eq(self.state),
"Internal state mutated during branch. This is \
usually due to a bug in the algorithm being tested writing in \
an invalid memory location."
);
}
}
impl State {
pub(super) fn check_for_leaks(&self) {
if self.ref_cnt != 0 {
if self.allocated.is_captured() {
panic!("Arc leaked.\n Allocated: {}", self.allocated);
} else {
panic!("Arc leaked.");
}
}
}
pub(super) fn last_dependent_access(&self, action: Action) -> Option<&Access> {
match action {
Action::RefInc => None,
Action::RefDec => self.last_ref_dec.as_ref(),
}
}
pub(super) fn set_last_access(&mut self, action: Action, path_id: usize, version: &VersionVec) {
match action {
Action::RefInc => Access::set_or_create(&mut self.last_ref_inc, path_id, version),
Action::RefDec => Access::set_or_create(&mut self.last_ref_dec, path_id, version),
}
}
}