use std::sync::Arc;
use std::time::Duration;
use crate::machine::MachineManager;
use super::DEFAULT_MACHINE_NAME;
const SERIAL_ACTIVE_INTERVAL_MS: u64 = 100;
const SERIAL_MAX_IDLE_SHIFT: u32 = 4;
pub(super) async fn serial_read_adaptive(machine_manager: Arc<MachineManager>) {
const MAX_LINE_BUF: usize = 64 * 1024;
let mut console_buf = String::new();
let mut agent_buf = String::new();
let mut idle_streak: u32 = 0;
loop {
let mut had_output = false;
if let Ok(output) = machine_manager.read_console_output(DEFAULT_MACHINE_NAME) {
if process_serial_output(&mut console_buf, &output, "Guest", true, MAX_LINE_BUF) {
had_output = true;
}
} else {
flush_line_buf(&mut console_buf, "Guest", true);
flush_line_buf(&mut agent_buf, "Agent", false);
tracing::debug!("Serial read loop stopped: console read failed");
break;
}
if let Ok(output) = machine_manager.read_agent_log_output(DEFAULT_MACHINE_NAME) {
if process_serial_output(&mut agent_buf, &output, "Agent", false, MAX_LINE_BUF) {
had_output = true;
}
}
if had_output {
idle_streak = 0;
} else {
idle_streak = idle_streak.saturating_add(1);
}
let delay_ms = SERIAL_ACTIVE_INTERVAL_MS * (1u64 << idle_streak.min(SERIAL_MAX_IDLE_SHIFT));
tokio::time::sleep(Duration::from_millis(delay_ms)).await;
}
}
fn process_serial_output(
line_buf: &mut String,
output: &str,
label: &str,
level_info: bool,
max_buf: usize,
) -> bool {
let trimmed = output.trim_matches('\0');
if trimmed.is_empty() {
return false;
}
line_buf.push_str(trimmed);
if line_buf.len() > max_buf {
tracing::warn!("{label}: line buffer overflow, flushing");
line_buf.clear();
return true;
}
while let Some(pos) = line_buf.find('\n') {
let line = line_buf[..pos].trim_end().to_owned();
line_buf.drain(..=pos);
if line.is_empty() {
continue;
}
if level_info {
tracing::info!("{label}: {line}");
} else {
tracing::debug!("{label}: {line}");
}
}
true
}
fn flush_line_buf(line_buf: &mut String, label: &str, level_info: bool) {
let trailing = line_buf.trim().to_owned();
if !trailing.is_empty() {
if level_info {
tracing::info!("{label}: {trailing}");
} else {
tracing::debug!("{label}: {trailing}");
}
}
line_buf.clear();
}