blob: e18195a789ebf7dc61d2479eadef0d26dbdc6dd4 [file] [log] [blame]
Harald Welteec8b4502010-02-20 20:34:29 +01001/*
2 * (C) 2008,2009 by Holger Hans Peter Freyther <zecke@selfish.org>
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +02003 * (C) 2011 by Harald Welte <laforge@gnumonks.org>
Harald Welteec8b4502010-02-20 20:34:29 +01004 * All Rights Reserved
5 *
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +02006 * Authors: Holger Hans Peter Freyther <zecke@selfish.org>
7 * Harald Welte <laforge@gnumonks.org>
8 * Pablo Neira Ayuso <pablo@gnumonks.org>
9 *
Harald Welteec8b4502010-02-20 20:34:29 +010010 * 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
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020026
Harald Welteba6988b2011-08-17 12:46:48 +020027/*! \addtogroup timer
28 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020029 * Osmocom timer abstraction; modelled after linux kernel timers
Harald Welteba6988b2011-08-17 12:46:48 +020030 */
31
Harald Welte96e2a002017-06-12 21:44:18 +020032/*! \file timer.c */
Harald Welteba6988b2011-08-17 12:46:48 +020033
Harald Welteec8b4502010-02-20 20:34:29 +010034#include <assert.h>
35#include <string.h>
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020036#include <limits.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010037#include <osmocom/core/timer.h>
Sylvain Munaut07f11032011-10-21 21:55:29 +020038#include <osmocom/core/timer_compat.h>
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020039#include <osmocom/core/linuxlist.h>
Harald Welteec8b4502010-02-20 20:34:29 +010040
Holger Hans Peter Freyther3f838b72015-08-20 18:17:15 +000041/* These store the amount of time that we wait until next timer expires. */
42static struct timeval nearest;
43static struct timeval *nearest_p;
44
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020045static struct rb_root timer_root = RB_ROOT;
Harald Welteec8b4502010-02-20 20:34:29 +010046
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020047static void __add_timer(struct osmo_timer_list *timer)
48{
49 struct rb_node **new = &(timer_root.rb_node);
50 struct rb_node *parent = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +010051
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020052 while (*new) {
53 struct osmo_timer_list *this;
Harald Welteec8b4502010-02-20 20:34:29 +010054
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020055 this = container_of(*new, struct osmo_timer_list, node);
56
57 parent = *new;
58 if (timercmp(&timer->timeout, &this->timeout, <))
59 new = &((*new)->rb_left);
60 else
61 new = &((*new)->rb_right);
62 }
63
64 rb_link_node(&timer->node, parent, new);
65 rb_insert_color(&timer->node, &timer_root);
66}
Harald Welteba6988b2011-08-17 12:46:48 +020067
Neels Hofmeyr87e45502017-06-20 00:17:59 +020068/*! set up timer callback and data
Pablo Neira Ayuso44f423f2017-05-08 18:00:28 +020069 * \param[in] timer the timer that should be added
70 * \param[in] callback function to be called when timer expires
71 * \param[in] pointer to data that passed to the callback function
72 */
73void osmo_timer_setup(struct osmo_timer_list *timer, void (*cb)(void *data),
74 void *data)
75{
76 timer->cb = cb;
77 timer->data = data;
78}
79
Neels Hofmeyr87e45502017-06-20 00:17:59 +020080/*! add a new timer to the timer management
Harald Welteba6988b2011-08-17 12:46:48 +020081 * \param[in] timer the timer that should be added
82 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020083void osmo_timer_add(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +010084{
Pablo Neira Ayusoa71b8ea2011-11-13 10:11:31 +010085 osmo_timer_del(timer);
86 timer->active = 1;
87 INIT_LLIST_HEAD(&timer->list);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020088 __add_timer(timer);
Harald Welteec8b4502010-02-20 20:34:29 +010089}
90
Neels Hofmeyr87e45502017-06-20 00:17:59 +020091/*! schedule a timer at a given future relative time
Harald Welteba6988b2011-08-17 12:46:48 +020092 * \param[in] timer the to-be-added timer
93 * \param[in] seconds number of seconds from now
94 * \param[in] microseconds number of microseconds from now
95 *
96 * This function can be used to (re-)schedule a given timer at a
97 * specified number of seconds+microseconds in the future. It will
98 * internally add it to the timer management data structures, thus
99 * osmo_timer_add() is automatically called.
100 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200101void
102osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds)
Harald Welteec8b4502010-02-20 20:34:29 +0100103{
104 struct timeval current_time;
105
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200106 osmo_gettimeofday(&current_time, NULL);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200107 timer->timeout.tv_sec = seconds;
108 timer->timeout.tv_usec = microseconds;
109 timeradd(&timer->timeout, &current_time, &timer->timeout);
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200110 osmo_timer_add(timer);
Harald Welteec8b4502010-02-20 20:34:29 +0100111}
112
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200113/*! delete a timer from timer management
Harald Welteba6988b2011-08-17 12:46:48 +0200114 * \param[in] timer the to-be-deleted timer
115 *
116 * This function can be used to delete a previously added/scheduled
117 * timer from the timer management code.
118 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200119void osmo_timer_del(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +0100120{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200121 if (timer->active) {
Harald Welteec8b4502010-02-20 20:34:29 +0100122 timer->active = 0;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200123 rb_erase(&timer->node, &timer_root);
124 /* make sure this is not already scheduled for removal. */
125 if (!llist_empty(&timer->list))
126 llist_del_init(&timer->list);
Harald Welteec8b4502010-02-20 20:34:29 +0100127 }
128}
129
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200130/*! check if given timer is still pending
Harald Welteba6988b2011-08-17 12:46:48 +0200131 * \param[in] timer the to-be-checked timer
132 * \return 1 if pending, 0 otherwise
133 *
134 * This function can be used to determine whether a given timer
135 * has alredy expired (returns 0) or is still pending (returns 1)
136 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200137int osmo_timer_pending(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +0100138{
139 return timer->active;
140}
141
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200142/*! compute the remaining time of a timer
Harald Weltee30b6ac2012-07-13 12:21:04 +0200143 * \param[in] timer the to-be-checked timer
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100144 * \param[in] now the current time (NULL if not known)
Harald Weltee30b6ac2012-07-13 12:21:04 +0200145 * \param[out] remaining remaining time until timer fires
146 * \return 0 if timer has not expired yet, -1 if it has
147 *
148 * This function can be used to determine the amount of time
149 * remaining until the expiration of the timer.
150 */
151int osmo_timer_remaining(const struct osmo_timer_list *timer,
152 const struct timeval *now,
153 struct timeval *remaining)
154{
155 struct timeval current_time;
156
Holger Hans Peter Freyther8e5435a2014-05-23 08:37:02 +0200157 if (!now)
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200158 osmo_gettimeofday(&current_time, NULL);
Holger Hans Peter Freyther8e5435a2014-05-23 08:37:02 +0200159 else
160 current_time = *now;
Harald Weltee30b6ac2012-07-13 12:21:04 +0200161
162 timersub(&timer->timeout, &current_time, remaining);
163
164 if (remaining->tv_sec < 0)
165 return -1;
166
167 return 0;
168}
169
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200170/*! Determine time between now and the nearest timer
Harald Weltede6e4982012-12-06 21:25:27 +0100171 * \returns pointer to timeval of nearest timer, NULL if there is none
172 *
Harald Welteec8b4502010-02-20 20:34:29 +0100173 * if we have a nearest time return the delta between the current
174 * time and the time of the nearest timer.
175 * If the nearest timer timed out return NULL and then we will
176 * dispatch everything after the select
177 */
Harald Welte7e820202011-07-16 10:15:16 +0200178struct timeval *osmo_timers_nearest(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100179{
Sylvain Munaut0061ded2011-10-18 20:11:03 +0200180 /* nearest_p is exactly what we need already: NULL if nothing is
181 * waiting, {0,0} if we must dispatch immediately, and the correct
182 * delay if we need to wait */
183 return nearest_p;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200184}
Harald Welteec8b4502010-02-20 20:34:29 +0100185
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200186static void update_nearest(struct timeval *cand, struct timeval *current)
187{
188 if (cand->tv_sec != LONG_MAX) {
189 if (timercmp(cand, current, >))
190 timersub(cand, current, &nearest);
191 else {
192 /* loop again inmediately */
Harald Welte00476022017-05-15 14:29:13 +0200193 timerclear(&nearest);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200194 }
195 nearest_p = &nearest;
Harald Welteec8b4502010-02-20 20:34:29 +0100196 } else {
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200197 nearest_p = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +0100198 }
Harald Welteec8b4502010-02-20 20:34:29 +0100199}
200
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200201/*! Find the nearest time and update nearest_p */
Harald Welte7e820202011-07-16 10:15:16 +0200202void osmo_timers_prepare(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100203{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200204 struct rb_node *node;
205 struct timeval current;
Harald Welteec8b4502010-02-20 20:34:29 +0100206
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200207 osmo_gettimeofday(&current, NULL);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200208
209 node = rb_first(&timer_root);
210 if (node) {
211 struct osmo_timer_list *this;
212 this = container_of(node, struct osmo_timer_list, node);
213 update_nearest(&this->timeout, &current);
Harald Welteec8b4502010-02-20 20:34:29 +0100214 } else {
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200215 nearest_p = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +0100216 }
217}
218
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200219/*! fire all timers... and remove them */
Harald Welte7e820202011-07-16 10:15:16 +0200220int osmo_timers_update(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100221{
222 struct timeval current_time;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200223 struct rb_node *node;
224 struct llist_head timer_eviction_list;
225 struct osmo_timer_list *this;
Harald Welteec8b4502010-02-20 20:34:29 +0100226 int work = 0;
227
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200228 osmo_gettimeofday(&current_time, NULL);
Harald Welteec8b4502010-02-20 20:34:29 +0100229
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200230 INIT_LLIST_HEAD(&timer_eviction_list);
231 for (node = rb_first(&timer_root); node; node = rb_next(node)) {
232 this = container_of(node, struct osmo_timer_list, node);
233
234 if (timercmp(&this->timeout, &current_time, >))
235 break;
236
237 llist_add(&this->list, &timer_eviction_list);
238 }
239
Harald Welteec8b4502010-02-20 20:34:29 +0100240 /*
241 * The callbacks might mess with our list and in this case
242 * even llist_for_each_entry_safe is not safe to use. To allow
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200243 * osmo_timer_del to be called from within the callback we need
244 * to restart the iteration for each element scheduled for removal.
Harald Welteec8b4502010-02-20 20:34:29 +0100245 *
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200246 * The problematic scenario is the following: Given two timers A
247 * and B that have expired at the same time. Thus, they are both
248 * in the eviction list in this order: A, then B. If we remove
249 * timer B from the A's callback, we continue with B in the next
250 * iteration step, leading to an access-after-release.
Harald Welteec8b4502010-02-20 20:34:29 +0100251 */
Harald Welteec8b4502010-02-20 20:34:29 +0100252restart:
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200253 llist_for_each_entry(this, &timer_eviction_list, list) {
254 osmo_timer_del(this);
Alexander Couzensec9bd522016-11-28 23:22:14 +0100255 if (this->cb)
256 this->cb(this->data);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200257 work = 1;
258 goto restart;
Harald Welteec8b4502010-02-20 20:34:29 +0100259 }
260
261 return work;
262}
263
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200264/*! Check how many timers we have in the system
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200265 * \returns number of \ref osmo_timer_list registered */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200266int osmo_timers_check(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100267{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200268 struct rb_node *node;
Harald Welteec8b4502010-02-20 20:34:29 +0100269 int i = 0;
270
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200271 for (node = rb_first(&timer_root); node; node = rb_next(node)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100272 i++;
273 }
274 return i;
275}
Harald Welteba6988b2011-08-17 12:46:48 +0200276
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200277/*! @} */