How to Implement Periodic Timer in Linux?

As a programmer we often need to do some task repeatedly after some time interval. We can think of a simple loop containing a function call to do the task followed by a sleep() call to wait for some time.  This seems to be very simple solution but what if we don’t want to sit ideal while waiting. We want to do other tasks while someone else keeps track of the timeout and wakes me up when timeout occurs and continue to track time for the next timeout. We can do the required timeout task after getting the timeout signal. This is an example of periodic timer.

Timer can be single shot also. One example could be in networking programming, we sent something to the peer and expecting response from the other end. What if the response never comes? How long should we wait? In this case we can start a timer that will notify after some timeout value expires. After timeout we can take necessary actions for not receiving response from other end.

In this article we’ll see how to implement our own timer library using C programming language on Linux. This library can be used to create one or more timers. Timers can be periodic or single shot. You’ll be able to use this timer library in your own program to create one or multiple timers.

The Timer Header File

We’ll start with the header file of the timer module. The following header file, mytimer.h, displays the functions and data structures that can be used by other programs to create periodic or single shot timers.

In the header file, there are four functions.

  • initialize(): The user of this timer library has to call this function before using any other function. This function should be called only once, no matter how many timers you want to create.
  • start_timer(): This function is used to create a timer. It returns the timer id that would be used in stop_timer() function. It has the following input parameters.
    • interval: Timeout interval in milli seconds. If the value of interval is 10, the timer will be expired (will call callback function) after 10 milli seconds. And in case of periodic timer, the timer will keep expiring after every 10 milli seconds.
    • handler: The call back function. After expiry of the timer, this callback function will be called.
    • type: This specifies whether the timer is periodic or single shot. The value of type could be TIMER_PERIODIC or TIMER_SINGLE_SHOT.
    • user_data: User of the library can specify any data in form of void pointer that would be passed in the callback function in timer expiry.
  • stop_timer(): This function is used to stop a particular timer. It takes the timer id as input. It stops the timer specified by the timer id. Timer id is returned by the start_timer() function in time of creating the timer.
  • finalize(): The function should be called when the timer module is no longer required. It stops (and deletes) all running timers.

Before going into the actual implementation of these functions, we’ll see how you can use these functions to implement periodic and single shot timer in the following example program.

Example Program

In this example program, three timers are created. One single shot timer which will expire after 20 milli seconds. Two periodic timers, one will expire in every 10 milli seconds and other in every 5 milli seconds.

In this program, we have three functions time_handler1(), time_handler2() and timer_handler3() for three timers. These functions will be used as callback functions. As we discussed earlier that before calling any timer function, initialize() needs to be called to make the timer module ready. That we did in line number 25.

In next three lines, we started 3 timers. First timer is the single shot one. In the first parameter is the time out value which is 20 milli seconds. The next parameter is the function pointer that would be called in timer expiry. The 3rd parameter specifies that the timer is single shot with the constant TIMER_SINGLE_SHOT. The next two timer_start() functions are similar. We specified timeout values 10 and 5 milli seconds respectively. To make the timers periodic, we used TIMER_PERIODIC constant as 3rd parameter.

Then we simply waits for 6 seconds before stopping all the timers and finalizing the timer module.

Output of the above program is shown below:

Implementation of the Timer Library

Here is the implementation of the timer library functions. This library internally uses the timerfd system calls.

We’ll briefly discuss about the timerfd system calls to understand this code. The timerfd_create() function is used create a timer. This function returns an fd (file descriptor) which will be used to start or monitor the timer. The timerfd_settime() function is used to start the timer. We can also specify whether the timer is periodic or single shot. If one or more timer expirations have occurred, then the corresponding file descriptors (fd) will be readable using read() system call. Our strategy is to have one thread that will continuously check all file descriptors (fd) using poll() system call. If any file descriptor is set (POLLIN) then we’ll call the callback function of the corresponding timer.
The data structure, timer_node, is declared in line 14 to store all information related to a timer. As we maintain multiple timers in this library, we store the timer contexts in a linked list. The head of the linked list, g_head, is defined in line 26.
Function definitions:

  • initialize(): This function creates a thread that will monitor all timers to check whether any timer is expired. It stores the thread id in g_thread_id which will be used to stop the thread.
  • start_timer(): This function creates and starts a timer. It first allocates memory (new_node) for the new timer. It stores all relavent information such as callback function, interval, type (periodic or single shot) of the timer in the new_node structure. Then it create the timer using timerfd_create() function and stores the returned fd (file descriptor) in new_node. Time out interval is set in new_value.it_value.tv_sec variable and in case of periodic timer the interval is set in new_value.it_interval.tv_sec variable also. Then it starts the timer using timerfd_settime() function. At the end it inserts the timer structure (new_node) in the linked list.
  • stop_timer(): This function stops the timer using close() system call using the file descriptor (fd) of the timer. Then the timer data structure is removed from the linked list.
  • finalize(): This function should be called when the timer module is no longer required. It first stops all running timers, if any, and then stops the thread.
  • _timer_thread(): It continuously checks if any timer file descriptor (fd) is set using poll() system call. If any timer file descriptor is set and readable using read() system call, then calls the callback function of the timer with timer id and user_data.

