From 58006613a7c858417881744419c5da54cc21a401 Mon Sep 17 00:00:00 2001 From: John Holman Date: Wed, 25 Oct 2017 11:07:35 +0100 Subject: Fix flush hanging issue Make write to UDR and clearing of TXC bit in flush() atomic to avoid race condition. Fixes #3745 (second different issue introduced later but discussed in the same issue) --- cores/arduino/HardwareSerial.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'cores/arduino/HardwareSerial.cpp') diff --git a/cores/arduino/HardwareSerial.cpp b/cores/arduino/HardwareSerial.cpp index e6ccdef..ecd7918 100644 --- a/cores/arduino/HardwareSerial.cpp +++ b/cores/arduino/HardwareSerial.cpp @@ -226,8 +226,18 @@ size_t HardwareSerial::write(uint8_t c) // significantly improve the effective datarate at high (> // 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. if (_tx_buffer_head == _tx_buffer_tail && bit_is_set(*_ucsra, UDRE0)) { - *_udr = c; - *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << MPCM0))) | (1 << TXC0); + // If TXC is cleared before writing UDR and the previous byte + // completes before writing to UDR, TXC will be set but a byte + // is still being transmitted causing flush() to return too soon. + // So writing UDR must happen first. + // Writing UDR and clearing TC must be done atomically, otherwise + // interrupts might delay the TXC clear so the byte written to UDR + // is transmitted (setting TXC) before clearing TXC. Then TXC will + // be cleared when no bytes are left, causing flush() to hang + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + *_udr = c; + *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << MPCM0))) | (1 << TXC0); + } return 1; } tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE; -- cgit v1.2.3-18-g5258