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) 验证.
This commit is contained in:
2026-07-04 12:22:51 +08:00
parent 4d830c0e56
commit ab1dd9160c
2 changed files with 13 additions and 2 deletions

View File

@@ -358,9 +358,12 @@ impl ServiceManager {
self.running = true; self.running = true;
while self.running { while self.running {
let envelope = match self.rx.recv() { // 使用 recv_timeout 而非 recv确保外部信号如 Ctrl+C 置 running=false
// 能在超时后被检查到,避免永久阻塞等待下一条消息。
let envelope = match self.rx.recv_timeout(std::time::Duration::from_millis(200)) {
Ok(env) => env, Ok(env) => env,
Err(_) => { Err(mpsc::RecvTimeoutError::Timeout) => continue,
Err(mpsc::RecvTimeoutError::Disconnected) => {
println!("[ServiceManager] 所有发送端已关闭,退出"); println!("[ServiceManager] 所有发送端已关闭,退出");
break; break;
} }
@@ -763,6 +766,7 @@ impl ServiceManager {
Message::Shutdown => { Message::Shutdown => {
println!("[ServiceManager] 收到 Shutdown 指令"); println!("[ServiceManager] 收到 Shutdown 指令");
self.broadcast_message(Message::Shutdown); self.broadcast_message(Message::Shutdown);
self.running = false;
} }
Message::WifiResult(payload) => { Message::WifiResult(payload) => {
self.broadcast_message(Message::WifiResult(payload)); self.broadcast_message(Message::WifiResult(payload));

View File

@@ -113,10 +113,17 @@ fn main() -> Result<()> {
// 设置 Ctrl+C 信号处理 // 设置 Ctrl+C 信号处理
let running = Arc::new(AtomicBool::new(true)); let running = Arc::new(AtomicBool::new(true));
let r = running.clone(); let r = running.clone();
let shutdown_tx = manager.sender();
ctrlc::set_handler(move || { ctrlc::set_handler(move || {
println!("\n收到退出信号,正在关闭..."); println!("\n收到退出信号,正在关闭...");
r.store(false, Ordering::SeqCst); 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!("启动所有插件..."); println!("启动所有插件...");