1use crate::context::PluginContext;
12use crate::error::PluginError;
13use crate::forwarder::{Forwarder, make_forwarder};
14use crate::lifecycle::{
15 EVENT_PLUG_DOWN, EVENT_PLUG_DOWN_EVENT_PREFIX, EVENT_PLUG_UP, EVENT_PLUG_UP_PREFIX,
16};
17use crate::plugin::{CLOSE_JOIN_TIMEOUT, Plugin, valid_plugin_name};
18use crate::registry::{EntryStats, PluginEntry, PluginStateCell, PluginStateCode, Registry};
19use crate::service::PlugNotice;
20use crate::worker::spawn_worker;
21use ek_event::{EventEngine, HandlerId};
22use std::any::Any;
23use std::sync::atomic::Ordering;
24use std::sync::{Arc, Mutex, RwLock};
25use std::time::{Duration, Instant};
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum PluginState {
30 Ready,
31 Running,
32 Stopping,
33 Exited,
34 Quarantined,
35}
36
37impl PluginState {
38 pub(crate) fn from_code(c: u8) -> Self {
39 match c {
40 0 => Self::Ready,
41 1 => Self::Running,
42 2 => Self::Stopping,
43 3 => Self::Exited,
44 _ => Self::Quarantined,
45 }
46 }
47 fn from_code_enum(code: PluginStateCode) -> Self {
48 Self::from_code(code as u8)
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct PluginInfo {
55 pub name: String,
56 pub version: String,
58 pub state: PluginState,
59 pub deps: Vec<String>,
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
64pub struct MailStats {
65 pub spilled: u64,
67 pub panics: u64,
69}
70
71pub struct MainEngine {
72 event: Arc<EventEngine>,
73 registry: Arc<Registry>,
74 started_order: Mutex<Option<Vec<String>>>,
76 closed: RwLock<bool>,
77}
78
79impl MainEngine {
80 pub fn new(event: Option<Arc<EventEngine>>) -> Self {
82 Self {
83 event: event.unwrap_or_else(|| Arc::new(EventEngine::new())),
84 registry: Arc::new(Registry::new()),
85 started_order: Mutex::new(None),
86 closed: RwLock::new(false),
87 }
88 }
89
90 pub fn event_engine(&self) -> &Arc<EventEngine> {
92 &self.event
93 }
94
95 fn closed_check(&self) -> Result<(), PluginError> {
96 if *self.closed.read().unwrap() {
97 Err(PluginError::Stopped)
98 } else {
99 Ok(())
100 }
101 }
102
103 pub fn add_plugin(&self, plugin: Arc<impl Plugin + 'static>) -> Result<(), PluginError> {
110 self.closed_check()?;
111 let name = plugin.name().to_string();
112 if !valid_plugin_name(&name) {
113 return Err(PluginError::IllegalName(name));
114 }
115 let deps: Vec<String> = plugin.deps().iter().map(|d| d.to_string()).collect();
116 if deps.iter().any(|d| d == &name) {
117 return Err(PluginError::DepCycle(vec![name]));
118 }
119 let mailbox_cap = plugin.mailbox_cap();
120 let any: Arc<dyn Any + Send + Sync> = Arc::clone(&plugin) as Arc<dyn Any + Send + Sync>;
121 let dyn_plugin: Arc<dyn Plugin> = plugin;
122 let svc_ids: Vec<String> = {
124 let mut ids = Vec::new();
125 for d in dyn_plugin.services() {
126 if !ids.contains(&d.id) {
127 ids.push(d.id);
128 }
129 }
130 ids
131 };
132
133 let mut w = self.registry.state_of.write().unwrap();
134 if w.entries.contains_key(&name) {
135 return Err(PluginError::Duplicate(name));
136 }
137 let entry = Arc::new(PluginEntry {
138 name: name.clone(),
139 plugin: dyn_plugin,
140 any,
141 state: PluginStateCell::new(PluginStateCode::Ready),
142 mailbox_cap,
143 deps,
144 mailbox: Mutex::new(None),
145 forwarder_ids: Mutex::new(Vec::new()),
146 worker: Mutex::new(None),
147 stats: Arc::new(EntryStats::default()),
148 });
149 w.order.push(entry.name.clone());
150 w.entries.insert(entry.name.clone(), entry);
151 drop(w);
152 self.registry.index_put(&name, &svc_ids);
153 self.registry.invalidate(&name);
154
155 let mut started = self.started_order.lock().unwrap();
157 if started.is_some() {
158 let entry = self.registry.get(&name).expect("just inserted");
159 self.launch_one(&entry);
160 started.as_mut().expect("checked Some").push(name);
161 }
162 Ok(())
163 }
164
165 pub fn get_state(&self, name: &str) -> Option<PluginState> {
167 self.registry.get(name).map(|e| e.state.get())
168 }
169
170 pub fn get_plugin(&self, name: &str) -> Option<PluginInfo> {
172 self.registry.info_of(name)
173 }
174
175 pub fn get_typed<T: Plugin + 'static>(&self, name: &str) -> Option<Arc<T>> {
177 let entry = self.registry.get(name)?;
178 Arc::downcast::<T>(Arc::clone(&entry.any)).ok()
179 }
180
181 pub fn plugin_names(&self) -> Vec<String> {
183 self.registry.names()
184 }
185
186 pub fn mail_stats(&self, name: &str) -> Option<MailStats> {
188 self.registry.get(name).map(|e| MailStats {
189 spilled: e.stats.spilled.load(Ordering::Acquire),
190 panics: e.stats.panics.load(Ordering::Acquire),
191 })
192 }
193
194 pub fn start(&self) -> Result<(), PluginError> {
199 let mut started = self.started_order.lock().unwrap();
200 if started.is_some() {
201 return Ok(());
202 }
203 self.closed_check()?;
204 self.event.start();
205 let order = self.registry.topo_order()?;
206 let mut launched = Vec::with_capacity(order.len());
207 for entry in order {
208 self.launch_one(&entry);
209 launched.push(entry.name.clone());
210 }
211 *started = Some(launched);
212 Ok(())
213 }
214
215 fn launch_one(&self, entry: &Arc<PluginEntry>) {
220 let ctx = Arc::new(PluginContext::new(
221 Arc::clone(&self.event),
222 Arc::clone(&self.registry),
223 entry.name.clone(),
224 ));
225 entry.plugin.on_init(&ctx);
226 let (tx, rx) = crossbeam_channel::bounded::<ek_event::Event>(entry.mailbox_cap);
227 *entry.mailbox.lock().unwrap() = Some(tx.clone());
228 let handler = spawn_worker(
229 Arc::clone(&entry.plugin),
230 rx,
231 entry.state.clone(),
232 Arc::clone(&entry.stats),
233 Arc::clone(&self.event),
234 );
235 *entry.worker.lock().unwrap() = Some(handler);
236 let _ = entry
238 .state
239 .cas(PluginStateCode::Ready, PluginStateCode::Running);
240 let mut ids: Vec<HandlerId> = Vec::new();
241 for ty in entry.plugin.routes() {
242 let fwd = Forwarder::new(tx.clone(), Arc::clone(&entry.stats), entry.name.clone());
243 let id = self.event.register(ty.into_owned(), make_forwarder(fwd));
244 ids.push(id);
245 }
246 *entry.forwarder_ids.lock().unwrap() = ids;
247 entry.plugin.on_start();
248
249 let version = entry.plugin.version().into_owned();
250 let _ = self.event.put(ek_event::Event::with_data(
251 format!("{EVENT_PLUG_UP_PREFIX}{}", entry.name),
252 Arc::new(version.clone()),
253 ));
254 let _ = self.event.put(ek_event::Event::with_data(
255 EVENT_PLUG_UP,
256 Arc::new(PlugNotice {
257 name: entry.name.clone(),
258 version,
259 }),
260 ));
261 }
262
263 fn close_one(&self, entry: &Arc<PluginEntry>, deadline: Instant) {
269 let pre_state = entry.state.get();
270 let call_stop = pre_state != PluginState::Ready;
271 let _ = entry
273 .state
274 .cas(PluginStateCode::Running, PluginStateCode::Stopping);
275 let ids: Vec<HandlerId> = std::mem::take(&mut *entry.forwarder_ids.lock().unwrap());
277 for id in ids {
278 self.event.unregister(id);
279 }
280 if call_stop {
282 entry.plugin.on_stop();
283 }
284 *entry.mailbox.lock().unwrap() = None;
286 while !entry.stats.done.load(Ordering::Acquire) && Instant::now() < deadline {
288 std::thread::sleep(Duration::from_millis(2));
289 }
290 if let Some(handle) = entry.worker.lock().unwrap().take() {
291 if entry.stats.done.load(Ordering::Acquire) {
292 let _ = handle.join();
293 } else {
294 eprintln!(
296 "ek-core: close timeout: worker of {:?} still busy; detach and keep draining",
297 entry.name
298 );
299 }
300 }
301 let _ = entry
303 .state
304 .cas(PluginStateCode::Stopping, PluginStateCode::Exited);
305 }
306
307 pub fn close(&self, timeout: Duration) {
309 let order = self.started_order.lock().unwrap().take();
310 *self.closed.write().unwrap() = true;
311 let deadline = Instant::now() + timeout.max(Duration::from_millis(1));
312 if let Some(order) = order {
313 for name in order.iter().rev() {
314 if let Some(entry) = self.registry.get(name) {
315 self.close_one(&entry, deadline);
316 }
317 }
318 }
319 self.registry.clear();
320 }
321
322 pub fn close_all(&self, timeout: Duration) {
324 self.close(timeout);
325 self.event.stop();
326 }
327
328 pub fn remove_plugin(&self, name: &str) -> Result<(), PluginError> {
335 self.closed_check()?;
336 let entry = self
337 .registry
338 .get(name)
339 .ok_or_else(|| PluginError::UnknownName(name.to_string()))?;
340 let dependents = self.registry.any_depends_on(name);
341 if !dependents.is_empty() {
342 eprintln!(
343 "ek-core: remove_plugin {:?} still depended on by {:?}",
344 name, dependents
345 );
346 }
347 let deadline = Instant::now() + CLOSE_JOIN_TIMEOUT;
348 if entry.state.get() == PluginState::from_code_enum(PluginStateCode::Running) {
349 self.close_one(&entry, deadline);
350 } else {
351 let ids: Vec<HandlerId> = std::mem::take(&mut *entry.forwarder_ids.lock().unwrap());
353 for id in ids {
354 self.event.unregister(id);
355 }
356 *entry.mailbox.lock().unwrap() = None;
357 }
358 let version = entry.plugin.version().into_owned();
359 self.registry
360 .remove(name)
361 .ok_or_else(|| PluginError::UnknownName(name.to_string()))?;
362 self.registry.index_remove(name);
364 self.registry.invalidate(name);
365 let _ = self.event.put(ek_event::Event::with_data(
366 format!("{EVENT_PLUG_DOWN_EVENT_PREFIX}{name}"),
367 Arc::new(version.clone()),
368 ));
369 let _ = self.event.put(ek_event::Event::with_data(
370 EVENT_PLUG_DOWN,
371 Arc::new(PlugNotice {
372 name: name.to_string(),
373 version,
374 }),
375 ));
376 Ok(())
377 }
378}