diff --git a/Assignment/rt_entry_expiration.c b/Assignment/rt_entry_expiration.c index a49b0bb..31eb165 100644 --- a/Assignment/rt_entry_expiration.c +++ b/Assignment/rt_entry_expiration.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "rt.h" static rt_table_t rt; diff --git a/CMakeLists.txt b/CMakeLists.txt index 854bd40..80cc820 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/$") #Library Name... set(TIMER_LIB timer) +set(WHEELTIMER_LIB wheeltimer) #Header file directory... diff --git a/include/WheelTimer.h b/include/WheelTimer.h index 4ff6d82..89652f9 100644 --- a/include/WheelTimer.h +++ b/include/WheelTimer.h @@ -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 +#include +#include -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 \ No newline at end of file diff --git a/include/glthread.h b/include/glthread.h new file mode 100644 index 0000000..a8e41bb --- /dev/null +++ b/include/glthread.h @@ -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__ */ \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index abec4c0..65bbd91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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}) \ No newline at end of file diff --git a/src/WheelTimer.c b/src/WheelTimer.c index e69de29..38e1a67 100644 --- a/src/WheelTimer.c +++ b/src/WheelTimer.c @@ -0,0 +1,342 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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); + } +} diff --git a/src/glthread.c b/src/glthread.c new file mode 100644 index 0000000..252c2dc --- /dev/null +++ b/src/glthread.c @@ -0,0 +1,167 @@ +#include "glthread.h" +#include + +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 \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 046ac6d..76dd3f3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -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}) \ No newline at end of file + ${HEADER_DIR}) + +target_include_directories(${WHEELTIMER_TEST_EXE} PUBLIC + ${HEADER_DIR}) \ No newline at end of file diff --git a/test/WheelTimerDemo.c b/test/WheelTimerDemo.c new file mode 100644 index 0000000..e6d0138 --- /dev/null +++ b/test/WheelTimerDemo.c @@ -0,0 +1,53 @@ +#include +#include +#include + +#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; +} \ No newline at end of file