use crate::{TelemetryError, TelemetryResult};
use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
pub type MemoryAdmissionProbe = extern "C" fn(usize, usize) -> bool;
static PROBE: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut());
static REJECTED: AtomicUsize = AtomicUsize::new(0);
pub fn set_probe(probe: Option<MemoryAdmissionProbe>) {
PROBE.store(
probe.map_or(core::ptr::null_mut(), |f| f as *mut ()),
Ordering::Release,
);
}
pub fn rejected_operations() -> usize {
REJECTED.load(Ordering::Relaxed)
}
pub(crate) fn check(additional_bytes: usize, largest_allocation: usize) -> TelemetryResult<()> {
let ptr = PROBE.load(Ordering::Acquire);
if ptr.is_null() {
return Ok(());
}
let probe: MemoryAdmissionProbe = unsafe { core::mem::transmute(ptr) };
if probe(additional_bytes, largest_allocation) {
return Ok(());
}
REJECTED.fetch_add(1, Ordering::Relaxed);
Err(TelemetryError::Io("memory pressure"))
}
pub(crate) fn check_receive(bytes: &[u8]) -> TelemetryResult<()> {
if PROBE.load(Ordering::Relaxed).is_null() {
return Ok(());
}
let ack = crate::wire_format::peek_routing_frame_info(bytes)
.ok()
.is_some_and(|frame| {
frame.ack_only()
|| matches!(
frame.envelope.ty,
crate::DataType::ReliableAck | crate::DataType::ReliablePartialAck
)
});
check_frame(bytes.len(), ack)
}
pub(crate) fn check_frame(len: usize, ack: bool) -> TelemetryResult<()> {
if ack && len <= 128 {
check(512, len.saturating_add(32))
} else {
check(
len.saturating_mul(3).saturating_add(2048),
len.saturating_add(32),
)
}
}
#[unsafe(no_mangle)]
pub extern "C" fn seds_set_memory_admission_probe(probe: Option<MemoryAdmissionProbe>) {
set_probe(probe);
}
#[unsafe(no_mangle)]
pub extern "C" fn seds_memory_admission_rejected() -> usize {
rejected_operations()
}
pub(crate) fn allow_drain<T: Default>(result: TelemetryResult<T>) -> TelemetryResult<T> {
match result {
Err(TelemetryError::Io("memory pressure")) => Ok(T::default()),
other => other,
}
}