mirror of
https://github.com/Hizenberg469/Timer-Library.git
synced 2026-04-19 17:52:26 +03:00
Timer Library Complete
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
*vsidx
|
||||||
|
*.opendb
|
||||||
|
.vs
|
||||||
|
out
|
||||||
|
CMakePresets.json
|
||||||
|
|
||||||
|
|
||||||
13
Assignment/CMakeLists.txt
Normal file
13
Assignment/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
set(RT_SRC "rt.c")
|
||||||
|
set(RT_MAIN "rt_entry_expiration.c")
|
||||||
|
|
||||||
|
set(RT_EXE rtManager)
|
||||||
|
|
||||||
|
add_executable(${RT_EXE} ${RT_SRC}
|
||||||
|
${RT_MAIN})
|
||||||
|
|
||||||
|
target_include_directories(${RT_EXE} PUBLIC
|
||||||
|
${HEADER_DIR})
|
||||||
|
|
||||||
|
target_link_libraries(${RT_EXE} PUBLIC
|
||||||
|
${TIMER_LIB})
|
||||||
117
Assignment/rt.c
Normal file
117
Assignment/rt.c
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include "rt.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
rt_init_rt_table(rt_table_t* rt_table) {
|
||||||
|
|
||||||
|
rt_table->head = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
rt_add_new_rt_entry(rt_table_t* rt_table,
|
||||||
|
char* dest,
|
||||||
|
char mask,
|
||||||
|
char* gw_ip,
|
||||||
|
char* oif,
|
||||||
|
void (*timer_cb)(Timer_t* , void* )) {
|
||||||
|
|
||||||
|
rt_entry_t* head = NULL;
|
||||||
|
rt_entry_t* rt_entry = NULL;
|
||||||
|
|
||||||
|
rt_entry = calloc(1, sizeof(rt_entry_t));
|
||||||
|
|
||||||
|
if (!rt_entry)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
strncpy(rt_entry->rt_entry_keys.dest, dest, sizeof(rt_entry->rt_entry_keys.dest));
|
||||||
|
rt_entry->rt_entry_keys.mask = mask;
|
||||||
|
|
||||||
|
if (gw_ip)
|
||||||
|
strncpy(rt_entry->gw_ip, gw_ip, sizeof(rt_entry->gw_ip));
|
||||||
|
if (oif)
|
||||||
|
strncpy(rt_entry->oif, oif, sizeof(rt_entry->oif));
|
||||||
|
|
||||||
|
rt_entry->time_to_expire = RT_TABLE_EXP_TIME;
|
||||||
|
|
||||||
|
rt_entry->timer = setup_timer((*timer_cb), RT_TABLE_EXP_TIME*1000, 0, 0, (void*)rt_entry, false);
|
||||||
|
|
||||||
|
head = rt_table->head;
|
||||||
|
rt_table->head = rt_entry;
|
||||||
|
rt_entry->prev = 0;
|
||||||
|
rt_entry->next = head;
|
||||||
|
if (head)
|
||||||
|
head->prev = rt_entry;
|
||||||
|
|
||||||
|
start_timer(rt_entry->timer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
rt_delete_rt_entry(rt_table_t* rt_table,
|
||||||
|
char* dest, char mask) {
|
||||||
|
|
||||||
|
rt_entry_t* rt_entry = NULL;
|
||||||
|
|
||||||
|
ITERTAE_RT_TABLE_BEGIN(rt_table, rt_entry) {
|
||||||
|
|
||||||
|
if (strncmp(rt_entry->rt_entry_keys.dest,
|
||||||
|
dest, sizeof(rt_entry->rt_entry_keys.dest)) == 0 &&
|
||||||
|
rt_entry->rt_entry_keys.mask == mask) {
|
||||||
|
|
||||||
|
rt_entry_remove(rt_table, rt_entry);
|
||||||
|
|
||||||
|
printf("deleting rt entry %p [%s:%d]\n",
|
||||||
|
rt_entry, rt_entry->rt_entry_keys.dest,
|
||||||
|
rt_entry->rt_entry_keys.mask);
|
||||||
|
|
||||||
|
free(rt_entry);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} ITERTAE_RT_TABLE_END(rt_table, curr);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
rt_update_rt_entry(rt_table_t* rt_table,
|
||||||
|
char* dest,
|
||||||
|
char mask,
|
||||||
|
char* new_gw_ip,
|
||||||
|
char* new_oif) {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rt_clear_rt_table(rt_table_t* rt_table) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rt_free_rt_table(rt_table_t* rt_table) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rt_dump_rt_table(rt_table_t* rt_table) {
|
||||||
|
|
||||||
|
rt_entry_t* rt_entry = NULL;
|
||||||
|
|
||||||
|
ITERTAE_RT_TABLE_BEGIN(rt_table, rt_entry) {
|
||||||
|
|
||||||
|
unsigned long time_in_milli_sec = timer_get_time_remaining_in_milli_sec(rt_entry->timer);
|
||||||
|
|
||||||
|
printf("%-20s %-4d %-20s %-12s %usec (%lu)\n",
|
||||||
|
rt_entry->rt_entry_keys.dest,
|
||||||
|
rt_entry->rt_entry_keys.mask,
|
||||||
|
rt_entry->gw_ip,
|
||||||
|
rt_entry->oif,
|
||||||
|
rt_entry->time_to_expire,
|
||||||
|
time_in_milli_sec);
|
||||||
|
} ITERTAE_RT_TABLE_END(rt_tabl, rt_entry);
|
||||||
|
}
|
||||||
97
Assignment/rt.h
Normal file
97
Assignment/rt.h
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#ifndef __RT__
|
||||||
|
#define __RT__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <timerlib.h>
|
||||||
|
|
||||||
|
#define RT_TABLE_EXP_TIME 30 /* 30 sec */
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct rt_entry_keys_ {
|
||||||
|
|
||||||
|
char dest[16];
|
||||||
|
char mask;
|
||||||
|
} rt_entry_keys_t;
|
||||||
|
|
||||||
|
typedef struct rt_entry_ {
|
||||||
|
|
||||||
|
/* A Structure which represents only the keys of the
|
||||||
|
* Routing Table.*/
|
||||||
|
rt_entry_keys_t rt_entry_keys;
|
||||||
|
|
||||||
|
char gw_ip[16];
|
||||||
|
char oif[32];
|
||||||
|
uint32_t time_to_expire; /* time left to delete the entry */
|
||||||
|
Timer_t* timer;
|
||||||
|
struct rt_entry_* prev;
|
||||||
|
struct rt_entry_* next;
|
||||||
|
} rt_entry_t;
|
||||||
|
|
||||||
|
typedef struct rt_table_ {
|
||||||
|
|
||||||
|
rt_entry_t* head;
|
||||||
|
} rt_table_t;
|
||||||
|
|
||||||
|
void
|
||||||
|
rt_init_rt_table(rt_table_t* rt_table);
|
||||||
|
|
||||||
|
bool
|
||||||
|
rt_add_new_rt_entry(rt_table_t* rt_table,
|
||||||
|
char* dest_ip, char mask, char* gw_ip, char* oif, void (*timer_cb)(Timer_t*, void*));
|
||||||
|
|
||||||
|
bool
|
||||||
|
rt_delete_rt_entry(rt_table_t* rt_table,
|
||||||
|
char* dest_ip, char mask);
|
||||||
|
|
||||||
|
bool
|
||||||
|
rt_update_rt_entry(rt_table_t* rt_table,
|
||||||
|
char* dest_ip, char mask,
|
||||||
|
char* new_gw_ip, char* new_oif);
|
||||||
|
|
||||||
|
void
|
||||||
|
rt_clear_rt_table(rt_table_t* rt_table);
|
||||||
|
|
||||||
|
void
|
||||||
|
rt_free_rt_table(rt_table_t* rt_table);
|
||||||
|
|
||||||
|
void
|
||||||
|
rt_dump_rt_table(rt_table_t* rt_table);
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
rt_entry_remove(rt_table_t* rt_table,
|
||||||
|
rt_entry_t* rt_entry) {
|
||||||
|
|
||||||
|
if (!rt_entry->prev) {
|
||||||
|
if (rt_entry->next) {
|
||||||
|
rt_entry->next->prev = 0;
|
||||||
|
rt_table->head = rt_entry->next;
|
||||||
|
rt_entry->next = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rt_table->head = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!rt_entry->next) {
|
||||||
|
rt_entry->prev->next = 0;
|
||||||
|
rt_entry->prev = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rt_entry->prev->next = rt_entry->next;
|
||||||
|
rt_entry->next->prev = rt_entry->prev;
|
||||||
|
rt_entry->prev = 0;
|
||||||
|
rt_entry->next = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ITERTAE_RT_TABLE_BEGIN(rt_table_ptr, rt_entry_ptr) \
|
||||||
|
{ \
|
||||||
|
rt_entry_t *_next_rt_entry; \
|
||||||
|
for((rt_entry_ptr) = (rt_table_ptr)->head; \
|
||||||
|
(rt_entry_ptr); \
|
||||||
|
(rt_entry_ptr) = _next_rt_entry) { \
|
||||||
|
_next_rt_entry = (rt_entry_ptr)->next;
|
||||||
|
|
||||||
|
#define ITERTAE_RT_TABLE_END(rt_table_ptr, rt_entry_ptr) }}
|
||||||
|
|
||||||
|
#endif /* __RT__ */
|
||||||
80
Assignment/rt_entry_expiration.c
Normal file
80
Assignment/rt_entry_expiration.c
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <timerlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "rt.h"
|
||||||
|
|
||||||
|
static rt_table_t rt;
|
||||||
|
|
||||||
|
void rt_entry_delete_on_timer_expiry(Timer_t* timer, void* user_data) {
|
||||||
|
|
||||||
|
assert(timer != NULL);
|
||||||
|
assert(user_data != NULL);
|
||||||
|
|
||||||
|
rt_entry_t* rt_entry = (rt_entry_t*)user_data;
|
||||||
|
|
||||||
|
char dest_ip[16];
|
||||||
|
strncpy(dest_ip,rt_entry->rt_entry_keys.dest, sizeof(rt_entry->rt_entry_keys.dest));
|
||||||
|
char mask = rt_entry->rt_entry_keys.mask;
|
||||||
|
delete_timer(rt_entry->timer);
|
||||||
|
|
||||||
|
assert(rt_delete_rt_entry(&rt, dest_ip, mask));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char** argv) {
|
||||||
|
|
||||||
|
rt_init_rt_table(&rt);
|
||||||
|
|
||||||
|
rt_add_new_rt_entry(&rt, "122.1.1.1", 32, "10.1.1.1", "eth0",rt_entry_delete_on_timer_expiry);
|
||||||
|
rt_add_new_rt_entry(&rt, "122.1.1.2", 32, "10.1.1.2", "eth1",rt_entry_delete_on_timer_expiry);
|
||||||
|
rt_add_new_rt_entry(&rt, "122.1.1.3", 32, "10.1.1.3", "eth2",rt_entry_delete_on_timer_expiry);
|
||||||
|
rt_add_new_rt_entry(&rt, "122.1.1.4", 32, "10.1.1.4", "eth3",rt_entry_delete_on_timer_expiry);
|
||||||
|
rt_add_new_rt_entry(&rt, "122.1.1.5", 32, "10.1.1.5", "eth4",rt_entry_delete_on_timer_expiry);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
printf("1. Add rt table entry\n");
|
||||||
|
printf("2. Update rt table entry\n");
|
||||||
|
printf("3. Delete rt table entry\n");
|
||||||
|
printf("4. Dump rt table\n");
|
||||||
|
|
||||||
|
int choice;
|
||||||
|
printf("Enter Choice :");
|
||||||
|
scanf("%d", &choice);
|
||||||
|
fflush(stdin);
|
||||||
|
switch (choice) {
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
char dest[16];
|
||||||
|
uint8_t mask;
|
||||||
|
char oif[32];
|
||||||
|
char gw[16];
|
||||||
|
printf("Enter Destination :");
|
||||||
|
scanf("%s", dest);
|
||||||
|
printf("Mask : ");
|
||||||
|
scanf("%hhd", &mask);
|
||||||
|
printf("Enter oif name :");
|
||||||
|
scanf("%s", oif);
|
||||||
|
printf("Enter Gateway IP :");
|
||||||
|
scanf("%s", gw);
|
||||||
|
if (!rt_add_new_rt_entry(&rt, dest, mask,
|
||||||
|
gw, oif, rt_entry_delete_on_timer_expiry)) {
|
||||||
|
printf("Error : Could not add an entry\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
rt_dump_rt_table(&rt);
|
||||||
|
default:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
35
CMakeLists.txt
Normal file
35
CMakeLists.txt
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.22.1)
|
||||||
|
|
||||||
|
project(Timer-Library VERSION 1.0.0 LANGUAGES C CXX)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 17)
|
||||||
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_C_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
|
||||||
|
#Output file structure
|
||||||
|
|
||||||
|
#Scheme 1
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$<CONFIG>")
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/$<CONFIG>")
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/$<CONFIG>")
|
||||||
|
|
||||||
|
#Scheme 2
|
||||||
|
#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$<CONFIG>/bin")
|
||||||
|
#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$<CONFIG>/lib")
|
||||||
|
#set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$<CONFIG>/lib")
|
||||||
|
|
||||||
|
#Library Name...
|
||||||
|
set(TIMER_LIB timer)
|
||||||
|
|
||||||
|
|
||||||
|
#Header file directory...
|
||||||
|
set(HEADER_DIR ${CMAKE_SOURCE_DIR}/include)
|
||||||
|
|
||||||
|
#Source file directory...
|
||||||
|
set(SOURCE_DIR ${CMAKE_SOURCE_DIR}/src)
|
||||||
|
|
||||||
|
#Adding Sub-directory having CMakeLists.txt...
|
||||||
|
add_subdirectory(src)
|
||||||
|
add_subdirectory(test)
|
||||||
|
add_subdirectory(Assignment)
|
||||||
174
include/timerlib.h
Normal file
174
include/timerlib.h
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
#ifndef __TIMER_WRAP__
|
||||||
|
#define __TIMER_WRAP__
|
||||||
|
|
||||||
|
//To use POSIX TIMER library
|
||||||
|
#define __USE_POSIX199309 1
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Timer Data Structure, with attributes
|
||||||
|
* for controlling timer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
|
||||||
|
TIMER_INIT,
|
||||||
|
TIMER_RUNNING,
|
||||||
|
TIMER_CANCELLED,
|
||||||
|
TIMER_DELETED,
|
||||||
|
TIMER_PAUSED,
|
||||||
|
TIMER_RESUMED
|
||||||
|
|
||||||
|
} TIMER_STATE_T;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct Timer_ {
|
||||||
|
|
||||||
|
/* Timer config */
|
||||||
|
timer_t* posix_timer; //Handle to Posix timer
|
||||||
|
void* user_arg; //Arg to passed to timer callback fun.
|
||||||
|
unsigned long exp_time; //expiration or fire time after timer start
|
||||||
|
unsigned long sec_exp_time; //expiration or fire time after first timer exp., used in periodic timer
|
||||||
|
uint32_t threshold; //No. of times to invoke the timer callback
|
||||||
|
void (*cb)(struct Timer_*, void*); //Function-pointer to timer callback
|
||||||
|
bool exponential_backoff; //If the timer is exponential type
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dynamic Attributes of timer data structure:
|
||||||
|
*
|
||||||
|
* Attributes which change dynamically with exec. of
|
||||||
|
* program.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned long time_remaining; //Time left for paused timer for next expiration/launch
|
||||||
|
uint32_t invocation_counter; //No. of times callback is invoked( Useful in periodic/exponential type)
|
||||||
|
struct itimerspec ts;
|
||||||
|
unsigned long exp_back_off_time;
|
||||||
|
TIMER_STATE_T timer_state; //State of the Timer object
|
||||||
|
|
||||||
|
}Timer_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assignment-1 functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
timespec_to_millisec(struct timespec* time);
|
||||||
|
|
||||||
|
void
|
||||||
|
timer_fill_itimerspec(struct timespec* ts,
|
||||||
|
unsigned long msec);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assignment-1 functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assignment-2 functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
timer_delete_user_data(Timer_t* timer) {
|
||||||
|
|
||||||
|
if (timer->user_arg) {
|
||||||
|
free(timer->user_arg);
|
||||||
|
timer->user_arg = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline TIMER_STATE_T
|
||||||
|
timer_get_current_state(Timer_t* timer) {
|
||||||
|
|
||||||
|
return timer->timer_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
timer_set_state(Timer_t* timer, TIMER_STATE_T timer_state) {
|
||||||
|
|
||||||
|
timer->timer_state = timer_state;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assignment-2 functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*Return 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 timer interval in msec*/
|
||||||
|
unsigned long,
|
||||||
|
/*Subsequent expiration time interval in msec*/
|
||||||
|
unsigned long,
|
||||||
|
/*Max no of expiration, 0 for infinite */
|
||||||
|
uint32_t,
|
||||||
|
/* Arg to timer callback */
|
||||||
|
void*,
|
||||||
|
/* Is timer Exp back off */
|
||||||
|
bool
|
||||||
|
);
|
||||||
|
|
||||||
|
/*****APIs' to control timer******/
|
||||||
|
|
||||||
|
void
|
||||||
|
resurrect_timer(Timer_t* timer);
|
||||||
|
|
||||||
|
void
|
||||||
|
start_timer(Timer_t* timer);
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
timer_get_time_remaining_in_milli_sec(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);
|
||||||
|
|
||||||
|
void
|
||||||
|
delete_timer(Timer_t* timer);
|
||||||
|
|
||||||
|
void
|
||||||
|
cancel_timer(Timer_t* timer);
|
||||||
|
|
||||||
|
void
|
||||||
|
restart_timer(Timer_t* timer);
|
||||||
|
|
||||||
|
void
|
||||||
|
reschedule_timer(Timer_t* timer,
|
||||||
|
unsigned long exp_ti,
|
||||||
|
unsigned long sec_exp_ti);
|
||||||
|
|
||||||
|
void
|
||||||
|
print_timer(Timer_t* timer);
|
||||||
|
|
||||||
|
bool
|
||||||
|
is_timer_running(Timer_t* timer);
|
||||||
|
|
||||||
|
|
||||||
|
/*****APIs' to control timer******/
|
||||||
|
|
||||||
|
#endif
|
||||||
23
src/CMakeLists.txt
Normal file
23
src/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#timerlib.c src file...
|
||||||
|
set(TIMER_LIB_SRC "timerlib.c")
|
||||||
|
|
||||||
|
#Library
|
||||||
|
add_library(${TIMER_LIB} STATIC
|
||||||
|
${TIMER_LIB_SRC})
|
||||||
|
|
||||||
|
#Including Header file dir...
|
||||||
|
target_include_directories(${TIMER_LIB} PUBLIC
|
||||||
|
${HEADER_DIR})
|
||||||
|
|
||||||
|
#Linking dependent library...
|
||||||
|
|
||||||
|
if( NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux") )
|
||||||
|
message(FATAL_ERROR "It's not a Unix-based system.\n \
|
||||||
|
POSIX TIMER Library will not compile in this project.\n")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(POSIX_TIMER_LIB rt)
|
||||||
|
|
||||||
|
|
||||||
|
target_link_libraries(${TIMER_LIB} PUBLIC
|
||||||
|
${POSIX_TIMER_LIB})
|
||||||
334
src/timerlib.c
Normal file
334
src/timerlib.c
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "timerlib.h"
|
||||||
|
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
timespec_to_millisec(struct timespec* time) {
|
||||||
|
|
||||||
|
unsigned long res_time = 0;
|
||||||
|
|
||||||
|
res_time += (unsigned long)time->tv_sec * 1000; // For seconds to be in millisecond.
|
||||||
|
|
||||||
|
res_time += (unsigned long)time->tv_nsec / 1000000; // For nanosecond to millisecond.
|
||||||
|
|
||||||
|
return res_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
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->threshold &&
|
||||||
|
(timer->invocation_counter > timer->threshold)) {
|
||||||
|
cancel_timer(timer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timer->timer_state == TIMER_RESUMED) {
|
||||||
|
if (timer->sec_exp_time != 0) {
|
||||||
|
timer->timer_state = TIMER_RUNNING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(timer->cb)(timer, timer->user_arg);
|
||||||
|
|
||||||
|
if (timer->exponential_backoff) {
|
||||||
|
|
||||||
|
assert(timer->exp_back_off_time);
|
||||||
|
reschedule_timer(timer,
|
||||||
|
timer->exp_back_off_time *= 2, 0);
|
||||||
|
}
|
||||||
|
else if (timer->timer_state == TIMER_RESUMED) {
|
||||||
|
|
||||||
|
reschedule_timer(timer,
|
||||||
|
timer->exp_time, timer->sec_exp_time);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Return 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_cb)(Timer_t*, void*),
|
||||||
|
/* First expiration timer interval in msec*/
|
||||||
|
unsigned long exp_timer,
|
||||||
|
/*Subsequent expiration time interval in msec*/
|
||||||
|
unsigned long sec_exp_timer,
|
||||||
|
/*Max no of expiration, 0 for infinite */
|
||||||
|
uint32_t threshold,
|
||||||
|
/* Arg to timer callback */
|
||||||
|
void* user_arg,
|
||||||
|
/* Is timer Exp back off */
|
||||||
|
bool exponential_backoff) {
|
||||||
|
|
||||||
|
Timer_t* timer = calloc(1, sizeof(Timer_t));
|
||||||
|
|
||||||
|
timer->posix_timer = calloc(1, sizeof(timer_t));
|
||||||
|
|
||||||
|
timer->user_arg = user_arg;
|
||||||
|
timer->exp_time = exp_timer;
|
||||||
|
timer->sec_exp_time = sec_exp_timer;
|
||||||
|
timer->cb = timer_cb;
|
||||||
|
timer->threshold = threshold;
|
||||||
|
timer_set_state(timer, TIMER_INIT);
|
||||||
|
timer->exponential_backoff = exponential_backoff;
|
||||||
|
|
||||||
|
/* Check for error */
|
||||||
|
assert(timer->cb);
|
||||||
|
|
||||||
|
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_time);
|
||||||
|
|
||||||
|
if (!timer->exponential_backoff) {
|
||||||
|
timer_fill_itimerspec(&timer->ts.it_interval, timer->sec_exp_time);
|
||||||
|
timer->exp_back_off_time = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timer->exp_back_off_time = timespec_to_millisec(&timer->ts.it_value);
|
||||||
|
timer_fill_itimerspec(&timer->ts.it_interval, 0);
|
||||||
|
}
|
||||||
|
return timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****APIs' to control timer******/
|
||||||
|
|
||||||
|
void
|
||||||
|
resurrect_timer(Timer_t* timer) {
|
||||||
|
|
||||||
|
int rc;
|
||||||
|
rc = timer_settime(*(timer->posix_timer), 0, &timer->ts, NULL);
|
||||||
|
assert(rc >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
start_timer(Timer_t* timer) {
|
||||||
|
|
||||||
|
resurrect_timer(timer);
|
||||||
|
timer_set_state(timer, TIMER_RUNNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
delete_timer(Timer_t* timer) {
|
||||||
|
|
||||||
|
int rc;
|
||||||
|
rc = timer_delete(*(timer->posix_timer));
|
||||||
|
assert(rc >= 0);
|
||||||
|
timer->user_arg = NULL; /* User arg need to be freed by Appln */
|
||||||
|
timer_set_state(timer, TIMER_DELETED);
|
||||||
|
free(timer->posix_timer);
|
||||||
|
free(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cancel_timer(Timer_t* timer) {
|
||||||
|
|
||||||
|
TIMER_STATE_T timer_curr_state;
|
||||||
|
|
||||||
|
timer_curr_state = timer_get_current_state(timer);
|
||||||
|
|
||||||
|
if (timer_curr_state = TIMER_INIT ||
|
||||||
|
timer_curr_state == TIMER_DELETED) {
|
||||||
|
|
||||||
|
return; /* No operations */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
timer->invocation_counter = 0;
|
||||||
|
resurrect_timer(timer);
|
||||||
|
timer_set_state(timer, TIMER_CANCELLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pause_timer(Timer_t* timer) {
|
||||||
|
|
||||||
|
if (timer_get_current_state(timer) == TIMER_PAUSED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer->time_remaining =
|
||||||
|
timer_get_time_remaining_in_milli_sec(timer);
|
||||||
|
|
||||||
|
timer_fill_itimerspec(&timer->ts.it_value, 0);
|
||||||
|
timer_fill_itimerspec(&timer->ts.it_interval, 0);
|
||||||
|
|
||||||
|
resurrect_timer(timer);
|
||||||
|
|
||||||
|
timer_set_state(timer, TIMER_PAUSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
resume_timer(Timer_t* timer) {
|
||||||
|
|
||||||
|
assert(timer_get_current_state(timer) == TIMER_PAUSED);
|
||||||
|
|
||||||
|
timer_fill_itimerspec(&timer->ts.it_value, timer->time_remaining);
|
||||||
|
timer_fill_itimerspec(&timer->ts.it_interval, timer->sec_exp_time);
|
||||||
|
|
||||||
|
timer->time_remaining = 0;
|
||||||
|
|
||||||
|
resurrect_timer(timer);
|
||||||
|
timer_set_state(timer, TIMER_RESUMED);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
timer_get_time_remaining_in_milli_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) {
|
||||||
|
|
||||||
|
assert(timer->timer_state != TIMER_DELETED);
|
||||||
|
|
||||||
|
cancel_timer(timer);
|
||||||
|
|
||||||
|
timer_fill_itimerspec(&timer->ts.it_value,
|
||||||
|
timer->exp_time);
|
||||||
|
|
||||||
|
if (!timer->exponential_backoff) {
|
||||||
|
timer_fill_itimerspec(&timer->ts.it_interval, timer->sec_exp_time);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timer_fill_itimerspec(&timer->ts.it_interval, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
timer->invocation_counter = 0;
|
||||||
|
timer->time_remaining = 0;
|
||||||
|
timer->exp_back_off_time = timer->exp_time;
|
||||||
|
resurrect_timer(timer);
|
||||||
|
timer_set_state(timer, TIMER_RUNNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
reschedule_timer(Timer_t* timer,
|
||||||
|
unsigned long exp_ti,
|
||||||
|
unsigned long sec_exp_ti) {
|
||||||
|
|
||||||
|
uint32_t invocation_counter;
|
||||||
|
TIMER_STATE_T timer_state;
|
||||||
|
|
||||||
|
timer_state = timer_get_current_state(timer);
|
||||||
|
|
||||||
|
if (timer_state == TIMER_DELETED) assert(0);
|
||||||
|
|
||||||
|
invocation_counter = timer->invocation_counter;
|
||||||
|
|
||||||
|
if (timer_state != TIMER_CANCELLED) {
|
||||||
|
cancel_timer(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
timer->invocation_counter = invocation_counter;
|
||||||
|
timer_fill_itimerspec(&timer->ts.it_value, exp_ti);
|
||||||
|
|
||||||
|
if (!timer->exponential_backoff) {
|
||||||
|
timer_fill_itimerspec(&timer->ts.it_interval, sec_exp_ti);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timer_fill_itimerspec(&timer->ts.it_interval, 0);
|
||||||
|
timer->exp_back_off_time = exp_ti;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer->time_remaining = 0;
|
||||||
|
resurrect_timer(timer);
|
||||||
|
timer_set_state(timer, TIMER_RUNNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
print_timer(Timer_t* timer) {
|
||||||
|
|
||||||
|
printf("Counter = %u, time remaining = %lu, state = %d\n",
|
||||||
|
timer->invocation_counter,
|
||||||
|
timer_get_time_remaining_in_milli_sec(timer),
|
||||||
|
timer_get_current_state(timer));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
is_timer_running(Timer_t* timer) {
|
||||||
|
|
||||||
|
TIMER_STATE_T timer_state;
|
||||||
|
|
||||||
|
timer_state = timer_get_current_state(timer);
|
||||||
|
|
||||||
|
if (timer_state == TIMER_RUNNING ||
|
||||||
|
timer_state == TIMER_RESUMED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*****APIs' to control timer******/
|
||||||
12
test/CMakeLists.txt
Normal file
12
test/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
set(TIMERLIB_TEST_EXE timerlib_test)
|
||||||
|
|
||||||
|
set(TIMERLIB_TEST_SRC "timerlib_test.c")
|
||||||
|
|
||||||
|
add_executable(${TIMERLIB_TEST_EXE}
|
||||||
|
${TIMERLIB_TEST_SRC})
|
||||||
|
|
||||||
|
target_link_libraries(${TIMERLIB_TEST_EXE} PUBLIC
|
||||||
|
${TIMER_LIB})
|
||||||
|
|
||||||
|
target_include_directories(${TIMERLIB_TEST_EXE} PUBLIC
|
||||||
|
${HEADER_DIR})
|
||||||
66
test/timerlib_test.c
Normal file
66
test/timerlib_test.c
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "timerlib.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
user_defined_app_cb(Timer_t* timer, void* user_data) {
|
||||||
|
|
||||||
|
printf("User data = %s\n", (char*)user_data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char* argv[]) {
|
||||||
|
|
||||||
|
char* name = "Junet";
|
||||||
|
|
||||||
|
Timer_t* timer = setup_timer(user_defined_app_cb, 1000, 1000, 0, name, false);
|
||||||
|
start_timer(timer);
|
||||||
|
|
||||||
|
printf("1. Pause Timer\n");
|
||||||
|
printf("2. Resume Timer\n");
|
||||||
|
printf("3. Restart timer\n");
|
||||||
|
printf("4. Reschedule timer\n");
|
||||||
|
printf("5. Delete timer\n");
|
||||||
|
printf("6. Cancel Timer\n");
|
||||||
|
printf("7. Get Remaining Time\n");
|
||||||
|
printf("8. Print Timer State\n");
|
||||||
|
|
||||||
|
int choice;
|
||||||
|
choice = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
scanf("%d", &choice);
|
||||||
|
|
||||||
|
switch (choice) {
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
//pause_timer(timer);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
//resume_timer(timer);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
//restart_timer(timer);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
/*reschedule_timer(timer,
|
||||||
|
timer->exp_time,
|
||||||
|
timer->sec_exp_time);*/
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
//delete_timer(timer);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
//cancel_timer(timer);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
//printf("Rem Time = %lu\n", timer_get_time_remaining_in_mill_sec(timer));
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
//print_timer(timer);
|
||||||
|
break;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user