use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::error::{MemError, Result};
use crate::governor::GlobalCounter;
use crate::over_release::{Layer, ReleaseIdentity, release_layer};
pub(crate) struct ReserveScope {
global: Arc<GlobalCounter>,
global_credited: bool,
database: Option<Arc<AtomicUsize>>,
tenant: Option<Arc<AtomicUsize>>,
engine: Option<Arc<AtomicUsize>>,
size: usize,
committed: bool,
identity: ReleaseIdentity,
}
pub(crate) struct ReservedLayers {
pub global: Arc<GlobalCounter>,
pub database: Option<Arc<AtomicUsize>>,
pub tenant: Option<Arc<AtomicUsize>>,
pub engine: Option<Arc<AtomicUsize>>,
}
impl ReserveScope {
pub(crate) fn new(global: Arc<GlobalCounter>, size: usize, identity: ReleaseIdentity) -> Self {
Self {
global,
global_credited: false,
database: None,
tenant: None,
engine: None,
size,
committed: false,
identity,
}
}
pub(crate) fn try_credit_global(&mut self) -> Result<()> {
if self.size == 0 {
self.global_credited = true;
return Ok(());
}
loop {
let current = self.global.allocated.load(Ordering::Relaxed);
if current + self.size > self.global.ceiling {
return Err(MemError::GlobalCeilingExceeded {
allocated: current,
ceiling: self.global.ceiling,
requested: self.size,
});
}
match self.global.allocated.compare_exchange_weak(
current,
current + self.size,
Ordering::AcqRel,
Ordering::Relaxed,
) {
Ok(_) => {
self.global_credited = true;
return Ok(());
}
Err(_) => continue,
}
}
}
pub(crate) fn credit_global_unchecked(&mut self) {
self.global.allocated.fetch_add(self.size, Ordering::AcqRel);
self.global_credited = true;
}
pub(crate) fn credit_database(&mut self, counter: Arc<AtomicUsize>) {
self.database = Some(counter);
}
pub(crate) fn credit_tenant(&mut self, counter: Arc<AtomicUsize>) {
self.tenant = Some(counter);
}
pub(crate) fn credit_engine(&mut self, counter: Arc<AtomicUsize>) {
self.engine = Some(counter);
}
pub(crate) fn commit(mut self) -> ReservedLayers {
self.committed = true;
ReservedLayers {
global: Arc::clone(&self.global),
database: self.database.take(),
tenant: self.tenant.take(),
engine: self.engine.take(),
}
}
}
impl Drop for ReserveScope {
fn drop(&mut self) {
if self.committed || self.size == 0 {
return;
}
if let Some(ref counter) = self.engine {
release_layer(
counter,
self.size,
Layer::Engine,
&self.global.over_release,
self.identity,
);
}
if let Some(ref counter) = self.tenant {
release_layer(
counter,
self.size,
Layer::Tenant,
&self.global.over_release,
self.identity,
);
}
if let Some(ref counter) = self.database {
release_layer(
counter,
self.size,
Layer::Database,
&self.global.over_release,
self.identity,
);
}
if self.global_credited {
release_layer(
&self.global.allocated,
self.size,
Layer::Global,
&self.global.over_release,
self.identity,
);
}
}
}