/* * Copyright (c) 2006-2007, 2009 Red Hat, Inc. * * Author: Steven Dake * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #ifndef QB_TLIST_H_DEFINED #define QB_TLIST_H_DEFINED #include "os_base.h" #include #include #include #ifndef TIMER_HANDLE typedef void *timer_handle; #define TIMER_HANDLE #endif static int64_t timerlist_hertz; struct timerlist { struct qb_list_head timer_head; }; struct timerlist_timer { struct qb_list_head list; uint64_t expire_time; int32_t is_absolute_timer; void (*timer_fn) (void *data); void *data; timer_handle handle_addr; }; static inline void timerlist_init(struct timerlist *timerlist) { qb_list_init(&timerlist->timer_head); timerlist_hertz = qb_util_nano_monotonic_hz(); } static inline void timerlist_add(struct timerlist *timerlist, struct timerlist_timer *timer) { struct qb_list_head *timer_list = 0; struct timerlist_timer *timer_from_list; int32_t found = QB_FALSE; qb_list_for_each(timer_list, &timerlist->timer_head) { timer_from_list = qb_list_entry(timer_list, struct timerlist_timer, list); if (timer_from_list->expire_time > timer->expire_time) { qb_list_add_tail(&timer->list, timer_list); found = QB_TRUE; break; /* for timer iteration */ } } if (found == QB_FALSE) { qb_list_add_tail(&timer->list, &timerlist->timer_head); } } static inline int32_t timerlist_add_duration(struct timerlist *timerlist, void (*timer_fn) (void *data), void *data, uint64_t nano_duration, timer_handle * handle) { struct timerlist_timer *timer; timer = (struct timerlist_timer *)malloc(sizeof(struct timerlist_timer)); if (timer == 0) { return -ENOMEM; } timer->expire_time = qb_util_nano_current_get() + nano_duration; timer->is_absolute_timer = QB_FALSE; timer->data = data; timer->timer_fn = timer_fn; timer->handle_addr = handle; timerlist_add(timerlist, timer); *handle = timer; return (0); } static inline void timerlist_del(struct timerlist *timerlist, timer_handle _timer_handle) { struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle; memset(timer->handle_addr, 0, sizeof(struct timerlist_timer *)); qb_list_del(&timer->list); qb_list_init(&timer->list); free(timer); } static inline uint64_t timerlist_expire_time(struct timerlist *timerlist, timer_handle _timer_handle) { struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle; return (timer->expire_time); } static inline void timerlist_pre_dispatch(struct timerlist *timerlist, timer_handle _timer_handle) { struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle; memset(timer->handle_addr, 0, sizeof(struct timerlist_timer *)); qb_list_del(&timer->list); qb_list_init(&timer->list); } static inline void timerlist_post_dispatch(struct timerlist *timerlist, timer_handle _timer_handle) { struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle; free(timer); } /* * returns the number of msec until the next timer will expire for use with poll */ static inline uint64_t timerlist_msec_duration_to_expire(struct timerlist *timerlist) { struct timerlist_timer *timer_from_list; volatile uint64_t current_time; volatile uint64_t msec_duration_to_expire; /* * empty list, no expire */ if (qb_list_empty(&timerlist->timer_head)) { return (-1); } timer_from_list = qb_list_first_entry(&timerlist->timer_head, struct timerlist_timer, list); if (timer_from_list->is_absolute_timer) { current_time = qb_util_nano_from_epoch_get(); } else { current_time = qb_util_nano_current_get(); } /* * timer at head of list is expired, zero msecs required */ if (timer_from_list->expire_time < current_time) { return (0); } msec_duration_to_expire = ((timer_from_list->expire_time - current_time) / QB_TIME_NS_IN_MSEC) + (1000 / timerlist_hertz); return (msec_duration_to_expire); } /* * Expires any timers that should be expired */ static inline void timerlist_expire(struct timerlist *timerlist) { struct timerlist_timer *timer_from_list; struct qb_list_head *pos; struct qb_list_head *next; uint64_t current_time_from_epoch; uint64_t current_monotonic_time; uint64_t current_time; current_monotonic_time = qb_util_nano_current_get(); current_time_from_epoch = qb_util_nano_from_epoch_get(); qb_list_for_each_safe(pos, next, &timerlist->timer_head) { timer_from_list = qb_list_entry(pos, struct timerlist_timer, list); current_time = (timer_from_list-> is_absolute_timer ? current_time_from_epoch : current_monotonic_time); if (timer_from_list->expire_time < current_time) { timerlist_pre_dispatch(timerlist, timer_from_list); timer_from_list->timer_fn(timer_from_list->data); timerlist_post_dispatch(timerlist, timer_from_list); } else { break; /* for timer iteration */ } } } #endif /* QB_TLIST_H_DEFINED */