From bb256bf9c8c1869fafb21587ddb507fc20adc543 Mon Sep 17 00:00:00 2001 From: Abhishek Sagar Date: Tue, 13 Oct 2020 00:43:26 -0700 Subject: [PATCH] Added Timer Library --- WheelTimer/libtimer/timerExample.c | 213 +++++++++++++++++++++++++ WheelTimer/libtimer/timerlib.c | 246 +++++++++++++++++++++++++++++ WheelTimer/libtimer/timerlib.h | 99 ++++++++++++ 3 files changed, 558 insertions(+) create mode 100644 WheelTimer/libtimer/timerExample.c create mode 100644 WheelTimer/libtimer/timerlib.c create mode 100644 WheelTimer/libtimer/timerlib.h diff --git a/WheelTimer/libtimer/timerExample.c b/WheelTimer/libtimer/timerExample.c new file mode 100644 index 0000000..25e143c --- /dev/null +++ b/WheelTimer/libtimer/timerExample.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 (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; +} diff --git a/WheelTimer/libtimer/timerlib.c b/WheelTimer/libtimer/timerlib.c new file mode 100644 index 0000000..4d782b3 --- /dev/null +++ b/WheelTimer/libtimer/timerlib.c @@ -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 +#include +#include +#include +#include +#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; +} + diff --git a/WheelTimer/libtimer/timerlib.h b/WheelTimer/libtimer/timerlib.h new file mode 100644 index 0000000..029fa46 --- /dev/null +++ b/WheelTimer/libtimer/timerlib.h @@ -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 +#include +#include +#include +#include + +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__ */