定时器溢出竞争条件
Timer overflow race condition
我正在使用一个带 16 位定时器的微控制器。当前值可以从寄存器中读取。但是我需要一个 32 位计数器。每次定时器溢出时,它都会产生一个中断。我当前的解决方案类似于下面的代码。每次计时器溢出时,变量 counter_high
都会递增。当前计数器值读取为 counter_high
和定时器寄存器的组合。
volatile uint16_t counter_high = 0;
uint32_t get_counter(void)
{
return (counter_high << 16) | timer->counter;
}
void timer_overflow(void)
{
counter_high++;
}
这似乎有效。但是我开始想知道如果在执行 get_counter()
时计时器溢出会发生什么?我可以获得 counter_high
的旧值与 timer->counter
的新值相结合,反之亦然。
是否有防止此问题的最佳做法?
阅读timer->counter
前后阅读counter_high
。如果 counter_high
的读取值没有改变,那么您就知道 timer->counter
没有在读取之间翻转,因此您可以信任从 timer->counter
.
读取的值
但是,如果 counter_high
在两次读取之间发生变化,那么您知道 timer->counter
在两次读取之间的某个时间翻滚。这意味着你不能相信你从 timer->counter
读取的值,因为你不知道你是在翻转之前还是之后读取它。但是现在您知道 timer->counter
最近才翻车所以您可以再次阅读它并知道它不会第二次翻车。
uint32_t get_counter(void)
{
uint32_t first_counter_high = counter_high;
uint32_t counter_low = timer->counter;
uint32_t second_counter_high = counter_high;
if (first_counter_high != second_counter_high)
{
counter_low = timer->counter; // Read timer->counter again, after rollover.
}
return (second_counter_high << 16) | counter_low;
}
我正在使用一个带 16 位定时器的微控制器。当前值可以从寄存器中读取。但是我需要一个 32 位计数器。每次定时器溢出时,它都会产生一个中断。我当前的解决方案类似于下面的代码。每次计时器溢出时,变量 counter_high
都会递增。当前计数器值读取为 counter_high
和定时器寄存器的组合。
volatile uint16_t counter_high = 0;
uint32_t get_counter(void)
{
return (counter_high << 16) | timer->counter;
}
void timer_overflow(void)
{
counter_high++;
}
这似乎有效。但是我开始想知道如果在执行 get_counter()
时计时器溢出会发生什么?我可以获得 counter_high
的旧值与 timer->counter
的新值相结合,反之亦然。
是否有防止此问题的最佳做法?
阅读timer->counter
前后阅读counter_high
。如果 counter_high
的读取值没有改变,那么您就知道 timer->counter
没有在读取之间翻转,因此您可以信任从 timer->counter
.
但是,如果 counter_high
在两次读取之间发生变化,那么您知道 timer->counter
在两次读取之间的某个时间翻滚。这意味着你不能相信你从 timer->counter
读取的值,因为你不知道你是在翻转之前还是之后读取它。但是现在您知道 timer->counter
最近才翻车所以您可以再次阅读它并知道它不会第二次翻车。
uint32_t get_counter(void)
{
uint32_t first_counter_high = counter_high;
uint32_t counter_low = timer->counter;
uint32_t second_counter_high = counter_high;
if (first_counter_high != second_counter_high)
{
counter_low = timer->counter; // Read timer->counter again, after rollover.
}
return (second_counter_high << 16) | counter_low;
}