diff --git a/ostool/src/run/httpboot_board.rs b/ostool/src/run/httpboot_board.rs index bd0d4152..1ccb7fe9 100644 --- a/ostool/src/run/httpboot_board.rs +++ b/ostool/src/run/httpboot_board.rs @@ -31,7 +31,9 @@ use crate::{ output_matcher::{ ByteStreamMatcher, MATCH_DRAIN_DURATION, compile_regexes, print_match_event, }, - shell_init::{SHELL_INIT_DELAY, ShellAutoInitMatcher}, + shell_init::{ + SHELL_INIT_CHUNK_DELAY, SHELL_INIT_CHUNK_SIZE, SHELL_INIT_DELAY, ShellAutoInitMatcher, + }, uboot::UbootRunInput, }, sterm::{AsyncTerminal, TerminalConfig}, @@ -385,7 +387,12 @@ where if let Some(shell_init) = shell_init.as_mut() && let Some(command) = shell_init.observe_byte(byte) { - handle.send_after(SHELL_INIT_DELAY, command); + handle.send_after_chunks( + SHELL_INIT_DELAY, + command, + SHELL_INIT_CHUNK_SIZE, + SHELL_INIT_CHUNK_DELAY, + ); } if matcher.should_stop() { diff --git a/ostool/src/run/qemu.rs b/ostool/src/run/qemu.rs index 2f1ded28..53dd2b18 100644 --- a/ostool/src/run/qemu.rs +++ b/ostool/src/run/qemu.rs @@ -58,7 +58,10 @@ use crate::{ output_matcher::{ByteStreamMatcher, compile_regexes, print_match_event}, ovmf_prebuilt::{Arch, FileType, Prebuilt, Source}, qemu_plan::{QemuBootSource, QemuCommandPlanInput, build_qemu_command_plan}, - shell_init::{SHELL_INIT_DELAY, ShellAutoInitMatcher, normalize_shell_init_config}, + shell_init::{ + SHELL_INIT_CHUNK_DELAY, SHELL_INIT_CHUNK_SIZE, SHELL_INIT_DELAY, ShellAutoInitMatcher, + normalize_shell_init_config, + }, }, sterm::{AsyncTerminal, TerminalConfig}, utils::PathResultExt, @@ -555,7 +558,12 @@ impl QemuRunner { if let Some(shell_auto_init) = shell_auto_init.as_mut() && let Some(command) = shell_auto_init.observe_byte(byte) { - handle.send_after(SHELL_INIT_DELAY, command); + handle.send_after_chunks( + SHELL_INIT_DELAY, + command, + SHELL_INIT_CHUNK_SIZE, + SHELL_INIT_CHUNK_DELAY, + ); } if matcher.should_stop() { diff --git a/ostool/src/run/shell_init.rs b/ostool/src/run/shell_init.rs index 83fa3ea7..f91e6b43 100644 --- a/ostool/src/run/shell_init.rs +++ b/ostool/src/run/shell_init.rs @@ -3,6 +3,8 @@ use std::time::Duration; use anyhow::{Result, bail}; pub(crate) const SHELL_INIT_DELAY: Duration = Duration::from_millis(100); +pub(crate) const SHELL_INIT_CHUNK_SIZE: usize = 64; +pub(crate) const SHELL_INIT_CHUNK_DELAY: Duration = Duration::from_millis(2); pub(crate) fn normalize_shell_init_config( shell_prefix: &mut Option, diff --git a/ostool/src/run/uboot.rs b/ostool/src/run/uboot.rs index 7542e275..f857223d 100644 --- a/ostool/src/run/uboot.rs +++ b/ostool/src/run/uboot.rs @@ -50,7 +50,10 @@ use crate::{ output_matcher::{ ByteStreamMatcher, MATCH_DRAIN_DURATION, compile_regexes, print_match_event, }, - shell_init::{SHELL_INIT_DELAY, ShellAutoInitMatcher, normalize_shell_init_config}, + shell_init::{ + SHELL_INIT_CHUNK_DELAY, SHELL_INIT_CHUNK_SIZE, SHELL_INIT_DELAY, ShellAutoInitMatcher, + normalize_shell_init_config, + }, tftp, }, sterm::{AsyncTerminal, TerminalConfig}, @@ -1270,7 +1273,12 @@ where if let Some(shell_init) = shell_init.as_mut() && let Some(command) = shell_init.observe_byte(byte) { - h.send_after(SHELL_INIT_DELAY, command); + h.send_after_chunks( + SHELL_INIT_DELAY, + command, + SHELL_INIT_CHUNK_SIZE, + SHELL_INIT_CHUNK_DELAY, + ); } if matcher.should_stop() { diff --git a/ostool/src/sterm/mod.rs b/ostool/src/sterm/mod.rs index 8c633174..65c8b6f6 100644 --- a/ostool/src/sterm/mod.rs +++ b/ostool/src/sterm/mod.rs @@ -321,6 +321,29 @@ impl TerminalHandle { }); } + pub fn send_after_chunks( + &self, + duration: Duration, + bytes: Vec, + chunk_size: usize, + chunk_delay: Duration, + ) { + let handle = self.clone(); + let chunk_size = chunk_size.max(1); + tokio::spawn(async move { + tokio::time::sleep(duration).await; + for chunk in bytes.chunks(chunk_size) { + if !handle.is_running() { + break; + } + if handle.send(chunk.to_vec()).is_err() { + break; + } + tokio::time::sleep(chunk_delay).await; + } + }); + } + pub fn is_running(&self) -> bool { self.inner.running.load(Ordering::Acquire) } @@ -842,6 +865,23 @@ mod tests { assert!(handle.timeout_deadline().is_some()); } + #[tokio::test] + async fn send_after_chunks_splits_long_terminal_input() { + let (tx, mut rx) = mpsc::unbounded_channel(); + let handle = TerminalHandle::new(tx); + + handle.send_after_chunks( + Duration::ZERO, + b"abcdef".to_vec(), + 2, + Duration::from_millis(1), + ); + + assert_eq!(rx.recv().await.unwrap(), b"ab"); + assert_eq!(rx.recv().await.unwrap(), b"cd"); + assert_eq!(rx.recv().await.unwrap(), b"ef"); + } + #[tokio::test] async fn non_tty_mode_consumes_output_without_event_stream() { let terminal = super::AsyncTerminal::new(super::TerminalConfig {