use std::sync::{Arc, Mutex};
use tokio::time::Instant;
use super::arrow::RetainedMemory;
use super::capacity::{Held, Pool, Refusal, Reservation};
use super::manager::Inner;
use crate::{Error, Result};
pub(super) struct MemoryBank {
state: Arc<Mutex<BankState>>,
}
pub(super) struct MemoryScope<'a> {
bank: &'a MemoryBank,
used: usize,
}
struct BankState {
reservation: Reservation,
used: usize,
retained: usize,
requested: usize,
refusal: Option<Shortfall>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum Shortfall {
Busy,
Exceeded,
}
impl MemoryBank {
pub(super) fn new(reservation: Reservation) -> Self {
Self { state: Arc::new(Mutex::new(BankState { requested: reservation.bytes(), reservation, used: 0, retained: 0, refusal: None })) }
}
pub(super) async fn admit(
inner: &Arc<Inner>, held: &Held, bytes: usize, deadline: Option<Instant>,
) -> std::result::Result<Self, Refusal> {
let reservation = Reservation::wait(inner, Pool::Read, held, bytes, deadline).await?;
Ok(Self::new(reservation))
}
pub(super) fn requested(&self) -> usize {
self.state.lock().unwrap().requested
}
pub(super) fn shortfall(&self) -> Option<Shortfall> {
self.state.lock().unwrap().refusal
}
pub(super) fn scope(&self) -> MemoryScope<'_> {
MemoryScope { bank: self, used: 0 }
}
pub(super) fn reserve(&self, bytes: usize) -> Result<RetainedMemory> {
let mut state = self.state.lock().unwrap();
state.ensure(bytes)?;
state.retained += bytes;
Ok(state.reservation.split(bytes).retain())
}
}
impl MemoryScope<'_> {
pub(super) fn claim(&mut self, bytes: usize) -> Result<()> {
let mut state = self.bank.state.lock().unwrap();
state.ensure(bytes)?;
state.used += bytes;
self.used += bytes;
Ok(())
}
}
impl Drop for MemoryScope<'_> {
fn drop(&mut self) {
self.bank.state.lock().unwrap().used -= self.used;
}
}
impl BankState {
fn ensure(&mut self, bytes: usize) -> Result<()> {
let required = self.used.checked_add(bytes).ok_or(Error::ResourceExhausted { resource: "storage read memory" })?;
self.requested = self.requested.max(required.saturating_add(self.retained));
let available = self.reservation.bytes();
if required <= available {
return Ok(());
}
match self.reservation.try_grow(required - available) {
Ok(()) => Ok(()),
Err(refusal) => {
self.refusal = Some(match refusal {
Refusal::Busy => Shortfall::Busy,
Refusal::Exceeded(_) => Shortfall::Exceeded,
Refusal::Cycle | Refusal::Elapsed | Refusal::Failed(_) => {
return Err(refusal.into_error());
}
});
Err(Refusal::Busy.into_error())
}
}
}
}