use crate::context::PluginContext;
use crate::log_plugin::{EVENT_PLUG_DOWN_PREFIX, LOG_LEVEL_ERROR, LOG_LEVEL_WARN};
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>,
ctx: Arc<PluginContext>,
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, ctx, bus))
.expect("ek-core: spawn worker thread")
}
fn worker_loop(
plugin: Arc<dyn Plugin>,
rx: Receiver<Event>,
state: PluginStateCell,
stats: Arc<EntryStats>,
ctx: Arc<PluginContext>,
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) {
ctx.write_log(
LOG_LEVEL_ERROR,
&format!(
"plugin {:?} quarantined after {} consecutive handler panics",
plugin.name(),
consecutive
),
);
let _ = bus.put(Event::new(format!(
"{EVENT_PLUG_DOWN_PREFIX}{}",
plugin.name()
)));
}
break;
}
ctx.write_log(
LOG_LEVEL_WARN,
&format!(
"plugin {:?} handler panicked ({consecutive}/{PANIC_LIMIT})",
plugin.name()
),
);
}
}
drop(event);
}
Err(_disconnected) => {
state.finish();
break;
}
}
}
stats.done.store(true, Ordering::Release);
}