Wednesday, October 21, 2020

Timer Threads

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 functionWhile 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 the code snippet from the timer stop function in Diminuto, a framework of C code useful for the kinds of work I am typically called upon to do. (You can click on an image to see a larger version.) The stop timer function informs the timer callback that it is being disarmed, and waits for the callback to acknowledge that fact, which it will do by transitioning to the idle state and condition signaling the application. The POSIX mutex and condition objects are embedded inside the Diminuto timer object.

Screen Shot 2020-10-19 at 07.43.00

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.

Screen Shot 2020-10-21 at 11.24.35 AM

As soon as I saw the backtrace in GDB, I knew I was going to learn something useful. That made for a good day.

No comments: