Skip to content

Commit 799f038

Browse files
committed
Refactor and add more comments for readability.
1 parent d260d9d commit 799f038

2 files changed

Lines changed: 33 additions & 15 deletions

File tree

PIMELauncher/src/backend_manager.rs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use futures::{SinkExt, StreamExt};
1212
use tokio_util::codec::{FramedRead, FramedWrite, LinesCodec};
1313

1414
/// Manages the lifecycle of backend processes and routes messages between clients and backends.
15+
1516
#[derive(Clone)]
1617
pub struct BackendManager {
1718
state: Arc<Mutex<BackendManagerState>>,
@@ -177,7 +178,10 @@ impl BackendManager {
177178
stderr_task.abort();
178179

179180
if exit_reason == BackendExitReason::Normal {
180-
info!("Backend {} input channel closed permanently. Stopping manager loop.", backend_name_clone);
181+
info!(
182+
"Backend {} input channel closed permanently. Stopping manager loop.",
183+
backend_name_clone
184+
);
181185
break; // Exit the loop entirely to prevent infinite restarts!
182186
}
183187

@@ -249,9 +253,12 @@ impl BackendManager {
249253
) {
250254
// TODO: Need to detect if the backend process hangs and is not responsive.
251255
// When a backend process hangs, reading from its stdout may blocks forever.
252-
let mut stdout_reader = FramedRead::new(stdout, LinesCodec::new_with_max_length(1048576));
256+
let mut stdout_reader = FramedRead::new(
257+
stdout,
258+
LinesCodec::new_with_max_length(protocol::MAX_MESSAGE_LINE_LENGTH),
259+
);
253260
while let Some(result) = stdout_reader.next().await {
254-
last_output_time.store(Self::current_ms(), Ordering::SeqCst);
261+
last_output_time.store(Self::current_ms(), Ordering::Relaxed);
255262
let line = match result {
256263
Ok(l) => l,
257264
Err(e) => {
@@ -301,7 +308,10 @@ impl BackendManager {
301308
backend_name: &str,
302309
last_output_time: Arc<AtomicU64>,
303310
) -> BackendExitReason {
304-
let mut stdin_writer = FramedWrite::new(stdin, LinesCodec::new_with_max_length(1048576));
311+
let mut stdin_writer = FramedWrite::new(
312+
stdin,
313+
LinesCodec::new_with_max_length(protocol::MAX_MESSAGE_LINE_LENGTH),
314+
);
305315
let mut last_request_time: Option<u64> = None;
306316

307317
let mut watchdog_interval = tokio::time::interval(Duration::from_secs(1));
@@ -310,15 +320,18 @@ impl BackendManager {
310320
tokio::select! {
311321
msg = stdin_rx.recv() => {
312322
let Some(data) = msg else {
323+
// mpsc::Receiver returns None when all Senders are dropped (e.g., Launcher shutdown
324+
// or explicit backend removal from BackendManager). In that case, no further input
325+
// will ever arrive. Thus, we exit normally without restarting the process.
313326
info!("Backend {} stdin channel closed. Exiting input loop.", backend_name);
314327
return BackendExitReason::Normal;
315328
};
316329
let now = Self::current_ms();
317330
last_request_time = Some(now);
318-
info!("Backend {} received request from channel. Data len: {}. req_t={}", backend_name, data.len(), now);
331+
info!("Backend {} received request from channel. Data len: {}.", backend_name, data.len());
319332

320333
// LinesCodec expects data without the newline, it will add it for us.
321-
let write_res = tokio::time::timeout(Duration::from_secs(5), stdin_writer.send(data)).await;
334+
let write_res = tokio::time::timeout(protocol::BACKEND_WRITE_TIMEOUT, stdin_writer.send(data)).await;
322335
if let Err(_) = write_res {
323336
error!("Timeout writing to backend {}. Forcing restart.", backend_name);
324337
let _ = child_process.kill().await;
@@ -333,15 +346,13 @@ impl BackendManager {
333346
_ = watchdog_interval.tick() => {
334347
let now = Self::current_ms();
335348
if let Some(req_t) = last_request_time {
336-
let last_out = last_output_time.load(Ordering::SeqCst);
337-
// Log tick status occasionally or at least for debugging
338-
debug!("Watchdog tick for {}: last_out={}, req_t={}, now={}, delta={}",
339-
backend_name, last_out, req_t, now, now as i64 - req_t as i64);
340-
341-
// If no output has been received since the last request and it's been more than 15 seconds
342-
if last_out < req_t && (now - req_t) > 15000 {
343-
error!("Backend {} seems to be hung (no output for 15s after request). last_out={}, req_t={}, now={}. Forcing restart.",
344-
backend_name, last_out, req_t, now);
349+
let last_out = last_output_time.load(Ordering::Relaxed);
350+
debug!("Watchdog tick for {}: last_out={}, req_t={}, now={}, delta={}ms",
351+
backend_name, last_out, req_t, now, now.saturating_sub(req_t));
352+
353+
if last_out < req_t && now - req_t > protocol::HANG_TIMEOUT.as_millis() as u64 {
354+
error!("Backend {} seems to be hung (no output for {}ms after request). Forcing restart.",
355+
backend_name, protocol::HANG_TIMEOUT.as_millis());
345356
let _ = child_process.kill().await;
346357
return BackendExitReason::Error;
347358
}

PIMELauncher/src/protocol.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
use serde_json::Value;
2+
use std::time::Duration;
23

34
/// Maximum allowed length (in bytes) for a single message line.
45
pub const MAX_MESSAGE_LINE_LENGTH: usize = 1048576;
56

7+
/// Timeout for backend output hang detection.
8+
pub const HANG_TIMEOUT: Duration = Duration::from_millis(15_000);
9+
10+
/// Timeout for backend write operations.
11+
pub const BACKEND_WRITE_TIMEOUT: Duration = Duration::from_secs(5);
12+
613
/// Parses the first line received from a client to determine which backend to use.
714
///
815
/// Supports standard `{"method": "init", "id": "{GUID}"}`.

0 commit comments

Comments
 (0)