Skip to content

xtimer_periodic_wakeup is not interrupt safe #8388

@Winkelkatze

Description

@Winkelkatze

Description

If an interrupt happens at the wrong time during a call to xtimer_periodic_wakeup with a short delay it may cause an internal overflow in the timer which results in the delay to be much longer than specified.

Steps to reproduce the issue

Reproducing this works best if the xtimer is built with ENABLE_DEBUG 1 and the task calling xtimer_periodic_wakeup has a low priority. (But it also happens without debug and at higher priorities (if the delay is short enough or too much is done in the interrupt))

To reproduce this, we need a low priority thread calling xtimer_periodic_wakeup with a short delay in an infinite loop. An other thread with a higher priority is waiting for a message that is sent in an interrupt. If this thread receives the message, it prints a not too small text on stdout. Important is that sending the text should take longer than the delay in xtimer_periodic_wakeup.

After a few interrupts the thread calling xtimer_periodic_wakeup is stuck.

Explanation

Looking at the xtimer implementation, it is easy to see the actual problem:
The interrupt needs to happen in _xtimer_periodic_wakeup after reading 'now' and before calling '_xtimer_set_absolute'. (This also explains why it works much better if debug is active, because the debug print takes some time which increases the chance of the interrupt happening in this range)
The interrupt (or sending the message from within the interrupt) will cause the scheduler to run again which results in execution of the higher priority thread that is waiting for that message. When this thread yields again and the thread calling _xtimer_periodic_wakeup is resumed, more time than specified in delay has elapsed. _xtimer_set_absolute is called anyway and gets the wakeup target as parameter. It will then recalculate the difference between the target and the current time (which is now larger than target). This causes the _xtimer_set_absolute to detect this overflow and increment timer->long_target. This way the delay will be much longer than specified.

References

This may be related to the PRs: #7628 and #5428

Versions

Tested with the current git version of RIOT on STM32F103x8.

Installed toolchain versions

native gcc: gcc (GCC) 7.2.1 20171224
msp430-gcc: missing/error
avr-gcc: missing/error
arm-none-eabi-gcc: arm-none-eabi-gcc (Arch Repository) 7.2.0
ips-mti-elf-gcc: missing/error
clang: clang version 5.0.1 (tags/RELEASE_501/final)
arm-none-eabi-newlib: "2.5.0"
mips-mti-elf-newlib: missing/error
avr-libc: missing/error (missing/error)
cppcheck: missing
coccinelle: missing
git: git version 2.15.1

Metadata

Metadata

Assignees

Labels

Area: timersArea: timer subsystemsType: bugThe issue reports a bug / The PR fixes a bug (including spelling errors)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions