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 <stdint.h>
|
||||||
#include <timerlib.h>
|
#include <timerlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
#include "rt.h"
|
#include "rt.h"
|
||||||
|
|
||||||
static rt_table_t rt;
|
static rt_table_t rt;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/$<CONFIG>")
|
|||||||
|
|
||||||
#Library Name...
|
#Library Name...
|
||||||
set(TIMER_LIB timer)
|
set(TIMER_LIB timer)
|
||||||
|
set(WHEELTIMER_LIB wheeltimer)
|
||||||
|
|
||||||
|
|
||||||
#Header file directory...
|
#Header file directory...
|
||||||
|
|||||||
@@ -1,20 +1,148 @@
|
|||||||
#ifndef __WHEEL_TIMER_H
|
#ifndef __WHEEL_TIMER__
|
||||||
#define __WHEEL_TIMER_H
|
#define __WHEEL_TIMER__
|
||||||
|
|
||||||
|
|
||||||
|
//To use POSIX TIMER library
|
||||||
|
#define __USE_POSIX199309 1
|
||||||
|
|
||||||
#include <pthread.h>
|
#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*/
|
typedef struct slotlist_ {
|
||||||
int clock_tic_interval; /*Time interval of each clock tick*/
|
glthread_t slots;
|
||||||
int wheel_size; /*No. of slots in wheel timer*/
|
pthread_mutex_t slot_mutex;
|
||||||
int current_cycle_no; /*No. of rotation completed by wheel timer clock tic*/
|
}slotlist_t;
|
||||||
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 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;
|
} 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
|
#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,13 +1,19 @@
|
|||||||
#timerlib.c src file...
|
#timerlib.c src file...
|
||||||
set(TIMER_LIB_SRC "timerlib.c")
|
set(TIMER_LIB_SRC "timerlib.c")
|
||||||
set(WHEEL_TIMER_LIB_SRC "WheelTimer.c")
|
set(WHEEL_TIMER_LIB_SRC "WheelTimer.c")
|
||||||
|
set(GLTHREAD "glthread.c")
|
||||||
|
|
||||||
#Library
|
#Library
|
||||||
add_library(${TIMER_LIB} STATIC
|
add_library(${TIMER_LIB} STATIC
|
||||||
|
${TIMER_LIB_SRC})
|
||||||
|
|
||||||
|
add_library(${WHEELTIMER_LIB} STATIC
|
||||||
${TIMER_LIB_SRC}
|
${TIMER_LIB_SRC}
|
||||||
|
${GLTHREAD}
|
||||||
${WHEEL_TIMER_LIB_SRC})
|
${WHEEL_TIMER_LIB_SRC})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#Linking dependent library...
|
#Linking dependent library...
|
||||||
|
|
||||||
if( NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux") )
|
if( NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux") )
|
||||||
@@ -19,12 +25,19 @@ set(POSIX_TIMER_LIB rt)
|
|||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
find_package(Threads REQUIRED)
|
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
|
target_link_libraries(${TIMER_LIB} PUBLIC
|
||||||
${POSIX_TIMER_LIB}
|
${POSIX_TIMER_LIB}
|
||||||
Threads::Threads)
|
Threads::Threads)
|
||||||
|
|
||||||
|
target_link_libraries(${WHEELTIMER_LIB} PUBLIC
|
||||||
|
${POSIX_TIMER_LIB}
|
||||||
|
Threads::Threads)
|
||||||
|
|
||||||
#Including Header file dir...
|
#Including Header file dir...
|
||||||
target_include_directories(${TIMER_LIB} PUBLIC
|
target_include_directories(${TIMER_LIB} PUBLIC
|
||||||
${HEADER_DIR})
|
${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(TIMERLIB_TEST_EXE timerlib_test)
|
||||||
|
set(WHEELTIMER_TEST_EXE wheeltimer_test)
|
||||||
|
|
||||||
set(TIMERLIB_TEST_SRC "timerlib_test.c")
|
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}
|
add_executable(${TIMERLIB_TEST_EXE}
|
||||||
${TIMERLIB_TEST_SRC})
|
${TIMERLIB_TEST_SRC})
|
||||||
|
|
||||||
|
add_executable(${WHEELTIMER_TEST_EXE}
|
||||||
|
${WHEEL_TIMER_TEST_SRC})
|
||||||
|
|
||||||
target_link_libraries(${TIMERLIB_TEST_EXE} PUBLIC
|
target_link_libraries(${TIMERLIB_TEST_EXE} PUBLIC
|
||||||
${TIMER_LIB})
|
${TIMER_LIB})
|
||||||
|
|
||||||
|
target_link_libraries(${WHEELTIMER_TEST_EXE} PUBLIC
|
||||||
|
${WHEELTIMER_LIB})
|
||||||
|
|
||||||
target_include_directories(${TIMERLIB_TEST_EXE} PUBLIC
|
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