blob: 02160a103b909996df350eb03b23b21da6476e4d [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 Weltee08da972017-11-13 01:00:26 +090010 * SPDX-License-Identifier: GPL-2.0+
11 *
Harald Welteec8b4502010-02-20 20:34:29 +010012 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 *
26 */
27
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020028
Harald Welteba6988b2011-08-17 12:46:48 +020029/*! \addtogroup timer
30 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020031 * Osmocom timer abstraction; modelled after linux kernel timers
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020032 *
33 * \file timer.c */
Harald Welteba6988b2011-08-17 12:46:48 +020034
Harald Welteec8b4502010-02-20 20:34:29 +010035#include <assert.h>
36#include <string.h>
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020037#include <limits.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010038#include <osmocom/core/timer.h>
Sylvain Munaut07f11032011-10-21 21:55:29 +020039#include <osmocom/core/timer_compat.h>
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020040#include <osmocom/core/linuxlist.h>
Harald Welteec8b4502010-02-20 20:34:29 +010041
Holger Hans Peter Freyther3f838b72015-08-20 18:17:15 +000042/* These store the amount of time that we wait until next timer expires. */
43static struct timeval nearest;
44static struct timeval *nearest_p;
45
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020046static struct rb_root timer_root = RB_ROOT;
Harald Welteec8b4502010-02-20 20:34:29 +010047
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020048static void __add_timer(struct osmo_timer_list *timer)
49{
50 struct rb_node **new = &(timer_root.rb_node);
51 struct rb_node *parent = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +010052
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020053 while (*new) {
54 struct osmo_timer_list *this;
Harald Welteec8b4502010-02-20 20:34:29 +010055
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020056 this = container_of(*new, struct osmo_timer_list, node);
57
58 parent = *new;
59 if (timercmp(&timer->timeout, &this->timeout, <))
60 new = &((*new)->rb_left);
61 else
62 new = &((*new)->rb_right);
Harald Welteaafc27c2017-11-20 20:22:22 +010063 }
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020064
Harald Welteaafc27c2017-11-20 20:22:22 +010065 rb_link_node(&timer->node, parent, new);
66 rb_insert_color(&timer->node, &timer_root);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020067}
Harald Welteba6988b2011-08-17 12:46:48 +020068
Neels Hofmeyr87e45502017-06-20 00:17:59 +020069/*! set up timer callback and data
Pablo Neira Ayuso44f423f2017-05-08 18:00:28 +020070 * \param[in] timer the timer that should be added
71 * \param[in] callback function to be called when timer expires
72 * \param[in] pointer to data that passed to the callback function
73 */
74void osmo_timer_setup(struct osmo_timer_list *timer, void (*cb)(void *data),
75 void *data)
76{
77 timer->cb = cb;
78 timer->data = data;
79}
80
Neels Hofmeyr87e45502017-06-20 00:17:59 +020081/*! add a new timer to the timer management
Harald Welteba6988b2011-08-17 12:46:48 +020082 * \param[in] timer the timer that should be added
83 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020084void osmo_timer_add(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +010085{
Pablo Neira Ayusoa71b8ea2011-11-13 10:11:31 +010086 osmo_timer_del(timer);
87 timer->active = 1;
88 INIT_LLIST_HEAD(&timer->list);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020089 __add_timer(timer);
Harald Welteec8b4502010-02-20 20:34:29 +010090}
91
Neels Hofmeyr87e45502017-06-20 00:17:59 +020092/*! schedule a timer at a given future relative time
Harald Welteba6988b2011-08-17 12:46:48 +020093 * \param[in] timer the to-be-added timer
94 * \param[in] seconds number of seconds from now
95 * \param[in] microseconds number of microseconds from now
96 *
97 * This function can be used to (re-)schedule a given timer at a
98 * specified number of seconds+microseconds in the future. It will
99 * internally add it to the timer management data structures, thus
100 * osmo_timer_add() is automatically called.
101 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200102void
103osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds)
Harald Welteec8b4502010-02-20 20:34:29 +0100104{
105 struct timeval current_time;
106
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200107 osmo_gettimeofday(&current_time, NULL);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200108 timer->timeout.tv_sec = seconds;
109 timer->timeout.tv_usec = microseconds;
110 timeradd(&timer->timeout, &current_time, &timer->timeout);
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200111 osmo_timer_add(timer);
Harald Welteec8b4502010-02-20 20:34:29 +0100112}
113
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200114/*! delete a timer from timer management
Harald Welteba6988b2011-08-17 12:46:48 +0200115 * \param[in] timer the to-be-deleted timer
116 *
117 * This function can be used to delete a previously added/scheduled
118 * timer from the timer management code.
119 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200120void osmo_timer_del(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +0100121{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200122 if (timer->active) {
Harald Welteec8b4502010-02-20 20:34:29 +0100123 timer->active = 0;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200124 rb_erase(&timer->node, &timer_root);
125 /* make sure this is not already scheduled for removal. */
126 if (!llist_empty(&timer->list))
127 llist_del_init(&timer->list);
Harald Welteec8b4502010-02-20 20:34:29 +0100128 }
129}
130
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200131/*! check if given timer is still pending
Harald Welteba6988b2011-08-17 12:46:48 +0200132 * \param[in] timer the to-be-checked timer
133 * \return 1 if pending, 0 otherwise
134 *
135 * This function can be used to determine whether a given timer
136 * has alredy expired (returns 0) or is still pending (returns 1)
137 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200138int osmo_timer_pending(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +0100139{
140 return timer->active;
141}
142
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200143/*! compute the remaining time of a timer
Harald Weltee30b6ac2012-07-13 12:21:04 +0200144 * \param[in] timer the to-be-checked timer
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100145 * \param[in] now the current time (NULL if not known)
Harald Weltee30b6ac2012-07-13 12:21:04 +0200146 * \param[out] remaining remaining time until timer fires
147 * \return 0 if timer has not expired yet, -1 if it has
148 *
149 * This function can be used to determine the amount of time
150 * remaining until the expiration of the timer.
151 */
152int osmo_timer_remaining(const struct osmo_timer_list *timer,
153 const struct timeval *now,
154 struct timeval *remaining)
155{
156 struct timeval current_time;
157
Holger Hans Peter Freyther8e5435a2014-05-23 08:37:02 +0200158 if (!now)
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200159 osmo_gettimeofday(&current_time, NULL);
Holger Hans Peter Freyther8e5435a2014-05-23 08:37:02 +0200160 else
161 current_time = *now;
Harald Weltee30b6ac2012-07-13 12:21:04 +0200162
163 timersub(&timer->timeout, &current_time, remaining);
164
165 if (remaining->tv_sec < 0)
166 return -1;
167
168 return 0;
169}
170
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200171/*! Determine time between now and the nearest timer
Harald Weltede6e4982012-12-06 21:25:27 +0100172 * \returns pointer to timeval of nearest timer, NULL if there is none
173 *
Harald Welteec8b4502010-02-20 20:34:29 +0100174 * if we have a nearest time return the delta between the current
175 * time and the time of the nearest timer.
176 * If the nearest timer timed out return NULL and then we will
177 * dispatch everything after the select
178 */
Harald Welte7e820202011-07-16 10:15:16 +0200179struct timeval *osmo_timers_nearest(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100180{
Sylvain Munaut0061ded2011-10-18 20:11:03 +0200181 /* nearest_p is exactly what we need already: NULL if nothing is
182 * waiting, {0,0} if we must dispatch immediately, and the correct
183 * delay if we need to wait */
184 return nearest_p;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200185}
Harald Welteec8b4502010-02-20 20:34:29 +0100186
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200187static void update_nearest(struct timeval *cand, struct timeval *current)
188{
189 if (cand->tv_sec != LONG_MAX) {
190 if (timercmp(cand, current, >))
191 timersub(cand, current, &nearest);
192 else {
193 /* loop again inmediately */
Harald Welte00476022017-05-15 14:29:13 +0200194 timerclear(&nearest);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200195 }
196 nearest_p = &nearest;
Harald Welteec8b4502010-02-20 20:34:29 +0100197 } else {
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200198 nearest_p = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +0100199 }
Harald Welteec8b4502010-02-20 20:34:29 +0100200}
201
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200202/*! Find the nearest time and update nearest_p */
Harald Welte7e820202011-07-16 10:15:16 +0200203void osmo_timers_prepare(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100204{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200205 struct rb_node *node;
206 struct timeval current;
Harald Welteec8b4502010-02-20 20:34:29 +0100207
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200208 osmo_gettimeofday(&current, NULL);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200209
210 node = rb_first(&timer_root);
211 if (node) {
212 struct osmo_timer_list *this;
213 this = container_of(node, struct osmo_timer_list, node);
214 update_nearest(&this->timeout, &current);
Harald Welteec8b4502010-02-20 20:34:29 +0100215 } else {
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200216 nearest_p = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +0100217 }
218}
219
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200220/*! fire all timers... and remove them */
Harald Welte7e820202011-07-16 10:15:16 +0200221int osmo_timers_update(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100222{
223 struct timeval current_time;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200224 struct rb_node *node;
225 struct llist_head timer_eviction_list;
226 struct osmo_timer_list *this;
Harald Welteec8b4502010-02-20 20:34:29 +0100227 int work = 0;
228
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200229 osmo_gettimeofday(&current_time, NULL);
Harald Welteec8b4502010-02-20 20:34:29 +0100230
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200231 INIT_LLIST_HEAD(&timer_eviction_list);
232 for (node = rb_first(&timer_root); node; node = rb_next(node)) {
233 this = container_of(node, struct osmo_timer_list, node);
234
235 if (timercmp(&this->timeout, &current_time, >))
236 break;
237
238 llist_add(&this->list, &timer_eviction_list);
239 }
240
Harald Welteec8b4502010-02-20 20:34:29 +0100241 /*
242 * The callbacks might mess with our list and in this case
243 * even llist_for_each_entry_safe is not safe to use. To allow
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200244 * osmo_timer_del to be called from within the callback we need
245 * to restart the iteration for each element scheduled for removal.
Harald Welteec8b4502010-02-20 20:34:29 +0100246 *
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200247 * The problematic scenario is the following: Given two timers A
248 * and B that have expired at the same time. Thus, they are both
249 * in the eviction list in this order: A, then B. If we remove
250 * timer B from the A's callback, we continue with B in the next
251 * iteration step, leading to an access-after-release.
Harald Welteec8b4502010-02-20 20:34:29 +0100252 */
Harald Welteec8b4502010-02-20 20:34:29 +0100253restart:
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200254 llist_for_each_entry(this, &timer_eviction_list, list) {
255 osmo_timer_del(this);
Alexander Couzensec9bd522016-11-28 23:22:14 +0100256 if (this->cb)
257 this->cb(this->data);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200258 work = 1;
259 goto restart;
Harald Welteec8b4502010-02-20 20:34:29 +0100260 }
261
262 return work;
263}
264
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200265/*! Check how many timers we have in the system
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200266 * \returns number of \ref osmo_timer_list registered */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200267int osmo_timers_check(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100268{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200269 struct rb_node *node;
Harald Welteec8b4502010-02-20 20:34:29 +0100270 int i = 0;
271
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200272 for (node = rb_first(&timer_root); node; node = rb_next(node)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100273 i++;
274 }
275 return i;
276}
Harald Welteba6988b2011-08-17 12:46:48 +0200277
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200278/*! @} */