Click here to download the complete program with Makefile.
Read also: How to implement periodic timer in Linux kernel.

19 thoughts on “How to Implement Periodic Timer in Linux?”

  1. I need to embed your code into my c++ code but it does not compile fine, The following error arises :>
    error: invalid conversion from ‘void (*)()’ to ‘time_handler {aka void (*)(unsigned int, void*)}’ [-fpermissive]
    time_handler timer_handler1=adc_timer_handler;

    note: adc_timer_handler is my callback function.
    do you suggest a solution?

  2. A nice example for timer implementation. I have a few comments which can help you to improve the performance of your program.

    1. read_fds = poll(ufds, iMaxCount, 100); This will introduce a bit of load on the system, as it will update the FDs after every 100 m.secs even if no timer has been started or stopped.
    An optimization could be wait here indefinitely and signal the thread whenever a timer is started/stopped.

    2. In function stop_timer()
    tmp = g_head;
    while(tmp && tmp->next != node) tmp = tmp->next;
    if(tmp && tmp->next)
    tmp->next = tmp->next->next;
    —The above part is a bit pointless when node == g_head as you already found the item. Suggestion to put it inside an else statement.
    and the test for tmp->next in “if(tmp && tmp->next)” is not really needed as it can’t be that tmp==NULL and tmp->next==NULL also.

    1. Thanks for the in-depth analysis of the code.
      1. We can wait indefinitely in poll() function until any timer is expired but it has two problems.
      a) While poll() is waiting of the existing timers which will expire, say, in far future, new timer is added to the timer module with shorter duration. That timer will not be added to the FD list and will not get chance to expire.
      b) You can not stop the program gracefully until any timer expires. For example, your next timer will expire in next one hour. So in next one hour you won’t be able to terminate the program gracefully.

      2. You are right, node == g_head and next few lines should be in else section. I did that. You are also right that checking tmp->next for NULL is meaningless. If tmp is not null tmp->next is definitely your node. So tmp->next can not be NULL.

  3. Srikanta, very good explanation to start off with timer concept. After going through your code, couple of questions popped up in my mind with respect to performance of whole timer implementation. It would be really great if you/some one can answer my following questions.

    1. is this solution scalable in case of huge number of active timers let us assume 1 Million or more?
    2. _get_timer_from_fd(): This function doing a linear search on linked list to retrieve a node which is O(n) complexity, which may cause delay in executing callback function. suppose if 1000 timers expired at same time, there might be delay in executing 1000th call back function due to 999 times linear search.
    3.I couldn’t understand why read() function call was used, what would be the 64-bytes data written into exp variable and we are not using exp anywhere, is read() call required here?
    4. can you explain if (s != sizeof(uint64_t)) continue;
    5. for (i = 0; i < iMaxCount; i++)
    I'm really against to this for{} loop becasue everytime poll() returns it itereate iMaxCount times in worst case. This is going to be a big blow to software. is there any other mechanism to optimize this?

    1. This program is for illustration purpose. I wanted to keep the program as simple as possible. It is not suitable for high performance production system. It will not also scale up to million timers. For our production software we use B-tree and jiffies for timer tracking. It would be a complicated topic and complex program. I’ll try to cover that sometime.
      Your other performance related concerns are also valid but I don’t want to change those at this moment to maintain its simplicity.

      3. Even though we are not using exp, we need to call read(). Otherwise the associated FD will return from next poll() even if the timer does not expire and give you wrong expiry. You can use expiry count (exp) if you want though.
      4. if (s != sizeof(uint64_t)) continue; If read does not read 64 bits (8 bytes), then some error happened. In this situation we are skipping that timer for that iteration.

    1. In my example I assumed the interval to be in seconds for illustration purpose. But we can change the code little bit to get millisecond level granularity.

      If your interval is in milliseconds, these lines of start_timer() function should be changed:

      new_value.it_value.tv_sec = interval;
      new_value.it_value.tv_nsec = 0;
      if (type == TIMER_PERIODIC)
      new_value.it_interval.tv_sec = interval;
      new_value.it_interval.tv_sec = 0;
      new_value.it_interval.tv_nsec = 0;

      Replace the above lines with the new ones.

      new_value.it_value.tv_sec = 0;
      new_value.it_value.tv_nsec = interval * 1000000;

      if (type == TIMER_PERIODIC)
      new_value.it_interval.tv_nsec = interval * 1000000;
      new_value.it_interval.tv_nsec = 0;

      new_value.it_interval.tv_sec= 0;

      1. very good example , i tried to implement resolution in mil seconds unfortunately your suggestion does not work any suggestion?

  4. I created the header file and tried to run the example program given above and encountered the following errors and warnings on MAC OSX:

    mohanishs-air:Desktop mohanishmankar$ gcc timer.c
    timer.c: In function ‘main’:
    timer.c:31:5: warning: implicit declaration of function ‘sleep’ [-Wimplicit-function-declaration]
    Undefined symbols for architecture x86_64:
    “_finalize”, referenced from:
    _main in ccRtE3Is.o
    “_initialize”, referenced from:
    _main in ccRtE3Is.o
    “_start_timer”, referenced from:
    _main in ccRtE3Is.o
    “_stop_timer”, referenced from:
    _main in ccRtE3Is.o
    ld: symbol(s) not found for architecture x86_64
    collect2: error: ld returned 1 exit status

    Please help me out. Thank you.

    1. The implementation of this article is based on timerfd which is Linux specific. I don’t think this is going to work on OSX. But there must be something equivalent in OSX. I don’t have MAC system to try with. If I can get that I’ll update you soon.

  5. Is time_handler can be called on a different thread other than the thread that initialized the timer (start_timer)?

    1. In fact the timer_handler will be called from different thread context than the thread that started the timer using start_timer() function.
      In the example program above, the main thread created the timers but the timer_handler functions got called from different thread. It is not obvious from the output but you can modify the program to print the current thread ids from the main() function and from the event handlers. You will see that the main thread id (that started the timers) is different from the thread ids of the event_handlers.
      From the implementation we can see that: the initialize() function is creating a thread by this line “if(pthread_create(&g_thread_id, NULL, _timer_thread, NULL))”. And in the thread function _timer_thread() is calling the handler callbacks from this line “if(tmp && tmp->callback) tmp->callback((size_t)tmp, tmp->user_data);”. That means all the time_handlers will be called from the thread context of _timer_thread().
      Please remember that this is my implementation. You can implement any threading model using timerfd system calls.

  6. Very nice.
    Couple of minor comments:
    a) Typo:
    if (read_fds <= 0) continue;

    if (read_fds <= 0) continue;

    I'm guessing you inadvertently pasted twice as this seems redundant
    Each time through while(1) loop, you:
    memset(ufds, 0, sizeof(struct pollfd)*MAX_TIMER_COUNT);
    which sets all 1000 ufds to 0. Doesn't this waste time? Could you possibly replace it with:
    memset(&ufds[iMaxCount], 0, sizeof(struct pollfd));
    at the beginning of the while(tmp) loop? Indeed, I'm not sure it's even needed as at the beginning of the thread you set ufds to 0 using
    struct pollfd ufds[MAX_TIMER_COUNT] = {{0}};
    and you only write into ufds being used and you write all fields except revents which isn't being used; and if you wanted to you could just add a line to set it to 0 if you really wanted to be safe.
    Summary: very minor comments as the example is very nicely done.

    1. Thank you for your comment.
      You are right, the line “if (read_fds <= 0) continue;" pasted twice.
      Your second point is also correct. We don't need to unnecessarily set all fds to 0 if you are concerned about performance. The initialization of the array "struct pollfd ufds[MAX_TIMER_COUNT] = {{0}};" will not help much. This is initialized once but the loop will iterate many times and the number of fds can increase and decrease several times. Putting the line "memset(&ufds[iMaxCount], 0, sizeof(struct pollfd));" at the beginning of the while(tmp) should be enough, as you mentioned, to be in the safe side. I'll update this article accordingly.
      Thanks again for helping us to make our article better.

Leave a Reply

Your email address will not be published. Required fields are marked *