In this article we’ll see how to implement periodic timer in Linux using C programming language. We’ll first create a timer library that will 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.
Click here to download the complete program with Makefile.
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.
/*mytimer.h*/ #ifndef TIME_H #define TIME_H #include <stdlib.h> typedef enum { TIMER_SINGLE_SHOT = 0, /*Periodic Timer*/ TIMER_PERIODIC /*Single Shot Timer*/ } t_timer; typedef void (*time_handler)(size_t timer_id, void * user_data); int initialize(); size_t start_timer(unsigned int interval, time_handler handler, t_timer type, void * user_data); void stop_timer(size_t timer_id); void finalize(); #endif
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 seconds. If the value of interval is 10, the timer will be expired (will call callback function) after 10 seconds. And in case of periodic timer, the timer will keep expiring after every 10 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 seconds. Two periodic timers, one will expire in every 10 seconds and other in every 5 seconds.
#include <stdio.h> #include <stdlib.h> #include "mytimer.h" void time_handler1(size_t timer_id, void * user_data) { printf("Single shot timer expired.(%d)\n", timer_id); } void time_handler2(size_t timer_id, void * user_data) { printf("10 sec timer expired. (%d)\n", timer_id); } void time_handler3(size_t timer_id, void * user_data) { printf("5 sec timer expired. (%d)\n", timer_id); } void main() { size_t timer1, timer2, timer3; initialize(); timer1 = start_timer(20, time_handler1, TIMER_SINGLE_SHOT, NULL); timer2 = start_timer(10, time_handler2, TIMER_PERIODIC, NULL); timer3 = start_timer(5, time_handler3, TIMER_PERIODIC, NULL); sleep(60); stop_timer(timer1); stop_timer(timer2); stop_timer(timer3); finalize(); }
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 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 seconds respectively. To make the timers periodic, we used TIMER_PERIODIC constant as 3rd parameter.
Then we simply waits for 60 seconds before stopping all the timers and finalizing the timer module.
Output of the above program is shown below:
5 sec timer expired. (28524944) 5 sec timer expired. (28524944) 10 sec timer expired. (28524896) 5 sec timer expired. (28524944) 5 sec timer expired. (28524944) 10 sec timer expired. (28524896) Single shot timer expired.(28524848) 5 sec timer expired. (28524944) 5 sec timer expired. (28524944) 10 sec timer expired. (28524896) 5 sec timer expired. (28524944) 5 sec timer expired. (28524944) 10 sec timer expired. (28524896) 5 sec timer expired. (28524944) 5 sec timer expired. (28524944) 10 sec timer expired. (28524896)
Implementation of the Timer Library
Here is the implementation of the timer library functions. This library internally uses the timerfd system calls.
/*mytimer.c*/ #include <stdint.h> #include <string.h> #include <sys/timerfd.h> #include <pthread.h> #include <poll.h> #include <stdio.h> #include "mytimer.h" #define MAX_TIMER_COUNT 1000 struct timer_node { int fd; time_handler callback; void * user_data; unsigned int interval; t_timer type; struct timer_node * next; }; static void * _timer_thread(void * data); static pthread_t g_thread_id; static struct timer_node *g_head = NULL; int initialize() { if(pthread_create(&g_thread_id, NULL, _timer_thread, NULL)) { /*Thread creation failed*/ return 0; } return 1; } size_t start_timer(unsigned int interval, time_handler handler, t_timer type, void * user_data) { struct timer_node * new_node = NULL; struct itimerspec new_value; new_node = (struct timer_node *)malloc(sizeof(struct timer_node)); if(new_node == NULL) return 0; new_node->callback = handler; new_node->user_data = user_data; new_node->interval = interval; new_node->type = type; new_node->fd = timerfd_create(CLOCK_REALTIME, 0); if (new_node->fd == -1) { free(new_node); return 0; } 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; } else { new_value.it_interval.tv_sec = 0; } new_value.it_interval.tv_nsec = 0; timerfd_settime(new_node->fd, 0, &new_value, NULL); /*Inserting the timer node into the list*/ new_node->next = g_head; g_head = new_node; return (size_t)new_node; } void stop_timer(size_t timer_id) { struct timer_node * tmp = NULL; struct timer_node * node = (struct timer_node *)timer_id; if (node == NULL) return; close(node->fd); if(node == g_head) { g_head = g_head->next; } tmp = g_head; while(tmp && tmp->next != node) tmp = tmp->next; if(tmp && tmp->next) { tmp->next = tmp->next->next; } if(node) free(node); } void finalize() { while(g_head) stop_timer((size_t)g_head); pthread_cancel(g_thread_id); pthread_join(g_thread_id, NULL); } struct timer_node * _get_timer_from_fd(int fd) { struct timer_node * tmp = g_head; while(tmp) { if(tmp->fd == fd) return tmp; tmp = tmp->next; } return NULL; } void * _timer_thread(void * data) { struct pollfd ufds[MAX_TIMER_COUNT] = {{0}}; int iMaxCount = 0; struct timer_node * tmp = NULL; int read_fds = 0, i, s; uint64_t exp; while(1) { pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_testcancel(); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); iMaxCount = 0; tmp = g_head; memset(ufds, 0, sizeof(struct pollfd)*MAX_TIMER_COUNT); while(tmp) { ufds[iMaxCount].fd = tmp->fd; ufds[iMaxCount].events = POLLIN; iMaxCount++; tmp = tmp->next; } read_fds = poll(ufds, iMaxCount, 100); if (read_fds <= 0) continue; if (read_fds <= 0) continue; for (i = 0; i < iMaxCount; i++) { if (ufds[i].revents & POLLIN) { s = read(ufds[i].fd, &exp, sizeof(uint64_t)); if (s != sizeof(uint64_t)) continue; tmp = _get_timer_from_fd(ufds[i].fd); if(tmp && tmp->callback) tmp->callback((size_t)tmp, tmp->user_data); } } } return NULL; }
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.
Read also: How to implement periodic timer in Linux kernel.
The post How to Implement Periodic Timer in Linux appeared first on QnA Plus.