Files
ShowenV2/src/main.rs
pulsareonbot ab1dd9160c fix: 修复 Ctrl+C 无法及时退出主循环的 P1 bug
问题: main.rs Ctrl+C handler 只置 AtomicBool, 但 manager.run() 阻塞在
rx.recv() 永久等待下一条消息, 导致 Ctrl+C 后需等任意消息到达才能退出.
CLAUDE.md:162 记录的已知 P1 隐患 (王浩然发现).

修复 (3 处):
1. service_manager.rs run(): recv() -> recv_timeout(200ms), 超时则
   continue 重新检查 self.running, 确保信号能被及时响应
2. service_manager.rs handle_manager_message(): Shutdown 分支广播后
   置 self.running=false, 让主循环正常退出 (原本只广播不停机)
3. main.rs Ctrl+C handler: 除置 AtomicBool 外, 向 channel 注入
   Message::Shutdown envelope, 唤醒 recv_timeout 并触发优雅停机

效果: Ctrl+C 后最多 200ms 退出, 而非等待下一条任意消息.

注: Windows 开发环境缺 pkg-config/libdbus, 无法本地 cargo check;
改动经 rust-analyzer 类型检查零诊断, 待目标机 (Linux ARM64) 验证.
2026-07-04 12:22:51 +08:00

145 lines
4.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use anyhow::Result;
use showen_v2::core::config::AppConfig;
use showen_v2::core::plugin_loader::PluginLoader;
use showen_v2::core::service_manager::ServiceManager;
#[cfg(not(test))]
use showen_v2::core::version_manager::VersionManager;
use showen_v2::plugins::{
ble::BlePlugin, device::DevicePlugin, http::HttpPlugin, screen::ScreenPlugin,
video::VideoPlugin, wifi::WifiPlugin,
};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
fn main() -> Result<()> {
// 解析命令行参数
let args: Vec<String> = std::env::args().collect();
// 处理 --validate 参数
if args.contains(&"--validate".to_string()) {
let config_path = args
.iter()
.skip_while(|arg| *arg != "--config")
.nth(1)
.cloned()
.unwrap_or_else(|| "configs/dog_state_machine.json".to_string());
println!("验证配置文件: {}", config_path);
let config = AppConfig::from_file(&config_path)?;
config.validate_paths()?;
println!("✓ 配置文件有效");
return Ok(());
}
// 获取配置文件路径
let config_path = args
.iter()
.skip_while(|arg| *arg != "--config")
.nth(1)
.cloned()
.or_else(|| args.get(1).cloned())
.unwrap_or_else(|| "configs/dog_state_machine.json".to_string());
println!("ShowenV2 — 数字生命窗口平台");
println!("加载配置: {}", config_path);
let config = AppConfig::from_file(&config_path)?;
config.validate_paths()?;
let mut manager = ServiceManager::new(config);
// 按依赖顺序注册插件
// 独立插件device, screen, wifi, video, ble
// 依赖插件http (依赖 video)
println!("注册插件...");
manager.register(Box::new(DevicePlugin::new_default()));
println!(" ✓ DevicePlugin");
manager.register(Box::new(ScreenPlugin::new()));
println!(" ✓ ScreenPlugin");
manager.register(Box::new(WifiPlugin::new()));
println!(" ✓ WifiPlugin");
manager.register(Box::new(VideoPlugin::new()));
println!(" ✓ VideoPlugin");
manager.register(Box::new(BlePlugin::new()));
println!(" ✓ BlePlugin");
manager.register(Box::new(HttpPlugin::new()));
println!(" ✓ HttpPlugin");
// 加载动态插件
let plugin_store = std::path::Path::new("plugin_store");
if plugin_store.exists() {
println!("扫描动态插件...");
#[cfg(not(test))]
manager.set_version_manager(VersionManager::new(PluginLoader::new(plugin_store)));
let loader = PluginLoader::new(plugin_store);
match loader.load_registry() {
Ok(registry) => {
for (plugin_id, entry) in &registry.plugins {
if !entry.enabled {
println!(" - {plugin_id} (禁用)");
continue;
}
match loader.load_plugin(plugin_id, Some(&entry.active_version)) {
Ok((plugin, manifest)) => {
manager.register_dynamic_with_manifest(
Box::new(plugin),
manifest.error_policy,
entry.max_errors,
manifest.required_capabilities,
manifest.capabilities,
manifest.auto_test,
);
println!("{} v{} (动态)", plugin_id, entry.active_version);
}
Err(e) => {
eprintln!("{} v{} 加载失败: {e}", plugin_id, entry.active_version);
}
}
}
}
Err(e) => {
eprintln!("读取插件注册表失败: {e}");
}
}
}
// 设置 Ctrl+C 信号处理
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
let shutdown_tx = manager.sender();
ctrlc::set_handler(move || {
println!("\n收到退出信号,正在关闭...");
r.store(false, Ordering::SeqCst);
// 注入 Shutdown 消息,唤醒主消息循环并触发优雅停机
let _ = shutdown_tx.send(showen_v2::core::message::Envelope {
from: "signal".to_string(),
to: showen_v2::core::message::Destination::Manager,
message: showen_v2::core::message::Message::Shutdown,
});
})?;
println!("启动所有插件...");
manager.start_all()?;
println!("ShowenV2 运行中... (按 Ctrl+C 退出)");
// 运行主循环
while running.load(Ordering::SeqCst) {
manager.run()?;
}
println!("正在停止所有插件...");
manager.stop_all()?;
println!("ShowenV2 已退出");
Ok(())
}