1use crate::lifecycle::EVENT_PLUG_DOWN_PREFIX;
8use crate::plugin::{PANIC_LIMIT, Plugin};
9use crate::registry::{EntryStats, PluginStateCell, PluginStateCode};
10use crossbeam_channel::Receiver;
11use ek_event::{Event, EventEngine};
12use std::panic::{AssertUnwindSafe, catch_unwind};
13use std::sync::Arc;
14use std::sync::atomic::Ordering;
15use std::thread::JoinHandle;
16
17pub(crate) fn spawn_worker(
18 plugin: Arc<dyn Plugin>,
19 rx: Receiver<Event>,
20 state: PluginStateCell,
21 stats: Arc<EntryStats>,
22 bus: Arc<EventEngine>,
23) -> JoinHandle<()> {
24 let name = plugin.name().to_string();
25 std::thread::Builder::new()
26 .name(format!("ek-worker.{name}"))
27 .spawn(move || worker_loop(plugin, rx, state, stats, bus))
28 .expect("ek-core: spawn worker thread")
29}
30
31fn worker_loop(
32 plugin: Arc<dyn Plugin>,
33 rx: Receiver<Event>,
34 state: PluginStateCell,
35 stats: Arc<EntryStats>,
36 bus: Arc<EventEngine>,
37) {
38 let mut consecutive: u64 = 0;
41 loop {
42 match rx.recv() {
43 Ok(event) => {
44 let result = catch_unwind(AssertUnwindSafe(|| plugin.on_mail(&event)));
45 match result {
46 Ok(()) => consecutive = 0,
47 Err(_panic) => {
48 consecutive += 1;
49 let _ = stats.panics.fetch_add(1, Ordering::AcqRel);
50 if consecutive >= PANIC_LIMIT as u64 {
51 if state.cas(PluginStateCode::Running, PluginStateCode::Quarantined) {
52 eprintln!(
54 "ek-core: plugin {:?} quarantined after {} consecutive handler panics",
55 plugin.name(),
56 consecutive
57 );
58 let _ = bus.put(Event::new(format!(
60 "{EVENT_PLUG_DOWN_PREFIX}{}",
61 plugin.name()
62 )));
63 }
64 break;
66 }
67 eprintln!(
69 "ek-core: plugin {:?} handler panicked ({consecutive}/{PANIC_LIMIT})",
70 plugin.name()
71 );
72 }
73 }
74 drop(event);
76 }
77 Err(_disconnected) => {
79 state.finish();
80 break;
81 }
82 }
83 }
84 stats.done.store(true, Ordering::Release);
85}