Simplified Header-only volatile read and write in C++
A C and C++ keyword commonly used to suppress compiler optimizations. Commonly used when performing memory mapped I/O. Volatile can also be simply thought of as "Hey compiler, I do not want you to optimize whatever I do on this value"
#include <cinttypes>
uint16_t volatile tick_count = 0U;
auto TickISR() -> void {
// increment tick count
tick_count++;
}
auto PerformTask() -> void {
// do something
}
int main(){
while(true){
while(tick_count > 512U) {
PerformTask();
};
};
}
In this example TickISR
might appear not to be called at all and thus, without volatile
the compiler might assume tick_count
never changes before and after main
. Since TickISR
can actually be called by an interrupt request, the volatile
keyword is needed to prevent the assumption that tick_count
might not change (remain at 0U). Else, the PerformTask()
might never execute in this example.
- The
volatile
keyword is easily misused and often hinders performance and compiler optimizations in the process. - It is difficult to tell by just looking at a code whether we are dealing with
volatile
reads and writes or not, especially as the code base grows larger. - Declaring variables as
volatile
is not the initial intention forvolatile
. intended use (by C standard) is for pointers only, just as in:
#include <cinttypes>
uint16_t tick_count = 0U;
auto TickISR() -> void {
// get tick count address
auto tick_count_ptr = &tick_count;
// make volatile so whatever we do on the variable is not optimized / elided by the compiler
auto volatile_tick_count_ptr = (volatile uint16_t *)tick_count_ptr;
// here, we perform a volatile write (increment)
(*volatile_tick_count_ptr)++;
}
#include <cinttypes>
#include "volta/volta.h"
uint16_t tick_count = 0U;
auto TickISR() -> void {
// perform a volatile read
auto tick_count_capture = volta::volatile_read(&tick_count);
// increment tick count
tick_count_capture++;
// perform a volatile write
volta::volatile_write(&tick_count, tick_count_capture);
}
auto PerformTask() -> void {
// do something
}
int main(){
while(true){
while(volta::volatile_read(&tick_count) > 512U) {
PerformTask();
};
};
}
- Never use the keyword
volatile
- Only use
volta::volatile_read
andvolta::volatile_read
on integral types or intergal member types
- This is inspired by JF Bastein's lightning talk at CppCon 2019
- Also inspired by Rust's
core::ptr::read_volatile
andcore::ptr::write_volatile
- This is an header-only library and can be used directly in your projects.
- For CMake, use the command:
add_subdirectory(parent_dir/volta volta_dependency)
whereparent_dir
is the parent directory of volta as downloaded to your workspace.
- C++ 11
- C++ 14
- C++ 17
- C++ 20
- None
- None
- volta
- None