| // Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <assert.h> |
| |
| #include "timer.h" |
| |
| #if 1 |
| #include "log.h" |
| #define timer_func_in(fmt, args...) xfunc_in(fmt, ## args) |
| #define timer_func_out(fmt, args...) xfunc_out(fmt, ## args) |
| #define timer_iprintf(fmt, args...) xprintf(SDK_INFO, fmt, ## args) |
| #define timer_dprintf(fmt, args...) xprintf(SDK_DBG, fmt, ## args) |
| #define timer_eprintf(fmt, args...) xprintf(SDK_ERR, fmt, ## args) |
| #define timer_std_eprintf(fmt, args...) xprintf(SDK_STD_ERR, fmt, ## args) |
| #else |
| #define timer_func_in(fmt, args...) fprintf(stdout, "+%s " fmt "\n", __FUNCTION__, ## args) |
| #define timer_func_out(fmt, args...) fprintf(stdout, "-%s " fmt "\n", __FUNCTION__, ## args) |
| #define timer_iprintf(fmt, args...) fprintf(stdout, fmt, ## args) |
| #define timer_dprintf(fmt, args...) fprintf(stdout, fmt, ## args) |
| #define timer_eprintf(fmt, args...) fprintf(stderr, "%s " fmt, __FUNCTION__, ## args) |
| #define timer_std_eprintf(fmt, args...) fprintf(stderr, "%s err=%s(%d):" fmt,\ |
| __FUNCTION__, strerror(errno), errno, ## args) |
| #endif |
| |
| #define CREATED_MASK 0x54494D45UL |
| |
| #if defined(PTHREAD_TIMER) |
| //#define TIMER_ASSERT |
| |
| typedef struct pthread_timer_s { |
| unsigned tt_inited; |
| pthread_t tt_thread; |
| pthread_mutex_t tt_lock; |
| pthread_cond_t tt_cond; |
| |
| struct list_head tt_head; |
| timer_obj_t *tt_active; |
| |
| } pthread_timer_t; |
| |
| static pthread_timer_t pthread_timer; |
| |
| static struct timespec *get_timespec_ms(struct timespec *ts, int timeout_ms) |
| { |
| #define nsec_per_sec (1000*1000*1000) |
| #define ps_timeval2timespec(tv,ts) \ |
| ((ts)->tv_sec = (tv)->tv_sec, (ts)->tv_nsec = (tv)->tv_usec * 1000) |
| struct timeval tv; |
| int sec, nsec; |
| |
| sec = timeout_ms/1000; |
| nsec = (timeout_ms%1000) * 1000/*usec*/ * 1000/*nsec*/; |
| |
| gettimeofday(&tv, NULL); |
| ps_timeval2timespec(&tv, ts); |
| ts->tv_sec += sec; |
| ts->tv_nsec += nsec; |
| |
| if (ts->tv_nsec >= nsec_per_sec) { |
| ts->tv_sec++; |
| ts->tv_nsec -= nsec_per_sec; |
| } |
| return ts; |
| } |
| |
| static struct timespec *cmp_earlier_timespec(struct timespec *ts1, struct timespec *ts2) |
| { |
| if (ts1->tv_sec == ts2->tv_sec) { |
| if (ts1->tv_nsec < ts2->tv_nsec) |
| return ts1; |
| } |
| else if (ts1->tv_sec < ts2->tv_sec) |
| return ts1; |
| return ts2; |
| } |
| |
| #if defined(TIMER_ASSERT) |
| static void time_assert(struct timespec *ts) |
| { |
| struct timespec curr_ts; |
| |
| get_timespec_ms(&curr_ts, 0); |
| |
| assert(cmp_earlier_timespec(&curr_ts, ts) == ts); |
| } |
| #endif |
| |
| static void *timer_thread(void *thr_data) |
| { |
| pthread_timer_t *ptt = &pthread_timer; |
| struct list_head *head = &ptt->tt_head; |
| timer_obj_t *to; |
| void *data; |
| void (*callback)(void *data); |
| int ret; |
| |
| timer_func_in(); |
| |
| while (1) { |
| pthread_mutex_lock(&ptt->tt_lock); |
| |
| /* |
| start_timer is called, cond-lock is released, |
| but at that time, |
| if stop_timer is called before wake up cond-wait, the list becomes empty. |
| Thus, we use while loop instead of if. |
| */ |
| while (list_empty(head)) |
| ret = pthread_cond_wait(&ptt->tt_cond, &ptt->tt_lock); |
| |
| to = list_entry(head->next, timer_obj_t, to_list); |
| list_del_init(&to->to_list); |
| |
| ptt->tt_active = to; |
| ret = pthread_cond_timedwait(&ptt->tt_cond, &ptt->tt_lock, &to->to_ts); |
| ptt->tt_active = NULL; |
| if (ret == ETIMEDOUT && to->to_active) { |
| #if defined(TIMER_ASSERT) |
| time_assert(&to->to_ts); |
| #endif |
| callback = to->to_callback; |
| data = to->to_data; |
| pthread_mutex_lock(&to->to_lock); |
| } |
| else |
| callback = NULL; |
| pthread_mutex_unlock(&ptt->tt_lock); |
| |
| if (callback) { |
| timer_dprintf("+timer(%p) callback\n", to); |
| callback(data); |
| timer_dprintf("-timer callback\n"); |
| pthread_mutex_unlock(&to->to_lock); |
| } |
| } |
| |
| timer_func_out(); |
| return NULL; |
| } |
| |
| int init_timer(timer_obj_t *timer, void (*callback)(void *), void *data) |
| { |
| pthread_timer_t *ptt = &pthread_timer; |
| |
| if (!ptt->tt_inited) { |
| timer_eprintf("Timer module hasn't been initialized!!\n"); |
| return -1; |
| } |
| |
| timer_func_in("timer=%p, callback=%p, data=%p", timer, callback, data); |
| |
| if (!timer || !callback || !data) { |
| timer_eprintf("Invalid parameter\n"); |
| return -1; |
| } |
| |
| pthread_mutex_init(&timer->to_lock, NULL); |
| INIT_LIST_HEAD(&timer->to_list); |
| timer->to_callback = callback; |
| timer->to_data = data; |
| timer->to_active = 0; |
| timer->to_created = CREATED_MASK; |
| |
| timer_func_out(); |
| return 0; |
| } |
| |
| int start_timer(timer_obj_t *timer, int expire_milisec) |
| { |
| pthread_timer_t *ptt = &pthread_timer; |
| struct list_head *head = &ptt->tt_head; |
| timer_obj_t *pos; |
| struct timespec ts; |
| int ret = 0, inserted = 0; |
| |
| if (!ptt->tt_inited) { |
| timer_eprintf("Timer module hasn't been initialized!!\n"); |
| return -1; |
| } |
| |
| timer_func_in("timer=%p, expire_milisec=%d", timer, expire_milisec); |
| |
| get_timespec_ms(&ts, expire_milisec); |
| |
| pthread_mutex_lock(&ptt->tt_lock); |
| if (timer->to_created != CREATED_MASK) { |
| timer_eprintf("The timer was not to_created or deleted.\n"); |
| goto out; |
| } |
| if (pthread_self() != ptt->tt_thread) |
| pthread_mutex_lock(&timer->to_lock); |
| |
| memcpy(&timer->to_ts, &ts, sizeof(ts)); |
| timer->to_active = 1; |
| timer->to_caller = __builtin_return_address(0); |
| |
| if (list_empty(head)) |
| list_add(&timer->to_list, head); |
| else { |
| list_for_each_entry(pos, head, to_list) { |
| if (pos != timer) { |
| if (cmp_earlier_timespec(&timer->to_ts, &pos->to_ts) == &timer->to_ts) { |
| if (list_empty(&timer->to_list)) /*It isn't in list*/ |
| list_add(&timer->to_list, &pos->to_list); |
| else |
| list_move(&timer->to_list, &pos->to_list); |
| inserted = 1; |
| break; |
| } |
| } |
| } |
| |
| if (!inserted) { |
| if (list_empty(&timer->to_list)) /*It isn't in list*/ |
| list_add_tail(&timer->to_list, head); |
| else |
| list_move_tail(&timer->to_list, head); |
| } |
| } |
| |
| assert(!list_empty(&timer->to_list)); |
| |
| if (ptt->tt_active) { |
| if (ptt->tt_active == timer) |
| pthread_cond_signal(&ptt->tt_cond); |
| else if (list_entry(head->next, timer_obj_t, to_list) == timer) {/*is head?*/ |
| if (cmp_earlier_timespec(&timer->to_ts, &ptt->tt_active->to_ts) |
| == &timer->to_ts) { |
| list_add(&ptt->tt_active->to_list, &timer->to_list); |
| pthread_cond_signal(&ptt->tt_cond); |
| } |
| } |
| } |
| else if (list_entry(head->next, timer_obj_t, to_list) == timer) /*is head?*/ |
| pthread_cond_signal(&ptt->tt_cond); |
| |
| if (pthread_self() != ptt->tt_thread) |
| pthread_mutex_unlock(&timer->to_lock); |
| out: |
| pthread_mutex_unlock(&ptt->tt_lock); |
| timer_func_out("ret=%d", ret); |
| return ret; |
| } |
| |
| int stop_timer(timer_obj_t *timer) |
| { |
| pthread_timer_t *ptt = &pthread_timer; |
| int ret = 0; |
| |
| if (!ptt->tt_inited) { |
| timer_eprintf("Timer module hasn't been initialized!!\n"); |
| return -1; |
| } |
| |
| timer_func_in("timer=%p", timer); |
| |
| pthread_mutex_lock(&ptt->tt_lock); |
| if (timer->to_created != CREATED_MASK) { |
| timer_iprintf("The timer was not to_created or deleted.\n"); |
| goto out; |
| } |
| if (pthread_self() != ptt->tt_thread) |
| pthread_mutex_lock(&timer->to_lock); |
| |
| timer->to_active = 0; |
| |
| if (ptt->tt_active == timer) |
| pthread_cond_signal(&ptt->tt_cond); |
| else |
| list_del_init(&timer->to_list); |
| |
| if (pthread_self() != ptt->tt_thread) |
| pthread_mutex_unlock(&timer->to_lock); |
| out: |
| pthread_mutex_unlock(&ptt->tt_lock); |
| timer_func_out("ret=%d", ret); |
| return ret; |
| } |
| |
| int del_timer(timer_obj_t *timer) |
| { |
| int ret = 0; |
| |
| timer_func_in("timer=%p", timer); |
| |
| ret = stop_timer(timer); |
| timer->to_created = 0; |
| |
| timer_func_out("ret=%d", ret); |
| return ret; |
| } |
| |
| int timer_module_init(void) |
| { |
| pthread_timer_t *ptt = &pthread_timer; |
| |
| timer_func_in(); |
| |
| INIT_LIST_HEAD(&ptt->tt_head); |
| |
| pthread_mutex_init(&ptt->tt_lock, NULL); |
| pthread_cond_init(&ptt->tt_cond, NULL); |
| ptt->tt_inited = 1; |
| |
| pthread_create(&ptt->tt_thread, NULL, timer_thread, (void *) NULL); |
| |
| timer_func_out(); |
| return 0; |
| } |
| |
| void timer_module_deinit(void) |
| { |
| pthread_timer_t *ptt = &pthread_timer; |
| pthread_t thread; |
| |
| timer_func_in(); |
| |
| if ((thread = ptt->tt_thread)) { |
| ptt->tt_thread = (pthread_t) NULL; |
| pthread_cancel(thread); |
| pthread_join(thread, NULL); |
| } |
| |
| pthread_mutex_destroy(&ptt->tt_lock); |
| pthread_cond_destroy(&ptt->tt_cond); |
| ptt->tt_inited = 0; |
| |
| timer_func_out(); |
| } |
| #else |
| #include <signal.h> |
| |
| #define SIGNO_TIMER SIGRTMIN |
| |
| static void timer_callback(int signum, siginfo_t *si, void *sv) |
| { |
| timer_obj_t *timer = si->_sifields._rt.si_sigval.sival_ptr; |
| void (*callback)(void *data) = timer->to_callback; |
| |
| assert(signum == SIGNO_TIMER); |
| |
| timer_func_in(); |
| if (callback) { |
| timer_dprintf("+timer callback\n"); |
| callback(timer->to_data); |
| timer_dprintf("-timer callback\n"); |
| } |
| timer_func_out(); |
| } |
| |
| int init_timer(timer_obj_t *timer, void (*callback)(void *), void *data) |
| { |
| struct sigaction sa_rt; |
| struct sigevent sigev; |
| |
| timer_func_in("timer=%p, callback=%p, data=%p\n", timer, callback, data); |
| |
| if (!timer || !callback || !data) { |
| timer_eprintf("Invalid parameter\n"); |
| return -1; |
| } |
| |
| memset(&sa_rt, 0, sizeof(sa_rt)); |
| sigemptyset(&sa_rt.sa_mask); |
| sa_rt.sa_sigaction = timer_callback; |
| sa_rt.sa_flags = SA_SIGINFO | SA_RESTART; |
| |
| if (sigaction(SIGNO_TIMER, &sa_rt, NULL) == -1) { |
| timer_std_eprintf("sigaction\n"); |
| return -1; |
| } |
| |
| timer->to_callback = callback; |
| timer->to_data = data; |
| memset(&sigev, 0, sizeof(sigev)); |
| sigev.sigev_value.sival_ptr = timer; |
| sigev.sigev_notify = SIGEV_SIGNAL; /* notification with signal */ |
| sigev.sigev_signo = SIGNO_TIMER; |
| |
| /* create timer */ |
| if (timer_create(CLOCK_REALTIME, &sigev, (timer_t *)&timer->to_id) < 0) { |
| timer_std_eprintf("timer_create\n"); |
| return -1; |
| } |
| |
| timer->to_created = CREATED_MASK; |
| timer_func_out("id=%p", timer->to_id); |
| return 0; |
| } |
| |
| int start_timer(timer_obj_t *timer, int expire_milisec) |
| { |
| struct itimerspec rt_itspec; |
| int second, nano_sec; |
| int ret = 0; |
| |
| timer_func_in("timer=%p, expire_milisec=%d", timer, expire_milisec); |
| |
| if (timer->to_created != CREATED_MASK) { |
| timer_iprintf("The timer was not to_created or deleted.\n"); |
| goto out; |
| } |
| |
| /* interval timer setting */ |
| second = expire_milisec/1000; |
| nano_sec = (expire_milisec%1000)*1000/*us*/*1000/*ns*/; |
| rt_itspec.it_value.tv_sec = second; |
| rt_itspec.it_value.tv_nsec = nano_sec; |
| /*We do not repeat timer.*/ |
| rt_itspec.it_interval.tv_sec = 0; |
| rt_itspec.it_interval.tv_nsec = 0; |
| |
| if (timer_settime((timer_t)timer->to_id, 0, &rt_itspec, NULL) < 0) { |
| timer_std_eprintf("timer_settime(%p)\n", timer->to_id); |
| timer_delete((timer_t)timer->to_id); |
| ret = -1; |
| } |
| out: |
| timer_func_out("ret=%d", ret); |
| return ret; |
| } |
| |
| int del_timer(timer_obj_t *timer) |
| { |
| int ret = 0; |
| |
| timer_func_in("timer=%p, id=%p", timer, timer->to_id); |
| |
| if (timer->to_created != CREATED_MASK) { |
| timer_eprintf("Invalid timer parameter\n"); |
| ret = -1; |
| } |
| else if (timer->to_id) { |
| timer->to_created = 0; |
| ret = timer_delete((timer_t) timer->to_id); |
| } |
| |
| timer_func_out("ret=%d", ret); |
| return ret; |
| } |
| #endif |
| |