diff options
Diffstat (limited to 'tinySAK/src/tsk_timer.c')
-rw-r--r-- | tinySAK/src/tsk_timer.c | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/tinySAK/src/tsk_timer.c b/tinySAK/src/tsk_timer.c new file mode 100644 index 0000000..0cb4ba0 --- /dev/null +++ b/tinySAK/src/tsk_timer.c @@ -0,0 +1,531 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ + +/**@file tsk_timer.c +* @brief Timer Manager. +* +* @author Mamadou Diop <diopmamadou(at)doubango.org> +* +* @date Created: Sat Nov 8 16:54:58 2009 mdiop +*/ +#include "tsk_timer.h" +#include "tsk_debug.h" +#include "tsk_list.h" +#include "tsk_thread.h" +#include "tsk_runnable.h" +#include "tsk_condwait.h" +#include "tsk_semaphore.h" +#include "tsk_time.h" + + +/**@defgroup tsk_timer_group Timers Management +*/ + +#define TSK_TIMER_CREATE(timeout, callback, arg) tsk_object_new(tsk_timer_def_t, timeout, callback, arg) +#define TSK_TIMER_TIMEOUT(self) ((tsk_timer_t*)self)->timeout +#define TSK_TIMER_GET_FIRST() (manager->timers && manager->timers->head) ? (tsk_timer_t*)(((tsk_list_item_t*)(manager->timers->head))->data) : 0 + +/** + * @struct tsk_timer_s + * @brief Timer. +**/ +typedef struct tsk_timer_s +{ + TSK_DECLARE_OBJECT; + + tsk_timer_id_t id; /**< Unique timer identifier. */ + const void *arg; /**< Opaque data to return with the callback function. */ + uint64_t timeout; /**< When the timer will timeout(as EPOCH time). */ + tsk_timer_callback_f callback; /**< The callback function to call after @ref timeout milliseconds. */ + + unsigned canceled:1; +} +tsk_timer_t; +typedef tsk_list_t tsk_timers_L_t; /**< List of @ref tsk_timer_t elements. */ + +/** + * @struct tsk_timer_manager_s + * + * @brief Timer manager. +**/ +typedef struct tsk_timer_manager_s +{ + TSK_DECLARE_RUNNABLE; + + void* mainThreadId[1]; + tsk_condwait_handle_t *condwait; + tsk_mutex_handle_t *mutex; + tsk_semaphore_handle_t *sem; + + tsk_timers_L_t *timers; +} +tsk_timer_manager_t; +typedef tsk_list_t tsk_timer_manager_L_t; /**< List of @ref tsk_timer_manager_t elements. */ + +/*== Definitions */ +static void *__tsk_timer_manager_mainthread(void *param); +static int __tsk_pred_find_timer_by_id(const tsk_list_item_t *item, const void *id); +static void __tsk_timer_manager_raise(tsk_timer_t *timer); +static void *run(void* self); + +/**@ingroup tsk_timer_group +*/ +tsk_timer_manager_handle_t* tsk_timer_manager_create() +{ + return tsk_object_new(tsk_timer_manager_def_t); +} + +/**@ingroup tsk_timer_group +* Starts the timer manager. +*/ +int tsk_timer_manager_start(tsk_timer_manager_handle_t *self) +{ + int err = -1; + tsk_timer_manager_t *manager = self; + + TSK_DEBUG_INFO("tsk_timer_manager_start"); + + if(!manager){ + return -1; + } + + tsk_mutex_lock(manager->mutex); + + if(!TSK_RUNNABLE(manager)->running && !TSK_RUNNABLE(manager)->started){ + TSK_RUNNABLE(manager)->run = run; + if(err = tsk_runnable_start(TSK_RUNNABLE(manager), tsk_timer_def_t)){ + //TSK_OBJECT_SAFE_FREE(manager); + goto bail; + } + } + else{ + TSK_DEBUG_WARN("Timer manager already running"); + } + +bail: + tsk_mutex_unlock(manager->mutex); + + return err; +} + +#if defined(DEBUG) || defined(_DEBUG) || !defined(NDEBUG) +/**@ingroup tsk_timer_group +*/ +void tsk_timer_manager_debug(tsk_timer_manager_handle_t *self) +{ + tsk_timer_manager_t *manager = self; + if(manager){ + //int index = 0; + tsk_list_item_t *item = tsk_null; + + tsk_mutex_lock(manager->mutex); + + tsk_list_foreach(item, manager->timers){ + tsk_timer_t* timer = item->data; + TSK_DEBUG_INFO("timer [%llu]- %llu, %llu", timer->id, timer->timeout, tsk_time_epoch()); + } + + tsk_mutex_unlock(manager->mutex); + } +} +#endif + +/**@ingroup tsk_timer_group +*/ +int tsk_timer_manager_stop(tsk_timer_manager_handle_t *self) +{ + int ret = -1; + tsk_timer_manager_t *manager = self; + + if(!manager){ + TSK_DEBUG_ERROR("Invalid paramater"); + return -1; + } + + // all functions called below are thread-safe ==> do not lock + // "mainthread" uses manager->mutex and runs in a separate thread ==> deadlock + + if(TSK_RUNNABLE(manager)->running){ + if(ret = tsk_runnable_stop(TSK_RUNNABLE(manager))){ + goto bail; + } + + tsk_semaphore_increment(manager->sem); + tsk_condwait_signal(manager->condwait); + + ret = tsk_thread_join(manager->mainThreadId); + goto bail; + } + else{ + ret = 0; /* already running. */ + goto bail; + } + +bail: + return ret; +} + +/**@ingroup tsk_timer_group +*/ +tsk_timer_id_t tsk_timer_manager_schedule(tsk_timer_manager_handle_t *self, uint64_t timeout, tsk_timer_callback_f callback, const void *arg) +{ + tsk_timer_id_t timer_id = TSK_INVALID_TIMER_ID; + tsk_timer_manager_t *manager = self; + + if(manager && (TSK_RUNNABLE(manager)->running || TSK_RUNNABLE(manager)->started)){ + tsk_timer_t *timer; + + timer = TSK_TIMER_CREATE(timeout, callback, arg); + timer_id = timer->id; + tsk_mutex_lock(manager->mutex); + tsk_list_push_ascending_data(manager->timers, ((void**) &timer)); + tsk_mutex_unlock(manager->mutex); + + //tsk_timer_manager_debug(self); + + tsk_condwait_signal(manager->condwait); + tsk_semaphore_increment(manager->sem); + } + + return timer_id; +} + +/**@ingroup tsk_timer_group +*/ +int tsk_timer_manager_cancel(tsk_timer_manager_handle_t *self, tsk_timer_id_t id) +{ + int ret = -1; + tsk_timer_manager_t *manager = self; + + /* Check validity. */ + if(!TSK_TIMER_ID_IS_VALID(id)){ /* Very common. */ + return 0; + } + + if(!TSK_LIST_IS_EMPTY(manager->timers) && TSK_RUNNABLE(manager)->running){ + const tsk_list_item_t *item; + tsk_mutex_lock(manager->mutex); + item = tsk_list_find_item_by_pred(manager->timers, __tsk_pred_find_timer_by_id, &id); + if(item && item->data){ + tsk_timer_t *timer = item->data; + timer->canceled = 1; + + if(item == manager->timers->head){ + /* The timer we are waiting on ? ==> remove it now. */ + tsk_condwait_signal(manager->condwait); + } + + ret = 0; + } + tsk_mutex_unlock(manager->mutex); + } + return ret; +} + +static void *run(void* self) +{ + int ret; + tsk_list_item_t *curr; + tsk_timer_manager_t *manager = self; + + TSK_RUNNABLE(manager)->running = tsk_true; // VERY IMPORTANT --> needed by the main thread + + /* create main thread */ + if((ret = tsk_thread_create(&(manager->mainThreadId[0]), __tsk_timer_manager_mainthread, manager))){ + TSK_DEBUG_FATAL("Failed to create mainthread: %d\n", ret); + return tsk_null; + } + + TSK_DEBUG_INFO("Timer manager run()::enter"); + + TSK_RUNNABLE_RUN_BEGIN(manager); + + if(curr = TSK_RUNNABLE_POP_FIRST(manager)){ + tsk_timer_t *timer = (tsk_timer_t *)curr->data; + if(timer->callback){ + timer->callback(timer->arg, timer->id); + } + tsk_object_unref(curr); + } + + TSK_RUNNABLE_RUN_END(manager); + + TSK_DEBUG_INFO("Timer manager run()::exit"); + + return tsk_null; +} + +static int __tsk_pred_find_timer_by_id(const tsk_list_item_t *item, const void *id) +{ + tsk_timer_t *timer; + if(item && item->data){ + timer = item->data; + return (int)(timer->id - *((tsk_timer_id_t*)id)); + } + return -1; +} + +static void *__tsk_timer_manager_mainthread(void *param) +{ + int ret; + tsk_timer_t *curr; + uint64_t epoch; + tsk_timer_manager_t *manager = param; + + TSK_DEBUG_INFO("TIMER MANAGER -- START"); + + while(TSK_RUNNABLE(manager)->running){ + tsk_semaphore_decrement(manager->sem); + +peek_first: + if(!TSK_RUNNABLE(manager)->running){ + break; + } + + tsk_mutex_lock(manager->mutex); + curr = TSK_TIMER_GET_FIRST(); + tsk_mutex_unlock(manager->mutex); + + if(curr && !curr->canceled) { + epoch = tsk_time_epoch(); + if(epoch >= curr->timeout){ + tsk_timer_t *timer = tsk_object_ref(curr); + //TSK_DEBUG_INFO("Timer raise %llu", timer->id); + + tsk_mutex_lock(manager->mutex); + TSK_RUNNABLE_ENQUEUE_OBJECT(TSK_RUNNABLE(manager), timer); + tsk_list_remove_item_by_data(manager->timers, curr); + tsk_mutex_unlock(manager->mutex); + } + else{ + if((ret = tsk_condwait_timedwait(manager->condwait, (curr->timeout - epoch)))){ + TSK_DEBUG_ERROR("CONWAIT for timer manager failed [%d]", ret); + break; + } + else{ + goto peek_first; + } + } + } + else if(curr){ + tsk_mutex_lock(manager->mutex); + /* TSK_DEBUG_INFO("Timer canceled %llu", curr->id); */ + tsk_list_remove_item_by_data(manager->timers, curr); + tsk_mutex_unlock(manager->mutex); + } + } /* while() */ + + TSK_DEBUG_INFO("TIMER MANAGER -- STOP"); + + return 0; +} + + + + + +/* ================= Global Timer Manager ================= */ + +static tsk_timer_manager_t* __timer_mgr = tsk_null; +static int __timer_mgr_start_count = 0; + +int tsk_timer_mgr_global_ref() +{ + if(!__timer_mgr){ + __timer_mgr = tsk_timer_manager_create(); + } + else{ + __timer_mgr = tsk_object_ref(__timer_mgr); + } + return 0; +} + +int tsk_timer_mgr_global_start() +{ + int ret = 0; + if(!__timer_mgr){ + TSK_DEBUG_ERROR("No global Timer manager could be found"); + return -1; + } + if(!TSK_RUNNABLE(__timer_mgr)->running && !TSK_RUNNABLE(__timer_mgr)->started){ + if((ret = tsk_timer_manager_start(__timer_mgr))){ + return ret; + } + } + __timer_mgr_start_count++; + return ret; +} + +tsk_timer_id_t tsk_timer_mgr_global_schedule(uint64_t timeout, tsk_timer_callback_f callback, const void *arg) +{ + if(!__timer_mgr){ + TSK_DEBUG_ERROR("No global Timer manager could be found"); + return TSK_INVALID_TIMER_ID; + } + return tsk_timer_manager_schedule(__timer_mgr, timeout, callback, arg); +} + +int tsk_timer_mgr_global_cancel(tsk_timer_id_t id) +{ + if(!__timer_mgr){ + TSK_DEBUG_ERROR("No global Timer manager could be found"); + return -1; + } + return tsk_timer_manager_cancel(__timer_mgr, id); +} + +int tsk_timer_mgr_global_stop() +{ + int ret = 0; + if(!__timer_mgr){ + TSK_DEBUG_ERROR("No global Timer manager could be found"); + return -1; + } + + if(__timer_mgr_start_count <= 0){ + TSK_DEBUG_ERROR("Global Timer is in an invalid state"); + return -2; + } + + if(TSK_RUNNABLE(__timer_mgr)->running){ + if(__timer_mgr_start_count == 1){ + if((ret = tsk_timer_manager_stop(__timer_mgr))){ + return ret; + } + } + __timer_mgr_start_count--; + } + return 0; +} + +int tsk_timer_mgr_global_unref() +{ + if(!__timer_mgr){ + TSK_DEBUG_ERROR("No global Timer manager could be found"); + return -1; + } + + __timer_mgr = tsk_object_unref(__timer_mgr); + + return 0; +} + + + + + + + +//================================================================================================= +// Timer manager object definition +// +static tsk_object_t* tsk_timer_manager_ctor(tsk_object_t * self, va_list * app) +{ + tsk_timer_manager_t *manager = self; + if(manager){ + manager->timers = tsk_list_create(); + manager->sem = tsk_semaphore_create(); + manager->condwait = tsk_condwait_create(); + manager->mutex = tsk_mutex_create(); + } + return self; +} + +static tsk_object_t* tsk_timer_manager_dtor(tsk_object_t * self) +{ + tsk_timer_manager_t *manager = self; + + if(manager){ + tsk_timer_manager_stop(manager); + + tsk_semaphore_destroy(&manager->sem); + tsk_condwait_destroy(&manager->condwait); + tsk_mutex_destroy(&manager->mutex); + tsk_object_unref(manager->timers); + } + + return self; +} + +static const tsk_object_def_t tsk_timer_manager_def_s = +{ + sizeof(tsk_timer_manager_t), + tsk_timer_manager_ctor, + tsk_timer_manager_dtor, + tsk_null, +}; +const tsk_object_def_t * tsk_timer_manager_def_t = &tsk_timer_manager_def_s; + + + + + + +//================================================================================================= +// Timer object definition +// +static tsk_object_t* tsk_timer_ctor(tsk_object_t * self, va_list * app) +{ + static tsk_timer_id_t tsk_unique_timer_id = 1; + tsk_timer_t *timer = self; + if(timer){ + timer->id = tsk_unique_timer_id++; + timer->timeout = va_arg(*app, uint64_t); + timer->callback = va_arg(*app, tsk_timer_callback_f); + timer->arg = va_arg(*app, const void *); + + timer->timeout += tsk_time_epoch(); + } + return self; +} + +static tsk_object_t* tsk_timer_dtor(tsk_object_t * self) +{ + tsk_timer_t *timer = self; + if(timer){ + } + + return self; +} + +static int tsk_timer_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) +{ + const tsk_timer_t *t1 = obj1; + const tsk_timer_t *t2 = obj2; + + if(t1 && t2){ + return (int)(t1->timeout - t2->timeout); + } + else if(!t1 && !t2) return 0; + else return -1; +} + +static const tsk_object_def_t tsk_timer_def_s = +{ + sizeof(tsk_timer_t), + tsk_timer_ctor, + tsk_timer_dtor, + tsk_timer_cmp, +}; +const tsk_object_def_t * tsk_timer_def_t = &tsk_timer_def_s; + + |