From 86a39dace75d2782cea5c59a9f0e0390a4f3e479 Mon Sep 17 00:00:00 2001 From: Blank038 Date: Tue, 21 Jul 2026 20:29:23 +0800 Subject: [PATCH] =?UTF-8?q?fix(debug-ipc):=20=E4=BF=AE=E5=A4=8D=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E7=94=9F=E5=91=BD=E5=91=A8=E6=9C=9F=E5=92=8C=E5=AE=8C?= =?UTF-8?q?=E6=95=B4=E5=B8=A7=E5=86=99=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 区分 IPC server 所有者与普通克隆,避免临时 clone 析构时提前停止共享服务。 改用带超时的阻塞 write_all 发送完整数据帧,防止非阻塞短写破坏热重载协议。 --- src/debug/ipc.rs | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/debug/ipc.rs b/src/debug/ipc.rs index c82440a..b2d4f67 100644 --- a/src/debug/ipc.rs +++ b/src/debug/ipc.rs @@ -11,9 +11,18 @@ use std::{ use crate::error::{CliError, Result}; -#[derive(Clone)] pub struct DebugIpcServer { inner: Arc, + owns_lifecycle: bool, +} + +impl Clone for DebugIpcServer { + fn clone(&self) -> Self { + Self { + inner: Arc::clone(&self.inner), + owns_lifecycle: false, + } + } } struct Inner { @@ -39,7 +48,10 @@ impl DebugIpcServer { let handle = thread::spawn(move || accept_loop(listener, thread_inner)); *inner.thread.lock().unwrap() = Some(handle); - Ok(Self { inner }) + Ok(Self { + inner, + owns_lifecycle: true, + }) } pub fn port(&self) -> u16 { @@ -50,12 +62,11 @@ impl DebugIpcServer { let frame = encode_frame(message_type, payload)?; let mut clients = self.inner.clients.lock().unwrap(); let mut any = false; - clients.retain_mut(|stream| match stream.write(&frame) { - Ok(_) => { + clients.retain_mut(|stream| match stream.write_all(&frame) { + Ok(()) => { any = true; true } - Err(err) if err.kind() == io::ErrorKind::WouldBlock => true, Err(_) => false, }); Ok(any) @@ -81,7 +92,9 @@ impl DebugIpcServer { impl Drop for DebugIpcServer { fn drop(&mut self) { - self.stop(); + if self.owns_lifecycle { + self.stop(); + } } } @@ -99,8 +112,12 @@ fn accept_loop(listener: TcpListener, inner: Arc) { while !inner.stop.load(Ordering::Relaxed) { match listener.accept() { Ok((stream, _)) => { - let _ = stream.set_nonblocking(true); - inner.clients.lock().unwrap().push(stream); + if stream + .set_write_timeout(Some(Duration::from_secs(1))) + .is_ok() + { + inner.clients.lock().unwrap().push(stream); + } } Err(err) if err.kind() == io::ErrorKind::WouldBlock => { thread::sleep(Duration::from_millis(50)); @@ -112,7 +129,7 @@ fn accept_loop(listener: TcpListener, inner: Arc) { #[cfg(test)] mod tests { - use super::encode_frame; + use super::{DebugIpcServer, encode_frame}; #[test] fn encodes_big_endian_ipc_frame() { @@ -120,4 +137,15 @@ mod tests { assert_eq!(&frame[..6], &[0, 2, 0, 0, 0, 7]); assert_eq!(&frame[6..], b"[\"a.b\"]"); } + + #[test] + fn dropping_clone_keeps_server_running() { + let server = DebugIpcServer::start().unwrap(); + let port = server.port(); + + drop(server.clone()); + + assert_eq!(server.port(), port); + server.safe_exit(); + } }