use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use tokio::sync::{OwnedSemaphorePermit, Semaphore, TryAcquireError};
use tokio::time::{timeout_at, Instant};
use super::MAX_FRAME_BYTES;
pub(crate) const NODE_PEER_QUEUE_BYTE_CAPACITY: usize = 256 * 1024 * 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PeerWriterDiagnostics {
capacity_bytes: usize,
admitted_bytes: usize,
producer_admitted_bytes: usize,
saturation_events: u64,
}
impl PeerWriterDiagnostics {
pub fn capacity_bytes(&self) -> usize {
self.capacity_bytes
}
pub fn admitted_bytes(&self) -> usize {
self.admitted_bytes
}
pub fn producer_admitted_bytes(&self) -> usize {
self.producer_admitted_bytes
}
pub fn saturation_events(&self) -> u64 {
self.saturation_events
}
}
pub(crate) struct GlobalBytePermits {
pub(super) total: OwnedSemaphorePermit,
pub(super) producer: Option<OwnedSemaphorePermit>,
}
pub(crate) struct NodePeerByteBudget {
capacity: usize,
total: Arc<Semaphore>,
producers: Arc<Semaphore>,
saturations: AtomicU64,
}
impl NodePeerByteBudget {
pub(crate) fn new(capacity: usize) -> Arc<Self> {
assert!(capacity >= 2 * MAX_FRAME_BYTES);
Arc::new(Self {
capacity,
total: Arc::new(Semaphore::new(capacity)),
producers: Arc::new(Semaphore::new(capacity - MAX_FRAME_BYTES)),
saturations: AtomicU64::new(0),
})
}
fn saturated(&self) {
let mut observed = self.saturations.load(Ordering::Relaxed);
loop {
match self.saturations.compare_exchange_weak(
observed,
observed.saturating_add(1),
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => return,
Err(actual) => observed = actual,
}
}
}
pub(crate) fn diagnostics(&self) -> PeerWriterDiagnostics {
PeerWriterDiagnostics {
capacity_bytes: self.capacity,
admitted_bytes: self.capacity.saturating_sub(self.total.available_permits()),
producer_admitted_bytes: (self.capacity - MAX_FRAME_BYTES)
.saturating_sub(self.producers.available_permits()),
saturation_events: self.saturations.load(Ordering::Relaxed),
}
}
pub(crate) fn try_response(&self, bytes: u32) -> Result<GlobalBytePermits, ()> {
match self.total.clone().try_acquire_many_owned(bytes) {
Ok(total) => Ok(GlobalBytePermits {
total,
producer: None,
}),
Err(TryAcquireError::NoPermits) => {
self.saturated();
Err(())
}
Err(TryAcquireError::Closed) => Err(()),
}
}
async fn acquire(
&self,
semaphore: &Arc<Semaphore>,
bytes: u32,
deadline: Instant,
) -> Result<OwnedSemaphorePermit, ()> {
if Instant::now() >= deadline {
return Err(());
}
match semaphore.clone().try_acquire_many_owned(bytes) {
Ok(permit) => Ok(permit),
Err(TryAcquireError::Closed) => Err(()),
Err(TryAcquireError::NoPermits) => {
self.saturated();
timeout_at(deadline, semaphore.clone().acquire_many_owned(bytes))
.await
.map_err(|_| ())?
.map_err(|_| ())
}
}
}
pub(crate) async fn acquire_producer(
&self,
bytes: u32,
deadline: Instant,
) -> Result<GlobalBytePermits, ()> {
let producer = self.acquire(&self.producers, bytes, deadline).await?;
let total = self.acquire(&self.total, bytes, deadline).await?;
Ok(GlobalBytePermits {
total,
producer: Some(producer),
})
}
}