mirror of
https://github.com/Hizenberg469/Timer-Library.git
synced 2026-04-19 17:52:26 +03:00
Wheel Timer Implemented
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include <stdint.h>
|
||||
#include <timerlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "rt.h"
|
||||
|
||||
static rt_table_t rt;
|
||||
|
||||
@@ -21,6 +21,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/$<CONFIG>")
|
||||
|
||||
#Library Name...
|
||||
set(TIMER_LIB timer)
|
||||
set(WHEELTIMER_LIB wheeltimer)
|
||||
|
||||
|
||||
#Header file directory...
|
||||
|
||||
@@ -1,20 +1,148 @@
|
||||
#ifndef __WHEEL_TIMER_H
|
||||
#define __WHEEL_TIMER_H
|
||||
#ifndef __WHEEL_TIMER__
|
||||
#define __WHEEL_TIMER__
|
||||
|
||||
|
||||
//To use POSIX TIMER library
|
||||
#define __USE_POSIX199309 1
|
||||
|
||||
#include <pthread.h>
|
||||
#include <timerlib.h>
|
||||
#include <glthread.h>
|
||||
|
||||
typdedef struct _wheel_timer_t {
|
||||
typedef struct _wheel_timer_elem_t wheel_timer_elem_t;
|
||||
typedef void (*app_call_back)(void* arg, int sizeof_arg);
|
||||
|
||||
int current_clock_tic; /*Pointer to current slot, pointed by clock tic*/
|
||||
int clock_tic_interval; /*Time interval of each clock tick*/
|
||||
int wheel_size; /*No. of slots in wheel timer*/
|
||||
int current_cycle_no; /*No. of rotation completed by wheel timer clock tic*/
|
||||
pthread_t wheel_thread; /*Thread where the wheel timer clock run separately*/
|
||||
/*@@@@@@@@@@@@*/
|
||||
ll_t* slots[0]; /*Array of linked list, where each index in array represent a slot*/
|
||||
/*@@@@@@@@@@@@*/
|
||||
typedef struct slotlist_ {
|
||||
glthread_t slots;
|
||||
pthread_mutex_t slot_mutex;
|
||||
}slotlist_t;
|
||||
|
||||
typedef enum {
|
||||
|
||||
WTELEM_CREATE,
|
||||
WTELEM_RESCHED,
|
||||
WTELEM_DELETE,
|
||||
WTELEM_SCHEDULED,
|
||||
WTELEM_UNKNOWN
|
||||
} wt_opcode_t;
|
||||
|
||||
struct _wheel_timer_elem_t {
|
||||
|
||||
wt_opcode_t opcode;
|
||||
int time_interval;
|
||||
int new_time_interval;
|
||||
int execute_cycle_no;
|
||||
int slot_no;
|
||||
app_call_back app_callback;
|
||||
void* arg;
|
||||
int arg_size;
|
||||
char is_recurrence;
|
||||
glthread_t glue;
|
||||
slotlist_t* slotlist_head;
|
||||
glthread_t reschedule_glue;
|
||||
unsigned int N_scheduled;
|
||||
};
|
||||
GLTHREAD_TO_STRUCT(glthread_to_wt_elem, wheel_timer_elem_t, glue);
|
||||
GLTHREAD_TO_STRUCT(glthread_reschedule_glue_to_wt_elem, wheel_timer_elem_t, reschedule_glue);
|
||||
|
||||
typedef struct _wheel_timer_t {
|
||||
int current_clock_tic;
|
||||
int clock_tic_interval;
|
||||
int wheel_size;
|
||||
int current_cycle_no;
|
||||
Timer_t* wheel_thread;
|
||||
slotlist_t reschd_list;
|
||||
unsigned int no_of_wt_elem;
|
||||
slotlist_t slotlist[0];
|
||||
} wheel_timer_t;
|
||||
|
||||
#define WT_UPTIME(wt_ptr) \
|
||||
(GET_WT_CURRENT_ABS_SLOT_NO(wt_ptr) * wt_ptr->clock_tic_interval)
|
||||
|
||||
#define WT_SLOTLIST(wt_ptr, index) \
|
||||
(&(wt_ptr->slotlist[index]))
|
||||
|
||||
#define WT_SLOTLIST_HEAD(wt_ptr, index) \
|
||||
(&(wt_ptr->slotlist[index].slots))
|
||||
|
||||
#define WT_SLOTLIST_MUTEX(wt_ptr, index) \
|
||||
(&(wt_ptr->slotlist[index].slot_mutex))
|
||||
|
||||
#define GET_WT_ELEM_SLOT_LIST(wt_elem_ptr) \
|
||||
(wt_elem_ptr->slotlist_head)
|
||||
|
||||
#define WT_LOCK_SLOT_LIST(slotlist_ptr) \
|
||||
pthread_mutex_lock(&(slotlist_ptr->slot_mutex))
|
||||
|
||||
#define WT_UNLOCK_SLOT_LIST(slotlist_ptr) \
|
||||
pthread_mutex_unlock(&(slotlist_ptr->slot_mutex))
|
||||
|
||||
#define WT_LOCK_WTELEM_SLOT_LIST(wt_elem_ptr) \
|
||||
{ \
|
||||
slotlist_t *_slotlist = GET_WT_ELEM_SLOT_LIST(wt_elem_ptr); \
|
||||
if(_slotlist) \
|
||||
WT_LOCK_SLOT_LIST(_slotlist); \
|
||||
}
|
||||
|
||||
#define WT_UNLOCK_WTELEM_SLOT_LIST(wt_elem_ptr) \
|
||||
{ \
|
||||
slotlist_t *_slotlist = GET_WT_ELEM_SLOT_LIST(wt_elem_ptr); \
|
||||
if(_slotlist) \
|
||||
WT_UNLOCK_SLOT_LIST(_slotlist); \
|
||||
}
|
||||
|
||||
#define WT_IS_SLOTLIST_EMPTY(slotlist_ptr) \
|
||||
IS_GLTHREAD_LIST_EMPTY(&(slotlist_ptr->slots))
|
||||
|
||||
#define WT_GET_RESCHD_SLOTLIST(wt_ptr) \
|
||||
(&(wt_ptr->reschd_list))
|
||||
|
||||
#define WT_GET_RESCHD_SLOTLIST_HEAD(wt_ptr) \
|
||||
(&((WT_GET_RESCHD_SLOTLIST(wt_ptr))->slots))
|
||||
|
||||
wheel_timer_t*
|
||||
init_wheel_timer(int wheel_size, int clock_tic_interval);
|
||||
|
||||
|
||||
int
|
||||
wt_get_remaining_time(wheel_timer_t* wt,
|
||||
wheel_timer_elem_t* wt_elem);
|
||||
|
||||
/*Gives the absolute slot no since the time WT has started*/
|
||||
#define GET_WT_CURRENT_ABS_SLOT_NO(wt) ((wt->current_cycle_no * wt->wheel_size) + wt->current_clock_tic)
|
||||
|
||||
wheel_timer_elem_t*
|
||||
register_app_event(wheel_timer_t* wt,
|
||||
app_call_back call_back,
|
||||
void* arg,
|
||||
int arg_size,
|
||||
int time_interval,
|
||||
char is_recursive);
|
||||
|
||||
void
|
||||
de_register_app_event(wheel_timer_t* wt, wheel_timer_elem_t* wt_elem);
|
||||
|
||||
void
|
||||
wt_elem_reschedule(wheel_timer_t* wt,
|
||||
wheel_timer_elem_t* wt_elem,
|
||||
int new_time_interval);
|
||||
|
||||
void
|
||||
free_wheel_timer_element(wheel_timer_elem_t* wt_elem);
|
||||
|
||||
void
|
||||
print_wheel_timer(wheel_timer_t* wt);
|
||||
|
||||
void
|
||||
start_wheel_timer(wheel_timer_t* wt);
|
||||
|
||||
void
|
||||
cancel_wheel_timer(wheel_timer_t* wt);
|
||||
|
||||
void
|
||||
reset_wheel_timer(wheel_timer_t* wt);
|
||||
|
||||
char*
|
||||
hrs_min_sec_format(unsigned int seconds);
|
||||
|
||||
#endif
|
||||
72
include/glthread.h
Normal file
72
include/glthread.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef __GLUETHREAD__
|
||||
#define __GLUETHREAD__
|
||||
|
||||
typedef struct _glthread {
|
||||
|
||||
struct _glthread* left;
|
||||
struct _glthread* right;
|
||||
} glthread_t;
|
||||
|
||||
void
|
||||
glthread_add_next(glthread_t* base_glthread, glthread_t* new_glthread);
|
||||
|
||||
void
|
||||
glthread_add_before(glthread_t* base_glthread, glthread_t* new_glthread);
|
||||
|
||||
void
|
||||
remove_glthread(glthread_t* glthread);
|
||||
|
||||
void
|
||||
init_glthread(glthread_t* glthread);
|
||||
|
||||
void
|
||||
glthread_add_last(glthread_t* base_glthread, glthread_t* new_glthread);
|
||||
|
||||
#define IS_GLTHREAD_LIST_EMPTY(glthreadptr) \
|
||||
((glthreadptr)->right == 0 && (glthreadptr)->left == 0)
|
||||
|
||||
#define GLTHREAD_TO_STRUCT(fn_name, structure_name, field_name) \
|
||||
static inline structure_name * fn_name(glthread_t *glthreadptr){ \
|
||||
return (structure_name *)((char *)(glthreadptr) - (char *)&(((structure_name *)0)->field_name)); \
|
||||
}
|
||||
|
||||
/* delete safe loop*/
|
||||
/*Normal continue and break can be used with this loop macro*/
|
||||
|
||||
#define BASE(glthreadptr) ((glthreadptr)->right)
|
||||
|
||||
#define ITERATE_GLTHREAD_BEGIN(glthreadptrstart, glthreadptr) \
|
||||
{ \
|
||||
glthread_t *_glthread_ptr = NULL; \
|
||||
glthreadptr = BASE(glthreadptrstart); \
|
||||
for(; glthreadptr!= NULL; glthreadptr = _glthread_ptr){ \
|
||||
_glthread_ptr = (glthreadptr)->right;
|
||||
|
||||
#define ITERATE_GLTHREAD_END(glthreadptrstart, glthreadptr) \
|
||||
}}
|
||||
|
||||
#define GLTHREAD_GET_USER_DATA_FROM_OFFSET(glthreadptr, offset) \
|
||||
(void *)((char *)(glthreadptr) - offset)
|
||||
|
||||
void
|
||||
delete_glthread_list(glthread_t* base_glthread);
|
||||
|
||||
unsigned int
|
||||
get_glthread_list_count(glthread_t* base_glthread);
|
||||
|
||||
void
|
||||
glthread_priority_insert(glthread_t* base_glthread,
|
||||
glthread_t* glthread,
|
||||
int (*comp_fn)(void*, void*),
|
||||
int offset);
|
||||
|
||||
|
||||
#if 0
|
||||
void*
|
||||
gl_thread_search(glthread_t* base_glthread,
|
||||
void* (*thread_to_struct_fn)(glthread_t*),
|
||||
void* key,
|
||||
int (*comparison_fn)(void*, void*));
|
||||
|
||||
#endif
|
||||
#endif /* __GLUETHREAD__ */
|
||||
@@ -1,11 +1,17 @@
|
||||
#timerlib.c src file...
|
||||
set(TIMER_LIB_SRC "timerlib.c")
|
||||
set(WHEEL_TIMER_LIB_SRC "WheelTimer.c")
|
||||
set(GLTHREAD "glthread.c")
|
||||
|
||||
#Library
|
||||
add_library(${TIMER_LIB} STATIC
|
||||
${TIMER_LIB_SRC}
|
||||
${WHEEL_TIMER_LIB_SRC})
|
||||
${TIMER_LIB_SRC})
|
||||
|
||||
add_library(${WHEELTIMER_LIB} STATIC
|
||||
${TIMER_LIB_SRC}
|
||||
${GLTHREAD}
|
||||
${WHEEL_TIMER_LIB_SRC})
|
||||
|
||||
|
||||
|
||||
#Linking dependent library...
|
||||
@@ -19,12 +25,19 @@ set(POSIX_TIMER_LIB rt)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
|
||||
#Below snippet is important for POSIX timer library in WSL2...
|
||||
target_compile_definitions(wheeltimer PRIVATE _POSIX_C_SOURCE=199309L)
|
||||
target_link_libraries(${TIMER_LIB} PUBLIC
|
||||
${POSIX_TIMER_LIB}
|
||||
Threads::Threads)
|
||||
|
||||
target_link_libraries(${WHEELTIMER_LIB} PUBLIC
|
||||
${POSIX_TIMER_LIB}
|
||||
Threads::Threads)
|
||||
|
||||
#Including Header file dir...
|
||||
target_include_directories(${TIMER_LIB} PUBLIC
|
||||
${HEADER_DIR})
|
||||
|
||||
target_include_directories(${WHEELTIMER_LIB} PUBLIC
|
||||
${HEADER_DIR})
|
||||
342
src/WheelTimer.c
342
src/WheelTimer.c
@@ -0,0 +1,342 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <timerlib.h>
|
||||
#include <WheelTimer.h>
|
||||
|
||||
int
|
||||
insert_wt_elem_in_slot(void* data1, void* data2) {
|
||||
|
||||
wheel_timer_elem_t* wt_elem1 = (wheel_timer_elem_t*)data1;
|
||||
wheel_timer_elem_t* wt_elem2 = (wheel_timer_elem_t*)data2;
|
||||
|
||||
if (wt_elem1->execute_cycle_no < wt_elem2->execute_cycle_no)
|
||||
return -1;
|
||||
|
||||
if (wt_elem1->execute_cycle_no > wt_elem2->execute_cycle_no)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
process_wt_reschedule_slotlist(wheel_timer_t* wt) {
|
||||
|
||||
glthread_t* curr;
|
||||
wheel_timer_elem_t* wt_elem;
|
||||
|
||||
WT_LOCK_SLOT_LIST(WT_GET_RESCHD_SLOTLIST(wt));
|
||||
if (WT_IS_SLOTLIST_EMPTY(WT_GET_RESCHD_SLOTLIST(wt))) {
|
||||
WT_UNLOCK_SLOT_LIST(WT_GET_RESCHD_SLOTLIST(wt));
|
||||
return;
|
||||
}
|
||||
|
||||
ITERATE_GLTHREAD_BEGIN(WT_GET_RESCHD_SLOTLIST_HEAD(wt), curr) {
|
||||
|
||||
wt_elem = glthread_reschedule_glue_to_wt_elem(curr);
|
||||
remove_glthread(&wt_elem->glue);
|
||||
wt_elem->slotlist_head = NULL;
|
||||
|
||||
switch (wt_elem->opcode) {
|
||||
case WTELEM_CREATE:
|
||||
case WTELEM_RESCHED:
|
||||
{
|
||||
assert(wt_elem->app_callback);
|
||||
wt_elem->time_interval = wt_elem->new_time_interval;
|
||||
int absolute_slot_no = GET_WT_CURRENT_ABS_SLOT_NO(wt);
|
||||
int next_abs_slot_no = absolute_slot_no +
|
||||
(wt_elem->time_interval / wt->clock_tic_interval);
|
||||
int next_cycle_no = next_abs_slot_no / wt->wheel_size;
|
||||
int next_slot_no = next_abs_slot_no % wt->wheel_size;
|
||||
wt_elem->execute_cycle_no = next_cycle_no;
|
||||
wt_elem->slot_no = next_slot_no;
|
||||
glthread_priority_insert(WT_SLOTLIST_HEAD(wt, wt_elem->slot_no),
|
||||
&wt_elem->glue,
|
||||
insert_wt_elem_in_slot,
|
||||
(unsigned long)&((wheel_timer_elem_t*)0)->glue);
|
||||
wt_elem->slotlist_head = WT_SLOTLIST(wt, wt_elem->slot_no);
|
||||
remove_glthread(&wt_elem->reschedule_glue);
|
||||
wt_elem->N_scheduled++;
|
||||
if (wt_elem->opcode == WTELEM_CREATE) {
|
||||
wt->no_of_wt_elem++;
|
||||
}
|
||||
wt_elem->opcode = WTELEM_SCHEDULED;
|
||||
}
|
||||
break;
|
||||
case WTELEM_DELETE:
|
||||
remove_glthread(&wt_elem->reschedule_glue);
|
||||
free_wheel_timer_element(wt_elem);
|
||||
wt->no_of_wt_elem--;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}ITERATE_GLTHREAD_END(WT_GET_RESCHD_SLOTLIST_HEAD(wt), curr)
|
||||
WT_UNLOCK_SLOT_LIST(WT_GET_RESCHD_SLOTLIST(wt));
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_fn(Timer_t* timer, void* arg) {
|
||||
|
||||
wheel_timer_t* wt = (wheel_timer_t*)arg;
|
||||
wheel_timer_elem_t* wt_elem = NULL;
|
||||
int absolute_slot_no = 0, i = 0;
|
||||
slotlist_t* slot_list = NULL;
|
||||
glthread_t* curr;
|
||||
|
||||
wt->current_clock_tic++;
|
||||
if (wt->current_clock_tic == wt->wheel_size) {
|
||||
wt->current_clock_tic = 0;
|
||||
wt->current_cycle_no++;
|
||||
}
|
||||
|
||||
slot_list = WT_SLOTLIST(wt, wt->current_clock_tic);
|
||||
absolute_slot_no = GET_WT_CURRENT_ABS_SLOT_NO(wt);
|
||||
|
||||
ITERATE_GLTHREAD_BEGIN(&slot_list->slots, curr) {
|
||||
|
||||
wt_elem = glthread_to_wt_elem(curr);
|
||||
|
||||
/*Check if R == r*/
|
||||
if (wt->current_cycle_no == wt_elem->execute_cycle_no) {
|
||||
/*Invoke the application event through fn pointer as below*/
|
||||
wt_elem->app_callback(wt_elem->arg, wt_elem->arg_size);
|
||||
|
||||
/* After invocation, check if the event needs to be rescheduled again
|
||||
* in future*/
|
||||
if (wt_elem->is_recurrence) {
|
||||
|
||||
/*relocate Or reschedule to the next slot*/
|
||||
int next_abs_slot_no = absolute_slot_no +
|
||||
(wt_elem->time_interval / wt->clock_tic_interval);
|
||||
int next_cycle_no = next_abs_slot_no / wt->wheel_size;
|
||||
int next_slot_no = next_abs_slot_no % wt->wheel_size;
|
||||
wt_elem->execute_cycle_no = next_cycle_no;
|
||||
remove_glthread(&wt_elem->glue);
|
||||
glthread_priority_insert(WT_SLOTLIST_HEAD(wt, next_slot_no), &wt_elem->glue,
|
||||
insert_wt_elem_in_slot,
|
||||
(unsigned long)&((wheel_timer_elem_t*)0)->glue);
|
||||
wt_elem->slotlist_head = WT_SLOTLIST(wt, next_slot_no);
|
||||
wt_elem->slot_no = next_slot_no;
|
||||
wt_elem->N_scheduled++;
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
} ITERATE_GLTHREAD_END(slot_list, curr)
|
||||
process_wt_reschedule_slotlist(wt);
|
||||
}
|
||||
|
||||
wheel_timer_t*
|
||||
init_wheel_timer(int wheel_size, int clock_tic_interval) {
|
||||
|
||||
wheel_timer_t* wt = calloc(1, sizeof(wheel_timer_t) +
|
||||
(wheel_size * sizeof(slotlist_t)));
|
||||
|
||||
wt->clock_tic_interval = clock_tic_interval;
|
||||
wt->wheel_size = wheel_size;
|
||||
|
||||
wt->wheel_thread = setup_timer(wheel_fn,
|
||||
wt->clock_tic_interval * 1000,
|
||||
wt->clock_tic_interval * 1000,
|
||||
0,
|
||||
(void*)wt,
|
||||
false);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (; i < wheel_size; i++) {
|
||||
init_glthread(WT_SLOTLIST_HEAD(wt, i));
|
||||
pthread_mutex_init(WT_SLOTLIST_MUTEX(wt, i), NULL);
|
||||
}
|
||||
|
||||
wt->no_of_wt_elem = 0;
|
||||
return wt;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
_wt_elem_reschedule(wheel_timer_t* wt,
|
||||
wheel_timer_elem_t* wt_elem,
|
||||
int new_time_interval,
|
||||
wt_opcode_t opcode) {
|
||||
|
||||
if (wt_elem->opcode == WTELEM_DELETE &&
|
||||
(opcode == WTELEM_CREATE ||
|
||||
opcode == WTELEM_RESCHED)) {
|
||||
/* This is a Valid Scenario. A Race condition may arise When WT itself
|
||||
* invoked a timer expiry callback for a wt_elem, and at the same time
|
||||
* hello packet also arrived to refresh the same wt_elem.*/
|
||||
//assert(0);
|
||||
}
|
||||
switch (opcode) {
|
||||
case WTELEM_CREATE:
|
||||
case WTELEM_RESCHED:
|
||||
case WTELEM_DELETE:
|
||||
|
||||
wt_elem->new_time_interval = new_time_interval;
|
||||
pause_timer(wt->wheel_thread);
|
||||
WT_LOCK_SLOT_LIST(WT_GET_RESCHD_SLOTLIST(wt));
|
||||
wt_elem->opcode = opcode;
|
||||
remove_glthread(&wt_elem->reschedule_glue);
|
||||
glthread_add_next(WT_GET_RESCHD_SLOTLIST_HEAD(wt),
|
||||
&wt_elem->reschedule_glue);
|
||||
WT_UNLOCK_SLOT_LIST(WT_GET_RESCHD_SLOTLIST(wt));
|
||||
resume_timer(wt->wheel_thread);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
wheel_timer_elem_t*
|
||||
register_app_event(wheel_timer_t* wt,
|
||||
app_call_back call_back,
|
||||
void* arg,
|
||||
int arg_size,
|
||||
int time_interval,
|
||||
char is_recursive) {
|
||||
|
||||
if (!wt || !call_back) return NULL;
|
||||
wheel_timer_elem_t* wt_elem = calloc(1, sizeof(wheel_timer_elem_t));
|
||||
wt_elem->app_callback = call_back;
|
||||
if (arg && arg_size) {
|
||||
wt_elem->arg = calloc(1, arg_size);
|
||||
memcpy(wt_elem->arg, arg, arg_size);
|
||||
wt_elem->arg_size = arg_size;
|
||||
}
|
||||
wt_elem->is_recurrence = is_recursive;
|
||||
init_glthread(&wt_elem->glue);
|
||||
init_glthread(&wt_elem->reschedule_glue);
|
||||
wt_elem->N_scheduled = 0;
|
||||
_wt_elem_reschedule(wt, wt_elem, time_interval, WTELEM_CREATE);
|
||||
return wt_elem;
|
||||
}
|
||||
|
||||
void
|
||||
de_register_app_event(wheel_timer_t* wt, wheel_timer_elem_t* wt_elem) {
|
||||
|
||||
_wt_elem_reschedule(wt, wt_elem, 0, WTELEM_DELETE);
|
||||
}
|
||||
|
||||
void
|
||||
wt_elem_reschedule(wheel_timer_t* wt,
|
||||
wheel_timer_elem_t* wt_elem,
|
||||
int new_time_interval) {
|
||||
|
||||
_wt_elem_reschedule(wt, wt_elem, new_time_interval, WTELEM_RESCHED);
|
||||
}
|
||||
|
||||
int
|
||||
wt_get_remaining_time(wheel_timer_t* wt,
|
||||
wheel_timer_elem_t* wt_elem) {
|
||||
|
||||
if (wt_elem->opcode == WTELEM_CREATE ||
|
||||
wt_elem->opcode == WTELEM_RESCHED) {
|
||||
/* Means : the wt_elem has not been assigned a slot in WT,
|
||||
* just return the time interval for which it has been scheduled
|
||||
* in this case*/
|
||||
return wt_elem->new_time_interval;
|
||||
}
|
||||
int wt_elem_absolute_slot = (wt_elem->execute_cycle_no * wt->wheel_size) +
|
||||
wt_elem->slot_no;
|
||||
int diff = wt_elem_absolute_slot - GET_WT_CURRENT_ABS_SLOT_NO(wt);
|
||||
return (diff * wt->clock_tic_interval);
|
||||
}
|
||||
|
||||
void
|
||||
free_wheel_timer_element(wheel_timer_elem_t* wt_elem) {
|
||||
|
||||
wt_elem->slotlist_head = NULL;
|
||||
free(wt_elem->arg);
|
||||
free(wt_elem);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
print_wheel_timer(wheel_timer_t* wt) {
|
||||
|
||||
int i = 0, j = 0;
|
||||
glthread_t* curr;
|
||||
glthread_t* slot_list_head = NULL;
|
||||
wheel_timer_elem_t* wt_elem = NULL;
|
||||
|
||||
printf("Printing Wheel Timer DS\n");
|
||||
printf("wt->current_clock_tic = %d\n", wt->current_clock_tic);
|
||||
printf("wt->clock_tic_interval = %d\n", wt->clock_tic_interval);
|
||||
printf("wt abs slot no = %d\n", GET_WT_CURRENT_ABS_SLOT_NO(wt));
|
||||
printf("wt->wheel_size = %d\n", wt->wheel_size);
|
||||
printf("wt->current_cycle_no = %d\n", wt->current_cycle_no);
|
||||
printf("wt->wheel_thread = %p\n", &wt->wheel_thread);
|
||||
printf("WT uptime = %s\n", hrs_min_sec_format(WT_UPTIME(wt)));
|
||||
printf("wt->no_of_wt_elem = %u\n", wt->no_of_wt_elem);
|
||||
printf("printing slots : \n");
|
||||
|
||||
for (; i < wt->wheel_size; i++) {
|
||||
slot_list_head = WT_SLOTLIST_HEAD(wt, i);
|
||||
ITERATE_GLTHREAD_BEGIN(slot_list_head, curr) {
|
||||
wt_elem = glthread_to_wt_elem(curr);
|
||||
printf(" wt_elem->opcode = %d\n", wt_elem->opcode);
|
||||
printf(" wt_elem = %p\n", wt_elem);
|
||||
printf(" wt_elem->time_interval = %d\n", wt_elem->time_interval);
|
||||
printf(" wt_elem->execute_cycle_no = %d\n", wt_elem->execute_cycle_no);
|
||||
printf(" wt_elem->slot_no = %d\n", wt_elem->slot_no);
|
||||
printf(" wt_elem abs slot no = %d\n",
|
||||
(wt_elem->execute_cycle_no * wt->wheel_size) + wt_elem->slot_no);
|
||||
printf(" wt_elem->app_callback = %p\n", wt_elem->app_callback);
|
||||
printf(" wt_elem->arg = %p\n", wt_elem->arg);
|
||||
printf(" wt_elem->is_recurrence = %d\n", wt_elem->is_recurrence);
|
||||
printf(" wt_elem->N_scheduled = %u\n", wt_elem->N_scheduled);
|
||||
printf(" Remaining Time to Fire = %d\n",
|
||||
wt_get_remaining_time(wt, wt_elem));
|
||||
printf("\n");
|
||||
} ITERATE_GLTHREAD_END(slot_list_head, curr)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
start_wheel_timer(wheel_timer_t* wt) {
|
||||
|
||||
start_timer(wt->wheel_thread);
|
||||
}
|
||||
|
||||
void
|
||||
reset_wheel_timer(wheel_timer_t* wt) {
|
||||
wt->current_clock_tic = 0;
|
||||
wt->current_cycle_no = 0;
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
hrs_min_sec_format(unsigned int seconds) {
|
||||
|
||||
static char time_f[16];
|
||||
unsigned int hrs = 0,
|
||||
min = 0, sec = 0;
|
||||
|
||||
if (seconds > 3600) {
|
||||
min = seconds / 60;
|
||||
sec = seconds % 60;
|
||||
hrs = min / 60;
|
||||
min = min % 60;
|
||||
}
|
||||
else {
|
||||
min = seconds / 60;
|
||||
sec = seconds % 60;
|
||||
}
|
||||
memset(time_f, 0, sizeof(time_f));
|
||||
sprintf(time_f, "%u::%u::%u", hrs, min, sec);
|
||||
return time_f;
|
||||
}
|
||||
|
||||
void
|
||||
cancel_wheel_timer(wheel_timer_t* wt) {
|
||||
|
||||
if (wt->wheel_thread) {
|
||||
cancel_timer(wt->wheel_thread);
|
||||
}
|
||||
}
|
||||
|
||||
167
src/glthread.c
Normal file
167
src/glthread.c
Normal file
@@ -0,0 +1,167 @@
|
||||
#include "glthread.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
void
|
||||
init_glthread(glthread_t* glthread) {
|
||||
|
||||
glthread->left = NULL;
|
||||
glthread->right = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
glthread_add_next(glthread_t* curr_glthread, glthread_t* new_glthread) {
|
||||
|
||||
if (!curr_glthread->right) {
|
||||
curr_glthread->right = new_glthread;
|
||||
new_glthread->left = curr_glthread;
|
||||
return;
|
||||
}
|
||||
|
||||
glthread_t* temp = curr_glthread->right;
|
||||
curr_glthread->right = new_glthread;
|
||||
new_glthread->left = curr_glthread;
|
||||
new_glthread->right = temp;
|
||||
temp->left = new_glthread;
|
||||
}
|
||||
|
||||
void
|
||||
glthread_add_before(glthread_t* curr_glthread, glthread_t* new_glthread) {
|
||||
|
||||
if (!curr_glthread->left) {
|
||||
new_glthread->left = NULL;
|
||||
new_glthread->right = curr_glthread;
|
||||
curr_glthread->left = new_glthread;
|
||||
return;
|
||||
}
|
||||
|
||||
glthread_t* temp = curr_glthread->left;
|
||||
temp->right = new_glthread;
|
||||
new_glthread->left = temp;
|
||||
new_glthread->right = curr_glthread;
|
||||
curr_glthread->left = new_glthread;
|
||||
}
|
||||
|
||||
void
|
||||
remove_glthread(glthread_t* curr_glthread) {
|
||||
|
||||
if (!curr_glthread->left) {
|
||||
if (curr_glthread->right) {
|
||||
curr_glthread->right->left = NULL;
|
||||
curr_glthread->right = 0;
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!curr_glthread->right) {
|
||||
curr_glthread->left->right = NULL;
|
||||
curr_glthread->left = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
curr_glthread->left->right = curr_glthread->right;
|
||||
curr_glthread->right->left = curr_glthread->left;
|
||||
curr_glthread->left = 0;
|
||||
curr_glthread->right = 0;
|
||||
}
|
||||
|
||||
void
|
||||
delete_glthread_list(glthread_t* base_glthread) {
|
||||
|
||||
glthread_t* glthreadptr = NULL;
|
||||
|
||||
ITERATE_GLTHREAD_BEGIN(base_glthread, glthreadptr) {
|
||||
remove_glthread(glthreadptr);
|
||||
} ITERATE_GLTHREAD_END(base_glthread, glthreadptr);
|
||||
}
|
||||
|
||||
void
|
||||
glthread_add_last(glthread_t* base_glthread, glthread_t* new_glthread) {
|
||||
|
||||
glthread_t* glthreadptr = NULL,
|
||||
* prevglthreadptr = NULL;
|
||||
|
||||
ITERATE_GLTHREAD_BEGIN(base_glthread, glthreadptr) {
|
||||
prevglthreadptr = glthreadptr;
|
||||
} ITERATE_GLTHREAD_END(base_glthread, glthreadptr);
|
||||
|
||||
if (prevglthreadptr)
|
||||
glthread_add_next(prevglthreadptr, new_glthread);
|
||||
else
|
||||
glthread_add_next(base_glthread, new_glthread);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
get_glthread_list_count(glthread_t* base_glthread) {
|
||||
|
||||
unsigned int count = 0;
|
||||
glthread_t* glthreadptr = NULL;
|
||||
|
||||
ITERATE_GLTHREAD_BEGIN(base_glthread, glthreadptr) {
|
||||
count++;
|
||||
} ITERATE_GLTHREAD_END(base_glthread, glthreadptr);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
glthread_priority_insert(glthread_t* base_glthread,
|
||||
glthread_t* glthread,
|
||||
int (*comp_fn)(void*, void*),
|
||||
int offset) {
|
||||
|
||||
|
||||
glthread_t* curr = NULL,
|
||||
* prev = NULL;
|
||||
|
||||
init_glthread(glthread);
|
||||
|
||||
if (IS_GLTHREAD_LIST_EMPTY(base_glthread)) {
|
||||
glthread_add_next(base_glthread, glthread);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Only one node*/
|
||||
if (base_glthread->right && !base_glthread->right->right) {
|
||||
if (comp_fn(GLTHREAD_GET_USER_DATA_FROM_OFFSET(base_glthread->right, offset),
|
||||
GLTHREAD_GET_USER_DATA_FROM_OFFSET(glthread, offset)) == -1) {
|
||||
glthread_add_next(base_glthread->right, glthread);
|
||||
}
|
||||
else {
|
||||
glthread_add_next(base_glthread, glthread);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp_fn(GLTHREAD_GET_USER_DATA_FROM_OFFSET(glthread, offset),
|
||||
GLTHREAD_GET_USER_DATA_FROM_OFFSET(base_glthread->right, offset)) == -1) {
|
||||
glthread_add_next(base_glthread, glthread);
|
||||
return;
|
||||
}
|
||||
|
||||
ITERATE_GLTHREAD_BEGIN(base_glthread, curr) {
|
||||
|
||||
if (comp_fn(GLTHREAD_GET_USER_DATA_FROM_OFFSET(glthread, offset),
|
||||
GLTHREAD_GET_USER_DATA_FROM_OFFSET(curr, offset)) != -1) {
|
||||
prev = curr;
|
||||
continue;
|
||||
}
|
||||
|
||||
glthread_add_next(curr, glthread);
|
||||
return;
|
||||
|
||||
}ITERATE_GLTHREAD_END(base_glthread, curr);
|
||||
|
||||
/*Add in the end*/
|
||||
glthread_add_next(prev, glthread);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void*
|
||||
gl_thread_search(glthread_t* base_glthread,
|
||||
void* (*thread_to_struct_fn)(glthread_t*),
|
||||
void* key,
|
||||
int (*comparison_fn)(void*, void*)) {
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
@@ -1,12 +1,25 @@
|
||||
set(TIMERLIB_TEST_EXE timerlib_test)
|
||||
set(WHEELTIMER_TEST_EXE wheeltimer_test)
|
||||
|
||||
set(TIMERLIB_TEST_SRC "timerlib_test.c")
|
||||
set(WHEEL_TIMER_TEST_SRC "WheelTimerDemo.c")
|
||||
|
||||
target_compile_definitions(wheeltimer PRIVATE _POSIX_C_SOURCE=199309L)
|
||||
|
||||
add_executable(${TIMERLIB_TEST_EXE}
|
||||
${TIMERLIB_TEST_SRC})
|
||||
|
||||
add_executable(${WHEELTIMER_TEST_EXE}
|
||||
${WHEEL_TIMER_TEST_SRC})
|
||||
|
||||
target_link_libraries(${TIMERLIB_TEST_EXE} PUBLIC
|
||||
${TIMER_LIB})
|
||||
|
||||
target_link_libraries(${WHEELTIMER_TEST_EXE} PUBLIC
|
||||
${WHEELTIMER_LIB})
|
||||
|
||||
target_include_directories(${TIMERLIB_TEST_EXE} PUBLIC
|
||||
${HEADER_DIR})
|
||||
${HEADER_DIR})
|
||||
|
||||
target_include_directories(${WHEELTIMER_TEST_EXE} PUBLIC
|
||||
${HEADER_DIR})
|
||||
53
test/WheelTimerDemo.c
Normal file
53
test/WheelTimerDemo.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <stdio.h>
|
||||
#include <WheelTimer.h>
|
||||
#include <string.h>
|
||||
|
||||
#define WHEEL_SIZE 10
|
||||
#define WHEEL_TIMER_CLOCK_TIC_INTERVAL 1
|
||||
|
||||
/* Application routine to be (indirectly) invoked by Wheel timer.
|
||||
* It could be of any prototype*/
|
||||
void
|
||||
print_hello(char* S) {
|
||||
printf("%s\n", S);
|
||||
}
|
||||
|
||||
/* But Only routines (Events) which have prototype : void *(fn)(void *arg, int arg_size)
|
||||
* could be registered with wheel timer. Therefore, we need to encapsulate
|
||||
* the actual routine print_hello() to be invoked inside the routine of
|
||||
* void *(fn)(void *arg, int arg_size) prototype. It is the wrapper which will be registered
|
||||
* with wheel timer and invoked by wheel timer. We will unwrap the argument and invoke the actual
|
||||
* appln routine with correct arguments. This technique is called 'Masking of routines'*/
|
||||
|
||||
void wrapper_print_hello(void* arg, int arg_size) {
|
||||
char* S = (char*)arg;
|
||||
print_hello(S);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv) {
|
||||
|
||||
/*create a wheel timer object*/
|
||||
wheel_timer_t* wt = init_wheel_timer(WHEEL_SIZE, WHEEL_TIMER_CLOCK_TIC_INTERVAL);
|
||||
/*start the wheel timer thread*/
|
||||
start_wheel_timer(wt);
|
||||
|
||||
/*Now Wheel timer has started running in a separte thread.
|
||||
* Register the events to be triggered with Wheel timer now.*/
|
||||
|
||||
wheel_timer_elem_t* wt_elem =
|
||||
register_app_event(wt, wrapper_print_hello, "MyString",
|
||||
strlen("MyString"),
|
||||
5, /*wrapper_print_hello fn will be called after every 5 seconds*/
|
||||
1); /*1 indefinitely, 0 only once : call for wrapper_print_hello*/
|
||||
|
||||
wt_elem =
|
||||
register_app_event(wt, wrapper_print_hello, "www.csepracticals.com",
|
||||
strlen("www.csepracticals.com"),
|
||||
3, /*wrapper_print_hello fn will be called after every 5 seconds*/
|
||||
1); /*1 indefinitely, 0 only once : call for wrapper_print_hello*/
|
||||
/*stop the main program from gettin terminated, otherwise wheel timer
|
||||
* thread we created will also get terminated*/
|
||||
scanf("\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user