use crate::registry::EntryStats;
use ek_event::{Event, HandlerFn};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use crossbeam_channel::{Sender, TrySendError};
pub const SPILL_REPORT_EVERY: u64 = 128;
pub(crate) struct Forwarder {
tx: Sender<Event>,
alive: AtomicBool,
stats: Arc<EntryStats>,
reported: AtomicU64,
target: String,
}
impl Forwarder {
pub(crate) fn new(tx: Sender<Event>, stats: Arc<EntryStats>, target: String) -> Arc<Self> {
Arc::new(Self {
tx,
alive: AtomicBool::new(true),
stats,
reported: AtomicU64::new(0),
target,
})
}
pub fn forward(&self, event: &Event) {
if !self.alive.load(Ordering::Acquire) {
return;
}
let event2 = Event::with_data(event.event_type.clone(), Arc::clone(&event.data));
match self.tx.try_send(event2) {
Ok(()) => {}
Err(TrySendError::Full(_)) => self.on_spill(),
Err(TrySendError::Disconnected(_)) => {
self.alive.store(false, Ordering::Release);
}
}
}
fn on_spill(&self) {
let total = self.stats.spilled.fetch_add(1, Ordering::AcqRel) + 1;
let last = self.reported.load(Ordering::Acquire);
if total >= last + SPILL_REPORT_EVERY
&& self
.reported
.compare_exchange(last, total, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
eprintln!(
"ek-core: mailbox overflow: plugin {:?} spilled {} events in total",
self.target, total
);
}
}
}
pub(crate) fn make_forwarder(fwd: Arc<Forwarder>) -> HandlerFn {
Arc::new(move |event: &Event| fwd.forward(event))
}