Added Timer Library

This commit is contained in:
Abhishek Sagar
2020-10-13 00:43:26 -07:00
parent 5497b5f026
commit bb256bf9c8
3 changed files with 558 additions and 0 deletions

View File

@@ -0,0 +1,213 @@
/*
* =====================================================================================
*
* Filename: timerExample.c
*
* Description: This file demonstrates the use of POSIX Timer routines
*
* Version: 1.0
* Created: 10/12/2020 11:25:06 AM
* Revision: none
* Compiler: gcc
*
* Author: ABHISHEK SAGAR (), sachinites@gmail.com
* Organization: Juniper Networks
*
* =====================================================================================
*/
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
/* Example */
typedef struct pair_{
int a;
int b;
} pair_t;
pair_t pair = { 10, 20 };
/* Define the structure which will be passed as an
* argument to the timer callback function, We do this
* to have more control over the timer*/
typedef struct timer_user_arg_{
void *user_arg;
size_t user_arg_size;
timer_t *timer;
uint32_t counter;
} timer_user_arg_t;
typedef struct timer_user_arg_ Timer_t;
unsigned long
get_time_remaining_in_mill_sec(
struct timespec *time){
unsigned long milli_sec = 0;
milli_sec = time->tv_sec * 1000;
milli_sec += time->tv_nsec / 1000000;
return milli_sec;
}
/* The Timer callback function which will be called every
* time the timer expires. The signature of the function would be :
* void <fn-name>(union sigval)
* */
void
timer_callback(union sigval arg){
/* Extract the user data structure*/
timer_user_arg_t *timer_user_arg =
(timer_user_arg_t *) (arg.sival_ptr);
pair_t *pair = (pair_t *)(timer_user_arg->user_arg);
/*
* Timer is also passed as an argumeent to the timer
* handler routine so that we can change the properties
* of the timer from within the handler itself
* */
timer_t *timer = timer_user_arg->timer;
/* Get remaining time in next timer expiry */
struct itimerspec remaining_time;
memset(&remaining_time, 0, sizeof(struct itimerspec));
int rc = timer_gettime(*timer, &remaining_time);
printf("Timer = %p, Expiry Remaining time = %ld msec,"
" pair : [%d, %d], invocation no = %u, overrun = %d\n",
timer, get_time_remaining_in_mill_sec(&remaining_time.it_value),
pair->a, pair->b, timer_user_arg->counter,
timer_getoverrun(*timer));
timer_user_arg->counter++;
/* Let us kill the timer when it is invoked for 10 times */
if(timer_user_arg->counter == 10){
rc = timer_delete(*timer); /* Now timer wont fire 11th time */
if(rc < 0) {
printf("Error in Timer Deletion, errno = %d\n", errno);
exit(0);
}
/* Free up all the memory/resources */
free(timer_user_arg->timer);
free(timer_user_arg);
return;
}
}
void
timer_demo(){
int ret;
timer_t *timer;
struct sigevent evp;
/* You can take it as a local variable if you
* wish, in that case we will not free it in
* timer handler fn */
timer = calloc(1, sizeof(timer_t));
/* evp variable is used to setup timer properties*/
memset(&evp, 0, sizeof(struct sigevent));
/* Fill the the user defined data structure.
* When timer expires, this will be passed as
* argument to the timer fn handler */
timer_user_arg_t *timer_user_arg =
calloc(1, sizeof(timer_user_arg_t));
timer_user_arg->user_arg = (void *)&pair;
timer_user_arg->user_arg_size = sizeof(pair_t);
timer_user_arg->timer = timer;
timer_user_arg->counter = 0;
evp.sigev_value.sival_ptr = (void *)timer_user_arg;
/* On timer Expiry, We want kernel to launch the
* timer handler routine in a separate thread context */
evp.sigev_notify = SIGEV_THREAD;
/* Register the timer hander routine. This routine shall
* be invoked when timer expires*/
evp.sigev_notify_function = timer_callback;
/* Create a timer. It is just a timer initialization, Timer
* is not fired (Alarmed) */
ret = timer_create (CLOCK_REALTIME,
&evp,
timer);
if ( ret < 0) {
printf("Timer Creation failed, errno = %d\n", errno);
exit(0);
}
/* Let us say, I want to start the timer after 5 seconds from now
* (now = say, t = 0) and once the 5 seconds elapsed, i
* want the timer to keep firing after every 2 seconds repeatedly.
* It simply mean that - if i start the timer as time t = 0, then
* timer handler routine (timer_callback) shall be called at t = 5,
* t = 7, t = 9 ... so on*/
/* Let us setup the time intervals */
struct itimerspec ts;
/* I want the timer to fire for the first time after 5 seconds
* and 0 nano seconds*/
ts.it_value.tv_sec = 5;
ts.it_value.tv_nsec = 0;
/* After the timer has fired for the first time, i want the timer
* to repeatedly fire after every 2 sec and 0 nano sec */
ts.it_interval.tv_sec = 2;
ts.it_interval.tv_nsec = 0;
/* Now start the timer*/
ret = timer_settime (*timer,
0,
&ts,
NULL);
if ( ret < 0) {
printf("Timer Start failed, errno = %d\n", errno);
exit(0);
}
pause();
}
/* Returns NULL in timer creation fails, else
* return a pointer to Timer object*/
Timer_t*
setup_timer(
void (*)(void *, size_t), /* Timer Callback */
unsigned long long, /* First expiration time interval in msec */
bool, /* true if timer is repeatable, false for one-shot */
unsigned long long, /* Subsequent expiration time interval in msec */
void *arg, /* Arg to timer callback */
size_t arg_size); /* Arg memory size */
int
main(int argc, char **argv){
timer_demo();
pause();
return 0;
}

