Skip to content

Commit 5800661

Browse files
johnholmanfacchinm
authored andcommitted
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)
1 parent 99c294c commit 5800661

File tree

1 file changed

+12
-2
lines changed

1 file changed

+12
-2
lines changed

cores/arduino/HardwareSerial.cpp

+12-2
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,18 @@ size_t HardwareSerial::write(uint8_t c)
226226
// significantly improve the effective datarate at high (>
227227
// 500kbit/s) bitrates, where interrupt overhead becomes a slowdown.
228228
if (_tx_buffer_head == _tx_buffer_tail && bit_is_set(*_ucsra, UDRE0)) {
229-
*_udr = c;
230-
*_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << MPCM0))) | (1 << TXC0);
229+
// If TXC is cleared before writing UDR and the previous byte
230+
// completes before writing to UDR, TXC will be set but a byte
231+
// is still being transmitted causing flush() to return too soon.
232+
// So writing UDR must happen first.
233+
// Writing UDR and clearing TC must be done atomically, otherwise
234+
// interrupts might delay the TXC clear so the byte written to UDR
235+
// is transmitted (setting TXC) before clearing TXC. Then TXC will
236+
// be cleared when no bytes are left, causing flush() to hang
237+
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
238+
*_udr = c;
239+
*_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << MPCM0))) | (1 << TXC0);
240+
}
231241
return 1;
232242
}
233243
tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE;

0 commit comments

Comments
 (0)