use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::time::Duration;
use super::handle::MemoryBudget;
use crate::ReservationError;
use crate::lease::LeaseGuard;
const EXECUTOR_SPILL_TARGET_DIVISOR: u64 = 16;
const MIN_EXECUTOR_SPILL_RUN_BUFFER_BYTES: u64 = 1024 * 1024;
const MAX_EXECUTOR_SPILL_RUN_BUFFER_BYTES: u64 = 32 * 1024 * 1024;
const RESULT_CHANNEL_TARGET_DIVISOR: u64 = 32;
const MIN_RESULT_CHANNEL_BYTES: u64 = 256 * 1024;
const MAX_RESULT_CHANNEL_BYTES: u64 = 8 * 1024 * 1024;
impl MemoryBudget {
#[must_use]
pub fn observed_usage_bytes(&self) -> u64 {
self.state
.jemalloc
.read()
.observed_usage_bytes(self.state.rss.read_rss_bytes())
}
#[must_use = "dropping the guard immediately releases the lease"]
pub fn lease(&self, bytes: u64) -> LeaseGuard {
self.try_lease(bytes)
.unwrap_or_else(|error| panic!("{error}"))
}
pub fn try_lease(&self, bytes: u64) -> Result<LeaseGuard, ReservationError> {
let capacity = self.target_bytes();
let mut reserved = self.state.active_lease_bytes.load(Ordering::Relaxed);
loop {
let Some(next) = reserved.checked_add(bytes).filter(|next| *next <= capacity) else {
return Err(ReservationError::new(bytes, reserved, capacity));
};
match self.state.active_lease_bytes.compare_exchange_weak(
reserved,
next,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => return Ok(LeaseGuard::new(bytes, Arc::clone(&self.state))),
Err(observed) => reserved = observed,
}
}
}
#[must_use]
pub fn target_bytes(&self) -> u64 {
self.state.config.target_bytes()
}
#[must_use]
pub fn effective_target_bytes(&self) -> u64 {
self.state
.config
.target_bytes()
.saturating_sub(self.state.active_lease_bytes.load(Ordering::Relaxed))
}
#[must_use]
pub fn executor_spill_run_buffer_bytes(&self) -> usize {
let bytes = (self.effective_target_bytes() / EXECUTOR_SPILL_TARGET_DIVISOR).clamp(
MIN_EXECUTOR_SPILL_RUN_BUFFER_BYTES,
MAX_EXECUTOR_SPILL_RUN_BUFFER_BYTES,
);
usize::try_from(bytes).expect("executor spill run buffer fits usize")
}
#[must_use]
pub fn result_channel_bytes(&self) -> usize {
let bytes = (self.effective_target_bytes() / RESULT_CHANNEL_TARGET_DIVISOR)
.clamp(MIN_RESULT_CHANNEL_BYTES, MAX_RESULT_CHANNEL_BYTES);
usize::try_from(bytes).expect("result channel size fits usize")
}
#[must_use]
pub fn tick_interval(&self) -> Duration {
self.state.config.tick()
}
#[must_use]
pub fn hard_ceiling_bytes(&self) -> Option<u64> {
self.state.config.hard_ceiling_bytes()
}
}