well, volatile in itself does not imply atomic R/W access as per the C++ standard. The compiler generated code does not guarantee that the access will be atomic, just that the lifetime of a register copy has to be limited and that limited optimisation can be done around it.
it can (and often is) by the C++ standard. If you want atomic access to your volatile variable (very very often required), you need to code for it. critical sections like blocking interrupts, using semaphores, ...
➜ so it's two separate concepts. I think that's what @bperrybap was trying to say
Have a look at the HardwareSerial.cpp code for example and you'll see that they support multibyte for the Tx or Rx buffer index
#if (SERIAL_TX_BUFFER_SIZE>256)
typedef uint16_t tx_buffer_index_t;
#else
typedef uint8_t tx_buffer_index_t;
#endif
#if (SERIAL_RX_BUFFER_SIZE>256)
typedef uint16_t rx_buffer_index_t;
#else
typedef uint8_t rx_buffer_index_t;
#endif
this type is used for the instance variables, that are defined as volatile:
volatile rx_buffer_index_t _rx_buffer_head;
volatile rx_buffer_index_t _rx_buffer_tail;
volatile tx_buffer_index_t _tx_buffer_head;
volatile tx_buffer_index_t _tx_buffer_tai
and thus in the code they allow for atomicity
you'll see things like this
int HardwareSerial::availableForWrite(void)
{
tx_buffer_index_t head;
tx_buffer_index_t tail;
TX_BUFFER_ATOMIC {
head = _tx_buffer_head;
tail = _tx_buffer_tail;
}
if (head >= tail) return SERIAL_TX_BUFFER_SIZE - 1 - head + tail;
return tail - head - 1;
}
where the local copies are made in a protected block