Skip to main content

ek_core/
main_engine.rs

1//! `MainEngine` —— 纯插件宿主:单表注册、(typed)获取、(deps)拓扑 start、
2//! 逆序五步 close、单插件 remove(允许+警告)、运行统计查询。零业务方法。
3//!
4//! close 五步(每插件,逆依赖序依次执行):
5//! ①卸 forwarder(unregister 全部 HandlerId)
6//! ②on_stop 钩子(业务清理)
7//! ③关邮箱(宿主侧 Sender 置 None;全部 send 端消费尽 → 信道断开)
8//! ④worker 排空(crossbeam recv 先取尽缓冲再报 Disconnected)
9//! ⑤join 带超时:超时则告警 + 剥离句柄(线程自然排空收尾,Rust 不杀线程)
10
11use 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/// 插件生命周期状态(worker 状态机,AtomicU8 CAS 迁移)。
28#[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/// 插件信息快照(get_plugin / ctx.plugins())。
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct PluginInfo {
55    pub name: String,
56    /// 插件申报版本(空串 = 未声明);A 侧版本策略据此选轨(F5:单一披露面)。
57    pub version: String,
58    pub state: PluginState,
59    pub deps: Vec<String>,
60}
61
62/// 邮箱运行统计(mail_stats)。
63///
64/// 0.5.0 加两深度读数:`pending`(当前积压)与 `peak`(历史峰值)。
65/// 口径 —— 不丢是默认,堵不堵要**看得见**:`spilled` 只在到软顶时才涨。
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
67pub struct MailStats {
68    /// 软顶触发的丢弃累计(默认无界不丢 → 常态应为 0)。
69    pub spilled: u64,
70    /// handler panic 累计。
71    pub panics: u64,
72    /// 当前邮箱深度(发送 +1 / 工人取走 −1)。
73    pub pending: u64,
74    /// 历史峰值(只涨不跌)。
75    pub peak: u64,
76    /// 该插件的软顶(消费侧判"溢满"要吃它自己的线,不能拿缺省去比)。
77    pub soft_cap: u64,
78}
79
80pub struct MainEngine {
81    event: Arc<EventEngine>,
82    registry: Arc<Registry>,
83    /// start() 装配顺序纪录(close 反向使用);None = 未 start。
84    started_order: Mutex<Option<Vec<String>>>,
85    closed: RwLock<bool>,
86}
87
88impl MainEngine {
89    /// 注入式创建:`Some(bus)` 复用外部引擎;`None` 自建(start() 时拉起)。
90    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    /// 宿主级引擎句柄(测试/装配工具可用;插件不可见——ctx 不暴露)。
100    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    /// 统一格式注册(D2:名/重名/同名自依赖;add 期【不校验 deps 存在性】——
113    /// 新 D1 注册自由,悬空 deps 由 start 期把关)。
114    ///
115    /// **运行期动态 add(0.2.0)**:start 之后仍可随时注入新插件——登记 → 织入
116    /// 能力索引 → 立即走 launch_one 装配上线 → 记入 started_order(close 反序覆盖);
117    /// 并广播 `ePlugUp` / `ePlugUp.<名>`,迟绑定依赖方据此自主发现。
118    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        // F8 纪律:services() 为纯声明;在拿锁前读一次抽 id
132        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        // 动态 add:start 已发生 → 立即装配上线(与 start 共用 launch_one)
165        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    /// 状态查询。
175    pub fn get_state(&self, name: &str) -> Option<PluginState> {
176        self.registry.get(name).map(|e| e.state.get())
177    }
178
179    /// 信息快照。
180    pub fn get_plugin(&self, name: &str) -> Option<PluginInfo> {
181        self.registry.info_of(name)
182    }
183
184    /// 类型安全获取(get_typed):同一分配的 Arc<dyn Any> downcast。
185    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    /// dyn 取壳(0.5.3 新增,additive):按注册名取回 `Arc<dyn Plugin>` 本体。
191    ///
192    /// 存在理由:装配侧把插件包进壳里(如 ek-station 的 `DynPlugin`)时,注册表的
193    /// downcast 底座是壳类型,`get_typed::<真插件>` 必然落空 —— 本口把 dyn 本体交回,
194    /// 界面侧经 `Plugin::as_any` 自行 downcast(壳侧需转发 `as_any`)。
195    /// 与 `get_typed` 互补,不改任何既有行为。
196    #[must_use]
197    pub fn plugin_arc(&self, name: &str) -> Option<Arc<dyn Plugin>> {
198        let entry = self.registry.get(name)?;
199        Some(Arc::clone(&entry.plugin))
200    }
201
202    /// 注册名列表(注册序)。
203    pub fn plugin_names(&self) -> Vec<String> {
204        self.registry.names()
205    }
206
207    /// 运行统计(软顶丢弃/panic/当前深度/峰值/软顶)。未注册 → None。
208    pub fn mail_stats(&self, name: &str) -> Option<MailStats> {
209        self.registry.get(name).map(|e| MailStats {
210            spilled: e.stats.spilled.load(Ordering::Acquire),
211            panics: e.stats.panics.load(Ordering::Acquire),
212            pending: e.stats.pending.load(Ordering::Acquire),
213            peak: e.stats.peak.load(Ordering::Acquire),
214            soft_cap: e.soft_cap,
215        })
216    }
217
218    /// 启动全部插件:总线 start → deps 拓扑序逐个装配
219    /// (on_init → 建邮箱 → spawn worker → state=Running → 注册 forwarder → on_start
220    /// → 广播 ePlugUp)。
221    /// 依赖环/悬空 deps → DepCycle/UnknownDep;重复 start 幂等 Ok;已 close → Stopped。
222    pub fn start(&self) -> Result<(), PluginError> {
223        let mut started = self.started_order.lock().unwrap();
224        if started.is_some() {
225            return Ok(());
226        }
227        self.closed_check()?;
228        self.event.start();
229        let order = self.registry.topo_order()?;
230        let mut launched = Vec::with_capacity(order.len());
231        for entry in order {
232            self.launch_one(&entry);
233            launched.push(entry.name.clone());
234        }
235        *started = Some(launched);
236        Ok(())
237    }
238
239    /// 单插件装配(start 与动态 add 共用的最小闭包):
240    /// on_init → 建邮箱 → spawn worker → Running → 注册 forwarder → on_start
241    /// → 广播 `ePlugUp.<名>`(后缀版,载荷=版本串)+ `ePlugUp`(固定版,
242    /// 载荷=PlugNotice,供不认识插件名的巡检者订阅)。
243    fn launch_one(&self, entry: &Arc<PluginEntry>) {
244        let ctx = Arc::new(PluginContext::new(
245            Arc::clone(&self.event),
246            Arc::clone(&self.registry),
247            entry.name.clone(),
248        ));
249        entry.plugin.on_init(&ctx);
250        // 0.5.0:邮箱**无界**(D5 改案:默认不丢),软顶在 forwarder 发送前裁决。
251        // close 第③步语义不变:卸完 forwarder + 宿主侧 Sender 置 None → 全部 send 端消费尽
252        // → rx Disconnected → worker 自退(与 bounded 时同一条路径)。
253        let (tx, rx) = crossbeam_channel::unbounded::<ek_event::Event>();
254        *entry.mailbox.lock().unwrap() = Some(tx.clone());
255        let handler = spawn_worker(
256            Arc::clone(&entry.plugin),
257            rx,
258            entry.state.clone(),
259            Arc::clone(&entry.stats),
260            Arc::clone(&self.event),
261        );
262        *entry.worker.lock().unwrap() = Some(handler);
263        // 先 Running 再装 forwarder:避免 ask 预检在装配窗口把目标误判 Ready
264        let _ = entry
265            .state
266            .cas(PluginStateCode::Ready, PluginStateCode::Running);
267        let mut ids: Vec<HandlerId> = Vec::new();
268        for ty in entry.plugin.routes() {
269            let fwd = Forwarder::new(
270                tx.clone(),
271                Arc::clone(&entry.stats),
272                entry.soft_cap,
273                entry.name.clone(),
274            );
275            let id = self.event.register(ty.into_owned(), make_forwarder(fwd));
276            ids.push(id);
277        }
278        *entry.forwarder_ids.lock().unwrap() = ids;
279        entry.plugin.on_start();
280
281        let version = entry.plugin.version().into_owned();
282        let _ = self.event.put(ek_event::Event::with_data(
283            format!("{EVENT_PLUG_UP_PREFIX}{}", entry.name),
284            Arc::new(version.clone()),
285        ));
286        let _ = self.event.put(ek_event::Event::with_data(
287            EVENT_PLUG_UP,
288            Arc::new(PlugNotice {
289                name: entry.name.clone(),
290                version,
291            }),
292        ));
293    }
294
295    /// D4 五步闭停单个插件(不摘表)。
296    ///
297    /// on_stop 语义(执行期复审 F1 修正):凡曾装配过的插件(Running/Stopping/Exited/
298    /// Quarantined)close 时都调 on_stop —— 熔断或自然退出的插件也需要业务清理;
299    /// 仅 Ready(从未 start)不调。
300    fn close_one(&self, entry: &Arc<PluginEntry>, deadline: Instant) {
301        let pre_state = entry.state.get();
302        let call_stop = pre_state != PluginState::Ready;
303        // 挂起迁移:Running → Stopping(容错其他状态)
304        let _ = entry
305            .state
306            .cas(PluginStateCode::Running, PluginStateCode::Stopping);
307        // ① 卸 forwarder
308        let ids: Vec<HandlerId> = std::mem::take(&mut *entry.forwarder_ids.lock().unwrap());
309        for id in ids {
310            self.event.unregister(id);
311        }
312        // ② on_stop
313        if call_stop {
314            entry.plugin.on_stop();
315        }
316        // ③ 关邮箱(宿主侧 Sender drop → 全部 send 端消费尽)
317        *entry.mailbox.lock().unwrap() = None;
318        // ④⑤ 排空等待 + join(带超时,超时告警 + 剥离)
319        while !entry.stats.done.load(Ordering::Acquire) && Instant::now() < deadline {
320            std::thread::sleep(Duration::from_millis(2));
321        }
322        if let Some(handle) = entry.worker.lock().unwrap().take() {
323            if entry.stats.done.load(Ordering::Acquire) {
324                let _ = handle.join();
325            } else {
326                // 运维诊断走 stderr(0.3.0:不进总线,不占类型)
327                eprintln!(
328                    "ek-core: close timeout: worker of {:?} still busy; detach and keep draining",
329                    entry.name
330                );
331            }
332        }
333        // 兜底:Stopping → Exited(worker 已退出但未来得及 CAS 的场合)
334        let _ = entry
335            .state
336            .cas(PluginStateCode::Stopping, PluginStateCode::Exited);
337    }
338
339    /// 全局闭停:逆 start 序逐插件五步;然后清插件表。总线留给调用方(close_all 停总线)。
340    pub fn close(&self, timeout: Duration) {
341        let order = self.started_order.lock().unwrap().take();
342        *self.closed.write().unwrap() = true;
343        let deadline = Instant::now() + timeout.max(Duration::from_millis(1));
344        if let Some(order) = order {
345            for name in order.iter().rev() {
346                if let Some(entry) = self.registry.get(name) {
347                    self.close_one(&entry, deadline);
348                }
349            }
350        }
351        self.registry.clear();
352    }
353
354    /// close + 停总线(终态)。
355    pub fn close_all(&self, timeout: Duration) {
356        self.close(timeout);
357        self.event.stop();
358    }
359
360    /// 移除单个插件:Running 者执行五步(UNKNOWN 表项报错);仍被其他插件 deps 引用
361    /// 时 stderr 警告(定稿 policy:允许移除 + 警告;依赖方后续 ask 走预检/断连失败)。
362    ///
363    /// 0.2.0:同步修剪能力索引(F3:防幽灵提供方)+ 作废缓存,并发
364    /// `ePlugDown`(固定版,PlugNotice)+ `ePlugDown.<名>`(后缀版,版本串)。
365    /// 注:close()(宿主整体下架)不发逐插件 ePlugDown(审核 F7:避免拆除期事件风暴)。
366    pub fn remove_plugin(&self, name: &str) -> Result<(), PluginError> {
367        self.closed_check()?;
368        let entry = self
369            .registry
370            .get(name)
371            .ok_or_else(|| PluginError::UnknownName(name.to_string()))?;
372        let dependents = self.registry.any_depends_on(name);
373        if !dependents.is_empty() {
374            eprintln!(
375                "ek-core: remove_plugin {:?} still depended on by {:?}",
376                name, dependents
377            );
378        }
379        let deadline = Instant::now() + CLOSE_JOIN_TIMEOUT;
380        if entry.state.get() == PluginState::from_code_enum(PluginStateCode::Running) {
381            self.close_one(&entry, deadline);
382        } else {
383            // Ready / Exited / Quarantined:摘 forwarder(可能残留于熔断前)+ 关邮箱
384            let ids: Vec<HandlerId> = std::mem::take(&mut *entry.forwarder_ids.lock().unwrap());
385            for id in ids {
386                self.event.unregister(id);
387            }
388            *entry.mailbox.lock().unwrap() = None;
389        }
390        let version = entry.plugin.version().into_owned();
391        self.registry
392            .remove(name)
393            .ok_or_else(|| PluginError::UnknownName(name.to_string()))?;
394        // 摘表成功才动索引(修剪幽灵;F3)+ 失效缓存 + 移除广播
395        self.registry.index_remove(name);
396        self.registry.invalidate(name);
397        let _ = self.event.put(ek_event::Event::with_data(
398            format!("{EVENT_PLUG_DOWN_EVENT_PREFIX}{name}"),
399            Arc::new(version.clone()),
400        ));
401        let _ = self.event.put(ek_event::Event::with_data(
402            EVENT_PLUG_DOWN,
403            Arc::new(PlugNotice {
404                name: name.to_string(),
405                version,
406            }),
407        ));
408        Ok(())
409    }
410}