View File

@@ -0,0 +1,246 @@
/*
* =====================================================================================
*
* Filename: timerExample.c
*
* Description: This file demonstrates the use of POSIX Timer routines
*
* Version: 1.0
* Created: 10/12/2020 11:25:06 AM
* Revision: none
* Compiler: gcc
*
* Author: ABHISHEK SAGAR (), sachinites@gmail.com
* Organization: Juniper Networks
*
* =====================================================================================
*/
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <assert.h>
#include <errno.h>
#include "timerlib.h"
static unsigned long
timespec_to_millisec(
struct timespec *time){
unsigned long milli_sec = 0;
milli_sec = time->tv_sec * 1000;
milli_sec += time->tv_nsec / 1000000;
return milli_sec;
}
static void
timer_fill_itimerspec(struct timespec *ts,
unsigned long msec) {
memset(ts, 0, sizeof(struct timespec));
if(!msec) return;
unsigned long sec = msec/1000;
ts->tv_sec = sec;
unsigned long remaining_msec = msec % 1000;
ts->tv_nsec = remaining_msec * (1000000);
}
static void
timer_callback_wrapper(union sigval arg){
Timer_t *timer = (Timer_t *)(arg.sival_ptr);
timer->invocation_counter++;
if(timer->thresdhold &&
(timer->invocation_counter > timer->thresdhold)){
cancel_timer(timer);
return;
}
(timer->cb)(timer, timer->user_arg);
}
/* Returns NULL in timer creation fails, else
* return a pointer to Timer object*/
Timer_t*
setup_timer(
void (*timer_cb)(Timer_t*, void *), /* Timer Callback with user data*/
unsigned long exp_timer, /* First expiration time interval in msec */
unsigned long sec_exp_timer, /* Subsequent expiration time interval in msec */
uint32_t threshold, /* Max no of expirations, 0 for infinite*/
void *user_arg){ /* Arg to timer callback */
Timer_t *timer = calloc(1, sizeof(Timer_t));
timer->posix_timer = calloc(1, sizeof(timer_t));
timer->user_arg = user_arg;
timer->exp_timer = exp_timer;
timer->sec_exp_timer = sec_exp_timer;
timer->cb = timer_cb;
timer->thresdhold = threshold;
timer->timer_state = TIMER_INIT;
/* Sanity checks */
assert(timer->cb); /* Mandatory */
struct sigevent evp;
memset(&evp, 0, sizeof(struct sigevent));
evp.sigev_value.sival_ptr = (void *)(timer);
evp.sigev_notify = SIGEV_THREAD;
evp.sigev_notify_function = timer_callback_wrapper;
int rc = timer_create (CLOCK_REALTIME,
&evp, timer->posix_timer);
assert(rc >= 0);
timer_fill_itimerspec(&timer->ts.it_value, timer->exp_timer);
timer_fill_itimerspec(&timer->ts.it_interval, timer->sec_exp_timer);
return timer;
}
void
start_timer(Timer_t *timer){
int rc;
rc = timer_settime(*(timer->posix_timer), 0, &timer->ts, NULL);
assert(rc >= 0);
timer->timer_state = TIMER_RUNNING;
}
void
delete_timer(Timer_t *timer){
int rc;
rc = timer_delete(*(timer->posix_timer));
assert(rc >= 0);
free(timer->posix_timer);
timer->posix_timer = NULL;
timer->user_arg = NULL; /* User arg need to be freed by Appln */
timer->timer_state = TIMER_DELETED;
}
void
cancel_timer(Timer_t *timer){
if(timer->timer_state == TIMER_INIT ||
timer->timer_state == TIMER_DELETED) {
return; /* No-Operation */
}
/* Only Paused or running timer can be cancelled */
timer_fill_itimerspec(&timer->ts.it_value, 0);
timer_fill_itimerspec(&timer->ts.it_interval, 0);
timer->time_remaining = 0;
start_timer(timer);
timer->timer_state = TIMER_CANCELLED;
}
void
pause_timer(Timer_t *timer){
assert(timer->timer_state == TIMER_RUNNING);
timer->time_remaining =
timer_get_time_remaining_in_mill_sec(timer);
timer_fill_itimerspec(&timer->ts.it_value, 0);
/* Do not reset the interval */
//timer_fill_itimerspec(&timer->ts.it_interval, 0);
start_timer(timer);
timer->timer_state = TIMER_PAUSED;
}
void
resume_timer(Timer_t *timer){
assert(timer->timer_state == TIMER_PAUSED);
timer_fill_itimerspec(&timer->ts.it_value, timer->time_remaining);
timer->time_remaining = 0;
start_timer(timer);
timer->timer_state = TIMER_RUNNING;
}
unsigned long
timer_get_time_remaining_in_mill_sec(Timer_t *timer){
struct itimerspec remaining_time;
switch(timer->timer_state){
case TIMER_INIT:
break;
case TIMER_DELETED:
return ~0;
case TIMER_PAUSED:
break;
case TIMER_CANCELLED:
return ~0;
case TIMER_RUNNING:
break;
default : ;
}
memset(&remaining_time, 0, sizeof(struct itimerspec));
timer_gettime(*timer->posix_timer, &remaining_time);
return timespec_to_millisec(&remaining_time.it_value);
}
void
restart_timer(Timer_t *timer){
if(timer->timer_state != TIMER_PAUSED ||
timer->timer_state != TIMER_RUNNING) {
assert(0);
}
cancel_timer(timer);
timer_fill_itimerspec(&timer->ts.it_value, timer->exp_timer);
timer_fill_itimerspec(&timer->ts.it_interval, timer->sec_exp_timer);
timer->invocation_counter = 0;
timer->time_remaining = 0;
start_timer(timer);
}
int
reschedule_timer(Timer_t *timer,
unsigned long exp_ti,
unsigned long sec_exp_ti){
if(timer->timer_state == TIMER_DELETED) assert(0);
if(timer->timer_state != TIMER_CANCELLED) {
cancel_timer(timer);
}
timer->exp_timer = exp_ti;
timer->sec_exp_timer = sec_exp_ti;
timer->invocation_counter = 0;
timer_fill_itimerspec(&timer->ts.it_value, exp_ti);
timer_fill_itimerspec(&timer->ts.it_interval, sec_exp_ti);
timer->time_remaining = 0;
start_timer(timer);
timer->timer_state = TIMER_RUNNING;
}

