blob: e04544192f0f60539f0a4b9c9e6e5ab3e7597df8 [file] [log] [blame]
Harald Welte987f59a2017-02-04 12:34:35 +01001/*
2 * (C) 2008,2009 by Holger Hans Peter Freyther <zecke@selfish.org>
3 * (C) 2011 by Harald Welte <laforge@gnumonks.org>
4 * All Rights Reserved
5 *
6 * Authors: Holger Hans Peter Freyther <zecke@selfish.org>
7 * Harald Welte <laforge@gnumonks.org>
8 * Pablo Neira Ayuso <pablo@gnumonks.org>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 *
24 */
25
26
27/*! \addtogroup timer
28 * @{
29 */
30
31/*! \file timer.c
32 */
33
34#include <assert.h>
35#include <limits.h>
36#include "osmocom/core/linuxlist.h"
37#include "osmocom/core/timer.h"
38
39/* These store the amount of time that we wait until next timer expires. */
40static struct osmo_timeval nearest;
41static struct osmo_timeval *nearest_p;
42
43static struct rb_root timer_root = RB_ROOT;
44
45static void __add_timer(struct osmo_timer_list *timer)
46{
47 struct rb_node **new = &(timer_root.rb_node);
48 struct rb_node *parent = NULL;
49
50 while (*new) {
51 struct osmo_timer_list *this;
52
53 this = container_of(*new, struct osmo_timer_list, node);
54
55 parent = *new;
56 if (timercmp(&timer->timeout, &this->timeout, <))
57 new = &((*new)->rb_left);
58 else
59 new = &((*new)->rb_right);
60 }
61
62 rb_link_node(&timer->node, parent, new);
63 rb_insert_color(&timer->node, &timer_root);
64}
65
66/*! \brief add a new timer to the timer management
67 * \param[in] timer the timer that should be added
68 */
69void osmo_timer_add(struct osmo_timer_list *timer)
70{
71 osmo_timer_del(timer);
72 timer->active = 1;
73 INIT_LLIST_HEAD(&timer->list);
74 __add_timer(timer);
75}
76
77/*! \brief schedule a timer at a given future relative time
78 * \param[in] timer the to-be-added timer
79 * \param[in] seconds number of seconds from now
80 * \param[in] microseconds number of microseconds from now
81 *
82 * This function can be used to (re-)schedule a given timer at a
83 * specified number of seconds+microseconds in the future. It will
84 * internally add it to the timer management data structures, thus
85 * osmo_timer_add() is automatically called.
86 */
87void
88osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds)
89{
90 struct osmo_timeval current_time;
91
92 osmo_gettimeofday(&current_time, NULL);
93#if 0
94 timer->timeout.tv_sec = seconds;
95 timer->timeout.tv_usec = microseconds;
96#else
97 timer->timeout.expires = (seconds*1000) + (microseconds/1000);
98#endif
99 timeradd(&timer->timeout, &current_time, &timer->timeout);
100 osmo_timer_add(timer);
101}
102
103/*! \brief delete a timer from timer management
104 * \param[in] timer the to-be-deleted timer
105 *
106 * This function can be used to delete a previously added/scheduled
107 * timer from the timer management code.
108 */
109void osmo_timer_del(struct osmo_timer_list *timer)
110{
111 if (timer->active) {
112 timer->active = 0;
113 rb_erase(&timer->node, &timer_root);
114 /* make sure this is not already scheduled for removal. */
115 if (!llist_empty(&timer->list))
116 llist_del_init(&timer->list);
117 }
118}
119
120/*! \brief check if given timer is still pending
121 * \param[in] timer the to-be-checked timer
122 * \return 1 if pending, 0 otherwise
123 *
124 * This function can be used to determine whether a given timer
125 * has alredy expired (returns 0) or is still pending (returns 1)
126 */
127int osmo_timer_pending(struct osmo_timer_list *timer)
128{
129 return timer->active;
130}
131
132/*! \brief compute the remaining time of a timer
133 * \param[in] timer the to-be-checked timer
134 * \param[in] now the current time (NULL if not known)
135 * \param[out] remaining remaining time until timer fires
136 * \return 0 if timer has not expired yet, -1 if it has
137 *
138 * This function can be used to determine the amount of time
139 * remaining until the expiration of the timer.
140 */
141int osmo_timer_remaining(const struct osmo_timer_list *timer,
142 const struct osmo_timeval *now,
143 struct osmo_timeval *remaining)
144{
145 struct osmo_timeval current_time;
146
147 if (!now)
148 osmo_gettimeofday(&current_time, NULL);
149 else
150 current_time = *now;
151
152 timersub(&timer->timeout, &current_time, remaining);
153
154#if 0
155 if (remaining->tv_sec < 0)
156#else
157 if (remaining->expires < 0)
158#endif
159 return -1;
160
161 return 0;
162}
163
164/*! \brief Determine time between now and the nearest timer
165 * \returns pointer to osmo_timeval of nearest timer, NULL if there is none
166 *
167 * if we have a nearest time return the delta between the current
168 * time and the time of the nearest timer.
169 * If the nearest timer timed out return NULL and then we will
170 * dispatch everything after the select
171 */
172struct osmo_timeval *osmo_timers_nearest(void)
173{
174 /* nearest_p is exactly what we need already: NULL if nothing is
175 * waiting, {0,0} if we must dispatch immediately, and the correct
176 * delay if we need to wait */
177 return nearest_p;
178}
179
180static void update_nearest(struct osmo_timeval *cand, struct osmo_timeval *current)
181{
182#if 0
183 if (cand->tv_sec != LONG_MAX) {
184#else
185 if (cand->expires != LONG_MAX) {
186#endif
187 if (timercmp(cand, current, >))
188 timersub(cand, current, &nearest);
189 else {
190 /* loop again inmediately */
191 timerclear(&nearest);
192 }
193 nearest_p = &nearest;
194 } else {
195 nearest_p = NULL;
196 }
197}
198
199/*! \brief Find the nearest time and update nearest_p */
200void osmo_timers_prepare(void)
201{
202 struct rb_node *node;
203 struct osmo_timeval current;
204
205 osmo_gettimeofday(&current, NULL);
206
207 node = rb_first(&timer_root);
208 if (node) {
209 struct osmo_timer_list *this;
210 this = container_of(node, struct osmo_timer_list, node);
211 update_nearest(&this->timeout, &current);
212 } else {
213 nearest_p = NULL;
214 }
215}
216
217/*! \brief fire all timers... and remove them */
218int osmo_timers_update(void)
219{
220 struct osmo_timeval current_time;
221 struct rb_node *node;
222 struct llist_head timer_eviction_list;
223 struct osmo_timer_list *this;
224 int work = 0;
225
226 osmo_gettimeofday(&current_time, NULL);
227
228 INIT_LLIST_HEAD(&timer_eviction_list);
229 for (node = rb_first(&timer_root); node; node = rb_next(node)) {
230 this = container_of(node, struct osmo_timer_list, node);
231
232 if (timercmp(&this->timeout, &current_time, >))
233 break;
234
235 llist_add(&this->list, &timer_eviction_list);
236 }
237
238 /*
239 * The callbacks might mess with our list and in this case
240 * even llist_for_each_entry_safe is not safe to use. To allow
241 * osmo_timer_del to be called from within the callback we need
242 * to restart the iteration for each element scheduled for removal.
243 *
244 * The problematic scenario is the following: Given two timers A
245 * and B that have expired at the same time. Thus, they are both
246 * in the eviction list in this order: A, then B. If we remove
247 * timer B from the A's callback, we continue with B in the next
248 * iteration step, leading to an access-after-release.
249 */
250restart:
251 llist_for_each_entry(this, &timer_eviction_list, list) {
252 osmo_timer_del(this);
253 if (this->cb)
254 this->cb(this->data);
255 work = 1;
256 goto restart;
257 }
258
259 return work;
260}
261
262/*! \brief Check how many timers we have in the system
263 * \returns number of \ref osmo_timer_list registered */
264int osmo_timers_check(void)
265{
266 struct rb_node *node;
267 int i = 0;
268
269 for (node = rb_first(&timer_root); node; node = rb_next(node)) {
270 i++;
271 }
272 return i;
273}
274
275extern volatile unsigned long jiffies;
276int osmo_gettimeofday(struct osmo_timeval *tv, struct timezone *tz)
277{
278 tv->expires = jiffies;
279 return 0;
280}
281
282/*! @} */