Way back in 2009, I wrote some code under Linux/GNU to use timers. Back then, it was the setitimer(2) feature, originally from BSD. But that approach has a flaw: real-time clock adjustments, for example via Network Time Protocol (NTP), can jitter the timer period. The newer timer_create(2) et al. POSIX timers can be configured to use a monotonic clock that makes no such adjustment.
POSIX timers can also be configured to either send a kill(2)-type signal, like SIGALRM, just like the BSD timers, or to invoke a callback function. While refactoring my code to use POSIX timers while maintaining the original signal-based API, I added the ability to also use timers with callback functions. I functionally tested the function callback feature by using it to do Pulse Width Modulation (PWM) on a General Purpose Input/Output (GPIO) pin. The functional test ran fine for hours... until it segfaulted! Pointing the GNU Debugger (GDB) at the core dump showed the callback had used a pointer that it shared with the application, and that pointer was NULL.
It only took me a few moments to realize it was a race condition: the callback runs in the context of its own POSIX thread. When the main thread in the application disarms (stops) the timer, then releases resources associated with it, the timer callback can be running, especially on a multicore processor. The timer callback had the resources pulled out from under it. I added a POSIX mutex and condition so that the main thread waits until the callback thread acknowledges that it's being stopped.Here's a code snippet from the callback function. Note that non-periodic (a.k.a. one-shot) timers place themselves in the idle state as soon as their callback completes.
No comments:
Post a Comment