View File

@@ -0,0 +1,99 @@
/*
* =====================================================================================
*
* Filename: timerlib.h
*
* Description: This file is a wrapper over Timer POSIX Timer library
*
* Version: 1.0
* Created: 10/12/2020 01:47:16 PM
* Revision: none
* Compiler: gcc
*
* Author: ABHISHEK SAGAR (), sachinites@gmail.com
* Organization: Juniper Networks
*
* =====================================================================================
*/
#ifndef __TIMER_WRAP__
#define __TIMER_WRAP__
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdint.h>
typedef enum{
TIMER_INIT,
TIMER_DELETED,
TIMER_PAUSED,
TIMER_CANCELLED,
TIMER_RUNNING,
} TIMER_STATE_T;
typedef struct Timer_{
/* Timer config */
timer_t *posix_timer;
void *user_arg;
unsigned long exp_timer; /* in milli-sec */
unsigned long sec_exp_timer; /* in milli-sec */
uint32_t thresdhold; /* No of times to invoke the timer callback */
void (*cb)(struct Timer_ *, void *); /* Timer Callback */
/* place holder value to store
* dynamic attributes of timer */
unsigned long time_remaining; /* Time left for paused timer for next expiration */
uint32_t invocation_counter;
struct itimerspec ts;
TIMER_STATE_T timer_state;
} Timer_t;
/* Returns NULL in timer creation fails, else
* return a pointer to Timer object*/
Timer_t*
setup_timer(
/* Timer Callback with user data and user size*/
void (*)(Timer_t*, void *),
/* First expiration time interval in msec */
unsigned long,
/* Subsequent expiration time interval in msec */
unsigned long,
/* Max no of expirations, 0 for infinite*/
uint32_t,
/* Arg to timer callback */
void *);
void
start_timer(Timer_t *timer);
void
delete_timer(Timer_t *timer);
void
cancel_timer(Timer_t *timer);
void
pause_timer(Timer_t *timer);
void
resume_timer(Timer_t *timer);
int
execute_timer(Timer_t *timer, TIMER_STATE_T action);
/* get remaining time in msec */
unsigned long
timer_get_time_remaining_in_mill_sec(Timer_t *timer);
void
restart_timer(Timer_t *timer);
int
reschedule_timer(Timer_t *timer,
unsigned long exp_ti,
unsigned long sec_exp_ti);
#endif /* __TIMER_WRAP__ */