Skip to content

Commit 95bb83e

Browse files
robert-rozeefacchinm
authored andcommitted
make serial.available, peek, read atomic
when SERIAL_RX_BUFFER_SIZE is defined as greater than 256, the current HardwareSerial.cpp code almost works correctly. but not quite. the function serial.available may return an incorrect number of characters waiting (though when treated purely as a boolean it seems not to fault).serial.peek has a similar issue detecting if a character is waiting, while serial.read may also leave _rx_buffer_tail corrupt to an interrupt occurring mid-update (this corruption may cause the ISR to be mistaken about how much free is available in the Rx buffer). the proposed changes to these three functions add cli/sti pairings around the critical pieces of code. i have tested and verified the change made to serial.available as correcting the problem, while the change to serial.peek follows the exact same pattern. the changes to serial.read have been confirmed as not breaking the function, but i have not been in a position to test the failure of the original (non-atomic) version to make comparisons. multiple testing was conducted at 115,200 baud and 500,000 baud using data streams of 1,000,000 characters sent over a 2 minute interval. any error causing loss of character would have resulted in a catastrophic (ie, very obvious) failure. cheers, rob :-) -> Squash and rebase of arduino/Arduino#3848
1 parent b084848 commit 95bb83e

File tree

1 file changed

+42
-6
lines changed

1 file changed

+42
-6
lines changed

cores/arduino/HardwareSerial.cpp

+42-6
Original file line numberDiff line numberDiff line change
@@ -151,26 +151,62 @@ void HardwareSerial::end()
151151

152152
int HardwareSerial::available(void)
153153
{
154-
return ((unsigned int)(SERIAL_RX_BUFFER_SIZE + _rx_buffer_head - _rx_buffer_tail)) % SERIAL_RX_BUFFER_SIZE;
154+
#if (SERIAL_RX_BUFFER_SIZE>256)
155+
uint8_t oldSREG = SREG; // save interrupt flag
156+
cli(); // disable interrupts
157+
#endif
158+
rx_buffer_index_t head = _rx_buffer_head; // retrieve Rx head index
159+
rx_buffer_index_t tail = _rx_buffer_tail; // retrieve Rx tail index
160+
#if (SERIAL_RX_BUFFER_SIZE>256)
161+
SREG = oldSREG; // restore the interrupt flag
162+
#endif
163+
return ((unsigned int)(SERIAL_RX_BUFFER_SIZE + head - tail)) % SERIAL_RX_BUFFER_SIZE;
155164
}
156165

157166
int HardwareSerial::peek(void)
158167
{
159-
if (_rx_buffer_head == _rx_buffer_tail) {
168+
#if (SERIAL_RX_BUFFER_SIZE>256)
169+
uint8_t oldSREG = SREG; // save interrupt flag
170+
cli(); // disable interrupts
171+
#endif
172+
rx_buffer_index_t head = _rx_buffer_head; // retrieve Rx head index
173+
rx_buffer_index_t tail = _rx_buffer_tail; // retrieve Rx tail index
174+
#if (SERIAL_RX_BUFFER_SIZE>256)
175+
SREG = oldSREG; // restore the interrupt flag
176+
#endif
177+
if (head == tail) {
160178
return -1;
161179
} else {
162-
return _rx_buffer[_rx_buffer_tail];
180+
return _rx_buffer[tail];
163181
}
164182
}
165183

166184
int HardwareSerial::read(void)
167185
{
168186
// if the head isn't ahead of the tail, we don't have any characters
169-
if (_rx_buffer_head == _rx_buffer_tail) {
187+
#if (SERIAL_RX_BUFFER_SIZE>256)
188+
uint8_t oldSREG = SREG; // save interrupt flag
189+
cli(); // disable interrupts
190+
#endif
191+
rx_buffer_index_t head = _rx_buffer_head; // retrieve Rx head index
192+
rx_buffer_index_t tail = _rx_buffer_tail; // retrieve Rx tail index
193+
#if (SERIAL_RX_BUFFER_SIZE>256)
194+
SREG = oldSREG; // restore the interrupt flag
195+
#endif
196+
197+
if (head == tail) {
170198
return -1;
171199
} else {
172-
unsigned char c = _rx_buffer[_rx_buffer_tail];
173-
_rx_buffer_tail = (rx_buffer_index_t)(_rx_buffer_tail + 1) % SERIAL_RX_BUFFER_SIZE;
200+
unsigned char c = _rx_buffer[tail];
201+
202+
#if (SERIAL_RX_BUFFER_SIZE>256)
203+
uint8_t oldSREG = SREG; // save interrupt flag
204+
cli(); // disable interrupts
205+
#endif
206+
_rx_buffer_tail = (tail + 1) % SERIAL_RX_BUFFER_SIZE;
207+
#if (SERIAL_RX_BUFFER_SIZE>256)
208+
SREG = oldSREG; // restore the interrupt flag
209+
#endif
174210
return c;
175211
}
176212
}

0 commit comments

Comments
 (0)