1use crate::context::PluginContext;
12use crate::error::PluginError;
13use crate::forwarder::{make_forwarder, Forwarder};
14use crate::log_plugin::{LogData, EVENT_LOG, LOG_LEVEL_WARN};
15use crate::plugin::{CLOSE_JOIN_TIMEOUT, Plugin, valid_plugin_name};
16use crate::registry::{EntryStats, PluginEntry, PluginStateCell, PluginStateCode, Registry};
17use crate::worker::spawn_worker;
18use ek_event::{EventEngine, HandlerId};
19use std::any::Any;
20use std::sync::atomic::Ordering;
21use std::sync::{Arc, Mutex, RwLock};
22use std::time::{Duration, Instant};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum PluginState {
27 Ready,
28 Running,
29 Stopping,
30 Exited,
31 Quarantined,
32}
33
34impl PluginState {
35 pub(crate) fn from_code(c: u8) -> Self {
36 match c {
37 0 => Self::Ready,
38 1 => Self::Running,
39 2 => Self::Stopping,
40 3 => Self::Exited,
41 _ => Self::Quarantined,
42 }
43 }
44 fn from_code_enum(code: PluginStateCode) -> Self {
45 Self::from_code(code as u8)
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct PluginInfo {
52 pub name: String,
53 pub state: PluginState,
54 pub deps: Vec<String>,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
59pub struct MailStats {
60 pub spilled: u64,
62 pub panics: u64,
64}
65
66fn log_event(source: &str, msg: String) -> ek_event::Event {
67 ek_event::Event::with_data(
68 EVENT_LOG,
69 Arc::new(LogData {
70 level: LOG_LEVEL_WARN,
71 source: source.to_string(),
72 msg,
73 }),
74 )
75}
76
77pub struct MainEngine {
78 event: Arc<EventEngine>,
79 registry: Arc<Registry>,
80 started_order: Mutex<Option<Vec<String>>>,
82 closed: RwLock<bool>,
83}
84
85impl MainEngine {
86 pub fn new(event: Option<Arc<EventEngine>>) -> Self {
88 Self {
89 event: event.unwrap_or_else(|| Arc::new(EventEngine::new())),
90 registry: Arc::new(Registry::new()),
91 started_order: Mutex::new(None),
92 closed: RwLock::new(false),
93 }
94 }
95
96 pub fn event_engine(&self) -> &Arc<EventEngine> {
98 &self.event
99 }
100
101 fn closed_check(&self) -> Result<(), PluginError> {
102 if *self.closed.read().unwrap() {
103 Err(PluginError::Stopped)
104 } else {
105 Ok(())
106 }
107 }
108
109 fn host_log(&self, msg: String) {
110 let _ = self.event.put(log_event("ek-core", msg));
111 }
112
113 pub fn add_plugin(&self, plugin: Arc<impl Plugin + 'static>) -> Result<(), PluginError> {
115 self.closed_check()?;
116 if self.started_order.lock().unwrap().is_some() {
117 return Err(PluginError::HostStarted);
118 }
119 let name = plugin.name().to_string();
120 if !valid_plugin_name(&name) {
121 return Err(PluginError::IllegalName(name));
122 }
123 let deps: Vec<String> = plugin.deps().iter().map(|d| d.to_string()).collect();
124 if deps.iter().any(|d| d == &name) {
125 return Err(PluginError::DepCycle(vec![name]));
126 }
127 let mailbox_cap = plugin.mailbox_cap();
128 let any: Arc<dyn Any + Send + Sync> = Arc::clone(&plugin) as Arc<dyn Any + Send + Sync>;
129 let dyn_plugin: Arc<dyn Plugin> = plugin;
130
131 let mut w = self.registry.state_of.write().unwrap();
132 if w.entries.contains_key(&name) {
133 return Err(PluginError::Duplicate(name));
134 }
135 for d in &deps {
136 if !w.entries.contains_key(d) {
137 return Err(PluginError::UnknownDep(d.clone()));
138 }
139 }
140 let entry = Arc::new(PluginEntry {
141 name: name.clone(),
142 plugin: dyn_plugin,
143 any,
144 state: PluginStateCell::new(PluginStateCode::Ready),
145 mailbox_cap,
146 deps,
147 mailbox: Mutex::new(None),
148 forwarder_ids: Mutex::new(Vec::new()),
149 worker: Mutex::new(None),
150 stats: Arc::new(EntryStats::default()),
151 });
152 w.order.push(entry.name.clone());
153 w.entries.insert(entry.name.clone(), entry);
154 Ok(())
155 }
156
157 pub fn get_state(&self, name: &str) -> Option<PluginState> {
159 self.registry.get(name).map(|e| e.state.get())
160 }
161
162 pub fn get_plugin(&self, name: &str) -> Option<PluginInfo> {
164 self.registry.get(name).map(|e| PluginInfo {
165 name: e.name.clone(),
166 state: e.state.get(),
167 deps: e.deps.clone(),
168 })
169 }
170
171 pub fn get_typed<T: Plugin + 'static>(&self, name: &str) -> Option<Arc<T>> {
173 let entry = self.registry.get(name)?;
174 Arc::downcast::<T>(Arc::clone(&entry.any)).ok()
175 }
176
177 pub fn plugin_names(&self) -> Vec<String> {
179 self.registry.names()
180 }
181
182 pub fn mail_stats(&self, name: &str) -> Option<MailStats> {
184 self.registry.get(name).map(|e| MailStats {
185 spilled: e.stats.spilled.load(Ordering::Acquire),
186 panics: e.stats.panics.load(Ordering::Acquire),
187 })
188 }
189
190 pub fn start(&self) -> Result<(), PluginError> {
194 let mut started = self.started_order.lock().unwrap();
195 if started.is_some() {
196 return Ok(());
197 }
198 self.closed_check()?;
199 self.event.start();
200 let order = self.registry.topo_order()?;
201 let mut launched = Vec::with_capacity(order.len());
202 for entry in order {
203 let ctx = Arc::new(PluginContext::new(
204 Arc::clone(&self.event),
205 Arc::clone(&self.registry),
206 entry.name.clone(),
207 ));
208 entry.plugin.on_init(&ctx);
209 let (tx, rx) = crossbeam_channel::bounded::<ek_event::Event>(entry.mailbox_cap);
210 *entry.mailbox.lock().unwrap() = Some(tx.clone());
211 let handler = spawn_worker(
212 Arc::clone(&entry.plugin),
213 rx,
214 entry.state.clone(),
215 Arc::clone(&entry.stats),
216 Arc::clone(&ctx),
217 Arc::clone(&self.event),
218 );
219 *entry.worker.lock().unwrap() = Some(handler);
220 let _ = entry.state.cas(PluginStateCode::Ready, PluginStateCode::Running);
222 let mut ids: Vec<HandlerId> = Vec::new();
223 for ty in entry.plugin.routes() {
224 let fwd = Forwarder::new(
225 tx.clone(),
226 Arc::clone(&entry.stats),
227 Arc::clone(&self.event),
228 entry.name.clone(),
229 );
230 let id = self.event.register(ty.into_owned(), make_forwarder(fwd));
231 ids.push(id);
232 }
233 *entry.forwarder_ids.lock().unwrap() = ids;
234 entry.plugin.on_start();
235 launched.push(entry.name.clone());
236 }
237 *started = Some(launched);
238 Ok(())
239 }
240
241 fn close_one(&self, entry: &Arc<PluginEntry>, deadline: Instant) {
247 let pre_state = entry.state.get();
248 let call_stop = pre_state != PluginState::Ready;
249 let _ = entry.state.cas(PluginStateCode::Running, PluginStateCode::Stopping);
251 let ids: Vec<HandlerId> = std::mem::take(&mut *entry.forwarder_ids.lock().unwrap());
253 for id in ids {
254 self.event.unregister(id);
255 }
256 if call_stop {
258 entry.plugin.on_stop();
259 }
260 *entry.mailbox.lock().unwrap() = None;
262 while !entry.stats.done.load(Ordering::Acquire) && Instant::now() < deadline {
264 std::thread::sleep(Duration::from_millis(2));
265 }
266 if let Some(handle) = entry.worker.lock().unwrap().take() {
267 if entry.stats.done.load(Ordering::Acquire) {
268 let _ = handle.join();
269 } else {
270 self.host_log(format!(
271 "close timeout: worker of {:?} still busy; detach and keep draining",
272 entry.name
273 ));
274 }
275 }
276 let _ = entry.state.cas(PluginStateCode::Stopping, PluginStateCode::Exited);
278 }
279
280 pub fn close(&self, timeout: Duration) {
282 let order = self.started_order.lock().unwrap().take();
283 *self.closed.write().unwrap() = true;
284 let deadline = Instant::now() + timeout.max(Duration::from_millis(1));
285 if let Some(order) = order {
286 for name in order.iter().rev() {
287 if let Some(entry) = self.registry.get(name) {
288 self.close_one(&entry, deadline);
289 }
290 }
291 }
292 self.registry.clear();
293 }
294
295 pub fn close_all(&self, timeout: Duration) {
297 self.close(timeout);
298 self.event.stop();
299 }
300
301 pub fn remove_plugin(&self, name: &str) -> Result<(), PluginError> {
304 self.closed_check()?;
305 let entry = self
306 .registry
307 .get(name)
308 .ok_or_else(|| PluginError::UnknownName(name.to_string()))?;
309 let dependents = self.registry.any_depends_on(name);
310 if !dependents.is_empty() {
311 self.host_log(format!(
312 "remove_plugin {:?} still depended on by {:?}",
313 name, dependents
314 ));
315 }
316 let deadline = Instant::now() + CLOSE_JOIN_TIMEOUT;
317 if entry.state.get() == PluginState::from_code_enum(PluginStateCode::Running) {
318 self.close_one(&entry, deadline);
319 } else {
320 let ids: Vec<HandlerId> = std::mem::take(&mut *entry.forwarder_ids.lock().unwrap());
322 for id in ids {
323 self.event.unregister(id);
324 }
325 *entry.mailbox.lock().unwrap() = None;
326 }
327 self.registry
328 .remove(name)
329 .ok_or_else(|| PluginError::UnknownName(name.to_string()))?;
330 Ok(())
331 }
332}