Fix write_str returning early (#3452)

This commit is contained in:
Dániel Buga 2025-05-05 15:41:07 +02:00 committed by GitHub
parent 16897bb68d
commit 8011a30bd0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 75 additions and 7 deletions

View File

@ -86,6 +86,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `OneShot` timer now returns an InvalidTimeout from `schedule` instead of panicking (#3433)
- GPIO interrupt handling no longer causes infinite looping if a task at higher priority is awaiting on a pin event (#3408)
- `esp_hal::gpio::Input::is_interrupt_set` can now return true (#3408)
- `Uart::write_str` (both core::fmt and uWrite implementations) no longer stops writing when the internal buffer fills up (#3452)
### Removed

View File

@ -747,6 +747,14 @@ where
self.uart.info().write(data)
}
fn write_all(&mut self, mut data: &[u8]) -> Result<(), TxError> {
while !data.is_empty() {
let bytes_written = self.write(data)?;
data = &data[bytes_written..];
}
Ok(())
}
/// Flush the transmit buffer.
///
/// This function blocks until all data in the TX FIFO has been
@ -1737,8 +1745,7 @@ where
#[inline]
fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
self.write(s.as_bytes())?;
Ok(())
self.write_all(s.as_bytes())
}
}
@ -1758,8 +1765,7 @@ where
{
#[inline]
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.write(s.as_bytes()).map_err(|_| core::fmt::Error)?;
Ok(())
self.write_all(s.as_bytes()).map_err(|_| core::fmt::Error)
}
}

View File

@ -6,19 +6,52 @@
#![no_std]
#![no_main]
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
use esp_hal::{
Async,
uart::{self, Uart},
Blocking,
interrupt::{
Priority,
software::{SoftwareInterrupt, SoftwareInterruptControl},
},
uart::{self, Uart, UartRx},
};
use hil_test as _;
use esp_hal_embassy::InterruptExecutor;
use hil_test::mk_static;
struct Context {
interrupt: SoftwareInterrupt<'static, 1>,
uart: Uart<'static, Async>,
}
const LONG_TEST_STRING: &str = "This is a very long string that will be printed to the serial console. First line of the string. Second line of the string. Third line of the string. Fourth line of the string. Fifth line of the string. Sixth line of the string.";
#[embassy_executor::task]
async fn long_string_reader(
uart: UartRx<'static, Blocking>,
signal: &'static Signal<CriticalSectionRawMutex, ()>,
) {
let mut uart = uart.into_async();
let mut received = 0;
let mut buf = [0u8; 128];
loop {
let len = uart.read_async(&mut buf[..]).await.unwrap();
received += len;
assert!(received <= LONG_TEST_STRING.len());
if received == LONG_TEST_STRING.len() {
break;
}
}
signal.signal(());
}
#[cfg(test)]
#[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())]
mod tests {
use core::fmt::Write;
use super::*;
#[init]
@ -33,7 +66,11 @@ mod tests {
.with_rx(rx)
.into_async();
Context { uart }
let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
Context {
interrupt: sw_ints.software_interrupt1,
uart,
}
}
#[test]
@ -48,4 +85,28 @@ mod tests {
ctx.uart.read_async(&mut buf[..]).await.unwrap();
assert_eq!(&buf[..], SEND);
}
#[test]
async fn test_long_strings(ctx: Context) {
// This is a regression test for https://github.com/esp-rs/esp-hal/issues/3450
// The issue was that the printed string is longer than the UART buffer so the
// end was lost. We can't test the fix in blocking code, only on
// dual-cores, but a preemptive interrupt executor should work on all
// chips.
let (rx, mut tx) = ctx.uart.into_blocking().split();
let interrupt_executor =
mk_static!(InterruptExecutor<1>, InterruptExecutor::new(ctx.interrupt));
let signal = &*mk_static!(Signal<CriticalSectionRawMutex, ()>, Signal::new());
let spawner = interrupt_executor.start(Priority::Priority3);
spawner.must_spawn(long_string_reader(rx, signal));
tx.write_str(LONG_TEST_STRING).unwrap();
// Wait for the signal to be sent
signal.wait().await;
}
}