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)]
67pub struct MailStats {
68 pub spilled: u64,
70 pub panics: u64,
72 pub pending: u64,
74 pub peak: u64,
76 pub soft_cap: u64,
78}
79
80pub struct MainEngine {
81 event: Arc<EventEngine>,
82 registry: Arc<Registry>,
83 started_order: Mutex<Option<Vec<String>>>,
85 closed: RwLock<bool>,
86}
87
88impl MainEngine {
89 pub fn new(event: Option<Arc<EventEngine>>) -> Self {
91 Self {
92 event: event.unwrap_or_else(|| Arc::new(EventEngine::new())),
93 registry: Arc::new(Registry::new()),
94 started_order: Mutex::new(None),
95 closed: RwLock::new(false),
96 }
97 }
98
99 pub fn event_engine(&self) -> &Arc<EventEngine> {
101 &self.event
102 }
103
104 fn closed_check(&self) -> Result<(), PluginError> {
105 if *self.closed.read().unwrap() {
106 Err(PluginError::Stopped)
107 } else {
108 Ok(())
109 }
110 }
111
112 pub fn add_plugin(&self, plugin: Arc<impl Plugin + 'static>) -> Result<(), PluginError> {
119 self.closed_check()?;
120 let name = plugin.name().to_string();
121 if !valid_plugin_name(&name) {
122 return Err(PluginError::IllegalName(name));
123 }
124 let deps: Vec<String> = plugin.deps().iter().map(|d| d.to_string()).collect();
125 if deps.iter().any(|d| d == &name) {
126 return Err(PluginError::DepCycle(vec![name]));
127 }
128 let soft_cap = plugin.mailbox_soft_cap();
129 let any: Arc<dyn Any + Send + Sync> = Arc::clone(&plugin) as Arc<dyn Any + Send + Sync>;
130 let dyn_plugin: Arc<dyn Plugin> = plugin;
131 let svc_ids: Vec<String> = {
133 let mut ids = Vec::new();
134 for d in dyn_plugin.services() {
135 if !ids.contains(&d.id) {
136 ids.push(d.id);
137 }
138 }
139 ids
140 };
141
142 let mut w = self.registry.state_of.write().unwrap();
143 if w.entries.contains_key(&name) {
144 return Err(PluginError::Duplicate(name));
145 }
146 let entry = Arc::new(PluginEntry {
147 name: name.clone(),
148 plugin: dyn_plugin,
149 any,
150 state: PluginStateCell::new(PluginStateCode::Ready),
151 soft_cap,
152 deps,
153 mailbox: Mutex::new(None),
154 forwarder_ids: Mutex::new(Vec::new()),
155 worker: Mutex::new(None),
156 stats: Arc::new(EntryStats::default()),
157 });
158 w.order.push(entry.name.clone());
159 w.entries.insert(entry.name.clone(), entry);
160 drop(w);
161 self.registry.index_put(&name, &svc_ids);
162 self.registry.invalidate(&name);
163
164 let mut started = self.started_order.lock().unwrap();
166 if started.is_some() {
167 let entry = self.registry.get(&name).expect("just inserted");
168 self.launch_one(&entry);
169 started.as_mut().expect("checked Some").push(name);
170 }
171 Ok(())
172 }
173
174 pub fn get_state(&self, name: &str) -> Option<PluginState> {
176 self.registry.get(name).map(|e| e.state.get())
177 }
178
179 pub fn get_plugin(&self, name: &str) -> Option<PluginInfo> {
181 self.registry.info_of(name)
182 }
183
184 pub fn get_typed<T: Plugin + 'static>(&self, name: &str) -> Option<Arc<T>> {
186 let entry = self.registry.get(name)?;
187 Arc::downcast::<T>(Arc::clone(&entry.any)).ok()
188 }
189
190 pub fn plugin_names(&self) -> Vec<String> {
192 self.registry.names()
193 }
194
195 pub fn mail_stats(&self, name: &str) -> Option<MailStats> {
197 self.registry.get(name).map(|e| MailStats {
198 spilled: e.stats.spilled.load(Ordering::Acquire),
199 panics: e.stats.panics.load(Ordering::Acquire),
200 pending: e.stats.pending.load(Ordering::Acquire),
201 peak: e.stats.peak.load(Ordering::Acquire),
202 soft_cap: e.soft_cap,
203 })
204 }
205
206 pub fn start(&self) -> Result<(), PluginError> {
211 let mut started = self.started_order.lock().unwrap();
212 if started.is_some() {
213 return Ok(());
214 }
215 self.closed_check()?;
216 self.event.start();
217 let order = self.registry.topo_order()?;
218 let mut launched = Vec::with_capacity(order.len());
219 for entry in order {
220 self.launch_one(&entry);
221 launched.push(entry.name.clone());
222 }
223 *started = Some(launched);
224 Ok(())
225 }
226
227 fn launch_one(&self, entry: &Arc<PluginEntry>) {
232 let ctx = Arc::new(PluginContext::new(
233 Arc::clone(&self.event),
234 Arc::clone(&self.registry),
235 entry.name.clone(),
236 ));
237 entry.plugin.on_init(&ctx);
238 let (tx, rx) = crossbeam_channel::unbounded::<ek_event::Event>();
242 *entry.mailbox.lock().unwrap() = Some(tx.clone());
243 let handler = spawn_worker(
244 Arc::clone(&entry.plugin),
245 rx,
246 entry.state.clone(),
247 Arc::clone(&entry.stats),
248 Arc::clone(&self.event),
249 );
250 *entry.worker.lock().unwrap() = Some(handler);
251 let _ = entry
253 .state
254 .cas(PluginStateCode::Ready, PluginStateCode::Running);
255 let mut ids: Vec<HandlerId> = Vec::new();
256 for ty in entry.plugin.routes() {
257 let fwd = Forwarder::new(
258 tx.clone(),
259 Arc::clone(&entry.stats),
260 entry.soft_cap,
261 entry.name.clone(),
262 );
263 let id = self.event.register(ty.into_owned(), make_forwarder(fwd));
264 ids.push(id);
265 }
266 *entry.forwarder_ids.lock().unwrap() = ids;
267 entry.plugin.on_start();
268
269 let version = entry.plugin.version().into_owned();
270 let _ = self.event.put(ek_event::Event::with_data(
271 format!("{EVENT_PLUG_UP_PREFIX}{}", entry.name),
272 Arc::new(version.clone()),
273 ));
274 let _ = self.event.put(ek_event::Event::with_data(
275 EVENT_PLUG_UP,
276 Arc::new(PlugNotice {
277 name: entry.name.clone(),
278 version,
279 }),
280 ));
281 }
282
283 fn close_one(&self, entry: &Arc<PluginEntry>, deadline: Instant) {
289 let pre_state = entry.state.get();
290 let call_stop = pre_state != PluginState::Ready;
291 let _ = entry
293 .state
294 .cas(PluginStateCode::Running, PluginStateCode::Stopping);
295 let ids: Vec<HandlerId> = std::mem::take(&mut *entry.forwarder_ids.lock().unwrap());
297 for id in ids {
298 self.event.unregister(id);
299 }
300 if call_stop {
302 entry.plugin.on_stop();
303 }
304 *entry.mailbox.lock().unwrap() = None;
306 while !entry.stats.done.load(Ordering::Acquire) && Instant::now() < deadline {
308 std::thread::sleep(Duration::from_millis(2));
309 }
310 if let Some(handle) = entry.worker.lock().unwrap().take() {
311 if entry.stats.done.load(Ordering::Acquire) {
312 let _ = handle.join();
313 } else {
314 eprintln!(
316 "ek-core: close timeout: worker of {:?} still busy; detach and keep draining",
317 entry.name
318 );
319 }
320 }
321 let _ = entry
323 .state
324 .cas(PluginStateCode::Stopping, PluginStateCode::Exited);
325 }
326
327 pub fn close(&self, timeout: Duration) {
329 let order = self.started_order.lock().unwrap().take();
330 *self.closed.write().unwrap() = true;
331 let deadline = Instant::now() + timeout.max(Duration::from_millis(1));
332 if let Some(order) = order {
333 for name in order.iter().rev() {
334 if let Some(entry) = self.registry.get(name) {
335 self.close_one(&entry, deadline);
336 }
337 }
338 }
339 self.registry.clear();
340 }
341
342 pub fn close_all(&self, timeout: Duration) {
344 self.close(timeout);
345 self.event.stop();
346 }
347
348 pub fn remove_plugin(&self, name: &str) -> Result<(), PluginError> {
355 self.closed_check()?;
356 let entry = self
357 .registry
358 .get(name)
359 .ok_or_else(|| PluginError::UnknownName(name.to_string()))?;
360 let dependents = self.registry.any_depends_on(name);
361 if !dependents.is_empty() {
362 eprintln!(
363 "ek-core: remove_plugin {:?} still depended on by {:?}",
364 name, dependents
365 );
366 }
367 let deadline = Instant::now() + CLOSE_JOIN_TIMEOUT;
368 if entry.state.get() == PluginState::from_code_enum(PluginStateCode::Running) {
369 self.close_one(&entry, deadline);
370 } else {
371 let ids: Vec<HandlerId> = std::mem::take(&mut *entry.forwarder_ids.lock().unwrap());
373 for id in ids {
374 self.event.unregister(id);
375 }
376 *entry.mailbox.lock().unwrap() = None;
377 }
378 let version = entry.plugin.version().into_owned();
379 self.registry
380 .remove(name)
381 .ok_or_else(|| PluginError::UnknownName(name.to_string()))?;
382 self.registry.index_remove(name);
384 self.registry.invalidate(name);
385 let _ = self.event.put(ek_event::Event::with_data(
386 format!("{EVENT_PLUG_DOWN_EVENT_PREFIX}{name}"),
387 Arc::new(version.clone()),
388 ));
389 let _ = self.event.put(ek_event::Event::with_data(
390 EVENT_PLUG_DOWN,
391 Arc::new(PlugNotice {
392 name: name.to_string(),
393 version,
394 }),
395 ));
396 Ok(())
397 }
398}