use crate::lifecycle::EVENT_PLUG_DOWN_PREFIX;
use crate::plugin::{PANIC_LIMIT, Plugin};
use crate::registry::{EntryStats, PluginStateCell, PluginStateCode};
use crossbeam_channel::Receiver;
use ek_event::{Event, EventEngine};
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::thread::JoinHandle;
pub(crate) fn spawn_worker(
plugin: Arc<dyn Plugin>,
rx: Receiver<Event>,
state: PluginStateCell,
stats: Arc<EntryStats>,
bus: Arc<EventEngine>,
) -> JoinHandle<()> {
let name = plugin.name().to_string();
std::thread::Builder::new()
.name(format!("ek-worker.{name}"))
.spawn(move || worker_loop(plugin, rx, state, stats, bus))
.expect("ek-core: spawn worker thread")
}
fn worker_loop(
plugin: Arc<dyn Plugin>,
rx: Receiver<Event>,
state: PluginStateCell,
stats: Arc<EntryStats>,
bus: Arc<EventEngine>,
) {
let mut consecutive: u64 = 0;
loop {
match rx.recv() {
Ok(event) => {
let result = catch_unwind(AssertUnwindSafe(|| plugin.on_mail(&event)));
match result {
Ok(()) => consecutive = 0,
Err(_panic) => {
consecutive += 1;
let _ = stats.panics.fetch_add(1, Ordering::AcqRel);
if consecutive >= PANIC_LIMIT as u64 {
if state.cas(PluginStateCode::Running, PluginStateCode::Quarantined) {
eprintln!(
"ek-core: plugin {:?} quarantined after {} consecutive handler panics",
plugin.name(),
consecutive
);
let _ = bus.put(Event::new(format!(
"{EVENT_PLUG_DOWN_PREFIX}{}",
plugin.name()
)));
}
break;
}
eprintln!(
"ek-core: plugin {:?} handler panicked ({consecutive}/{PANIC_LIMIT})",
plugin.name()
);
}
}
drop(event);
}
Err(_disconnected) => {
state.finish();
break;
}
}
}
stats.done.store(true, Ordering::Release);
}