use crate::log_plugin::{LogData, EVENT_LOG, LOG_LEVEL_WARN};
use crate::registry::EntryStats;
use ek_event::{Event, EventEngine, HandlerFn};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Arc;
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,
bus: Arc<EventEngine>,
target: String,
}
impl Forwarder {
pub(crate) fn new(
tx: Sender<Event>,
stats: Arc<EntryStats>,
bus: Arc<EventEngine>,
target: String,
) -> Arc<Self> {
Arc::new(Self {
tx,
alive: AtomicBool::new(true),
stats,
reported: AtomicU64::new(0),
bus,
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()
{
let data = LogData {
level: LOG_LEVEL_WARN,
source: "ek-core".to_string(),
msg: format!(
"mailbox overflow: plugin {:?} spilled {} events in total",
self.target, total
),
};
let _ = self.bus.put(Event::with_data(EVENT_LOG, Arc::new(data)));
}
}
}
pub(crate) fn make_forwarder(fwd: Arc<Forwarder>) -> HandlerFn {
Arc::new(move |event: &Event| fwd.forward(event))
}