blob: ed2b2963279dffce2b11c19f0bed0df7a5f67c31 [file] [log] [blame]
Harald Welteec8b4502010-02-20 20:34:29 +01001/*
2 * (C) 2008,2009 by Holger Hans Peter Freyther <zecke@selfish.org>
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 */
20
Harald Welteba6988b2011-08-17 12:46:48 +020021/*! \addtogroup timer
22 * @{
23 */
24
25/*! \file timer.c
26 */
27
Harald Welteec8b4502010-02-20 20:34:29 +010028#include <assert.h>
29#include <string.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010030#include <osmocom/core/timer.h>
Harald Welteec8b4502010-02-20 20:34:29 +010031
32static LLIST_HEAD(timer_list);
33static struct timeval s_nearest_time;
34static struct timeval s_select_time;
35
36#define MICRO_SECONDS 1000000LL
37
38#define TIME_SMALLER(left, right) \
39 (left.tv_sec*MICRO_SECONDS+left.tv_usec) <= (right.tv_sec*MICRO_SECONDS+right.tv_usec)
40
Harald Welteba6988b2011-08-17 12:46:48 +020041
42/*! \brief add a new timer to the timer management
43 * \param[in] timer the timer that should be added
44 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020045void osmo_timer_add(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +010046{
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020047 struct osmo_timer_list *list_timer;
Harald Welteec8b4502010-02-20 20:34:29 +010048
49 /* TODO: Optimize and remember the closest item... */
50 timer->active = 1;
51
52 /* this might be called from within update_timers */
53 llist_for_each_entry(list_timer, &timer_list, entry)
54 if (timer == list_timer)
55 return;
56
57 timer->in_list = 1;
58 llist_add(&timer->entry, &timer_list);
59}
60
Harald Welteba6988b2011-08-17 12:46:48 +020061/*! \brief schedule a timer at a given future relative time
62 * \param[in] timer the to-be-added timer
63 * \param[in] seconds number of seconds from now
64 * \param[in] microseconds number of microseconds from now
65 *
66 * This function can be used to (re-)schedule a given timer at a
67 * specified number of seconds+microseconds in the future. It will
68 * internally add it to the timer management data structures, thus
69 * osmo_timer_add() is automatically called.
70 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020071void
72osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds)
Harald Welteec8b4502010-02-20 20:34:29 +010073{
74 struct timeval current_time;
75
76 gettimeofday(&current_time, NULL);
77 unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
78 currentTime += seconds * MICRO_SECONDS + microseconds;
79 timer->timeout.tv_sec = currentTime / MICRO_SECONDS;
80 timer->timeout.tv_usec = currentTime % MICRO_SECONDS;
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020081 osmo_timer_add(timer);
Harald Welteec8b4502010-02-20 20:34:29 +010082}
83
Harald Welteba6988b2011-08-17 12:46:48 +020084/*! \brief delete a timer from timer management
85 * \param[in] timer the to-be-deleted timer
86 *
87 * This function can be used to delete a previously added/scheduled
88 * timer from the timer management code.
89 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020090void osmo_timer_del(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +010091{
92 if (timer->in_list) {
93 timer->active = 0;
94 timer->in_list = 0;
95 llist_del(&timer->entry);
96 }
97}
98
Harald Welteba6988b2011-08-17 12:46:48 +020099/*! \brief check if given timer is still pending
100 * \param[in] timer the to-be-checked timer
101 * \return 1 if pending, 0 otherwise
102 *
103 * This function can be used to determine whether a given timer
104 * has alredy expired (returns 0) or is still pending (returns 1)
105 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200106int osmo_timer_pending(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +0100107{
108 return timer->active;
109}
110
111/*
112 * if we have a nearest time return the delta between the current
113 * time and the time of the nearest timer.
114 * If the nearest timer timed out return NULL and then we will
115 * dispatch everything after the select
116 */
Harald Welte7e820202011-07-16 10:15:16 +0200117struct timeval *osmo_timers_nearest(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100118{
119 struct timeval current_time;
120
121 if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0)
122 return NULL;
123
124 if (gettimeofday(&current_time, NULL) == -1)
125 return NULL;
126
127 unsigned long long nearestTime = s_nearest_time.tv_sec * MICRO_SECONDS + s_nearest_time.tv_usec;
128 unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
129
130 if (nearestTime < currentTime) {
131 s_select_time.tv_sec = 0;
132 s_select_time.tv_usec = 0;
133 } else {
134 s_select_time.tv_sec = (nearestTime - currentTime) / MICRO_SECONDS;
135 s_select_time.tv_usec = (nearestTime - currentTime) % MICRO_SECONDS;
136 }
137
138 return &s_select_time;
139}
140
141/*
142 * Find the nearest time and update s_nearest_time
143 */
Harald Welte7e820202011-07-16 10:15:16 +0200144void osmo_timers_prepare(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100145{
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200146 struct osmo_timer_list *timer, *nearest_timer = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +0100147 llist_for_each_entry(timer, &timer_list, entry) {
148 if (!nearest_timer || TIME_SMALLER(timer->timeout, nearest_timer->timeout)) {
149 nearest_timer = timer;
150 }
151 }
152
153 if (nearest_timer) {
154 s_nearest_time = nearest_timer->timeout;
155 } else {
156 memset(&s_nearest_time, 0, sizeof(struct timeval));
157 }
158}
159
160/*
161 * fire all timers... and remove them
162 */
Harald Welte7e820202011-07-16 10:15:16 +0200163int osmo_timers_update(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100164{
165 struct timeval current_time;
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200166 struct osmo_timer_list *timer, *tmp;
Harald Welteec8b4502010-02-20 20:34:29 +0100167 int work = 0;
168
169 gettimeofday(&current_time, NULL);
170
171 /*
172 * The callbacks might mess with our list and in this case
173 * even llist_for_each_entry_safe is not safe to use. To allow
174 * del_timer, add_timer, schedule_timer to be called from within
175 * the callback we jump through some loops.
176 *
177 * First we set the handled flag of each active timer to zero,
178 * then we iterate over the list and execute the callbacks. As the
179 * list might have been changed (specially the next) from within
180 * the callback we have to start over again. Once every callback
181 * is dispatched we will remove the non-active from the list.
182 *
183 * TODO: If this is a performance issue we can poison a global
184 * variable in add_timer and del_timer and only then restart.
185 */
186 llist_for_each_entry(timer, &timer_list, entry) {
187 timer->handled = 0;
188 }
189
190restart:
191 llist_for_each_entry(timer, &timer_list, entry) {
192 if (!timer->handled && TIME_SMALLER(timer->timeout, current_time)) {
193 timer->handled = 1;
194 timer->active = 0;
195 (*timer->cb)(timer->data);
196 work = 1;
197 goto restart;
198 }
199 }
200
201 llist_for_each_entry_safe(timer, tmp, &timer_list, entry) {
202 timer->handled = 0;
203 if (!timer->active) {
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200204 osmo_timer_del(timer);
Harald Welteec8b4502010-02-20 20:34:29 +0100205 }
206 }
207
208 return work;
209}
210
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200211int osmo_timers_check(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100212{
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200213 struct osmo_timer_list *timer;
Harald Welteec8b4502010-02-20 20:34:29 +0100214 int i = 0;
215
216 llist_for_each_entry(timer, &timer_list, entry) {
217 i++;
218 }
219 return i;
220}
Harald Welteba6988b2011-08-17 12:46:48 +0200221
222/*! }@ */