blob: 8e4953ed92b15127c52ec13ff5ee106224547a0b [file] [log] [blame]
// 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