blob: c8376c8ae3864fbcbfa2236fe9490866a1536f5d [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/* These store the amount of time that we wait until next timer expires. */
27static struct timeval nearest;
28static struct timeval *nearest_p;
29
Harald Welteba6988b2011-08-17 12:46:48 +020030/*! \addtogroup timer
31 * @{
32 */
33
34/*! \file timer.c
35 */
36
Harald Welteec8b4502010-02-20 20:34:29 +010037#include <assert.h>
38#include <string.h>
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020039#include <limits.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010040#include <osmocom/core/timer.h>
Sylvain Munaut07f11032011-10-21 21:55:29 +020041#include <osmocom/core/timer_compat.h>
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020042#include <osmocom/core/linuxlist.h>
Harald Welteec8b4502010-02-20 20:34:29 +010043
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020044static struct rb_root timer_root = RB_ROOT;
Harald Welteec8b4502010-02-20 20:34:29 +010045
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020046static void __add_timer(struct osmo_timer_list *timer)
47{
48 struct rb_node **new = &(timer_root.rb_node);
49 struct rb_node *parent = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +010050
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020051 while (*new) {
52 struct osmo_timer_list *this;
Harald Welteec8b4502010-02-20 20:34:29 +010053
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020054 this = container_of(*new, struct osmo_timer_list, node);
55
56 parent = *new;
57 if (timercmp(&timer->timeout, &this->timeout, <))
58 new = &((*new)->rb_left);
59 else
60 new = &((*new)->rb_right);
61 }
62
63 rb_link_node(&timer->node, parent, new);
64 rb_insert_color(&timer->node, &timer_root);
65}
Harald Welteba6988b2011-08-17 12:46:48 +020066
67/*! \brief add a new timer to the timer management
68 * \param[in] timer the timer that should be added
69 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020070void osmo_timer_add(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +010071{
Pablo Neira Ayusoa71b8ea2011-11-13 10:11:31 +010072 osmo_timer_del(timer);
73 timer->active = 1;
74 INIT_LLIST_HEAD(&timer->list);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020075 __add_timer(timer);
Harald Welteec8b4502010-02-20 20:34:29 +010076}
77
Harald Welteba6988b2011-08-17 12:46:48 +020078/*! \brief schedule a timer at a given future relative time
79 * \param[in] timer the to-be-added timer
80 * \param[in] seconds number of seconds from now
81 * \param[in] microseconds number of microseconds from now
82 *
83 * This function can be used to (re-)schedule a given timer at a
84 * specified number of seconds+microseconds in the future. It will
85 * internally add it to the timer management data structures, thus
86 * osmo_timer_add() is automatically called.
87 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020088void
89osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds)
Harald Welteec8b4502010-02-20 20:34:29 +010090{
91 struct timeval current_time;
92
93 gettimeofday(&current_time, NULL);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020094 timer->timeout.tv_sec = seconds;
95 timer->timeout.tv_usec = microseconds;
96 timeradd(&timer->timeout, &current_time, &timer->timeout);
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020097 osmo_timer_add(timer);
Harald Welteec8b4502010-02-20 20:34:29 +010098}
99
Harald Welteba6988b2011-08-17 12:46:48 +0200100/*! \brief delete a timer from timer management
101 * \param[in] timer the to-be-deleted timer
102 *
103 * This function can be used to delete a previously added/scheduled
104 * timer from the timer management code.
105 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200106void osmo_timer_del(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +0100107{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200108 if (timer->active) {
Harald Welteec8b4502010-02-20 20:34:29 +0100109 timer->active = 0;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200110 rb_erase(&timer->node, &timer_root);
111 /* make sure this is not already scheduled for removal. */
112 if (!llist_empty(&timer->list))
113 llist_del_init(&timer->list);
Harald Welteec8b4502010-02-20 20:34:29 +0100114 }
115}
116
Harald Welteba6988b2011-08-17 12:46:48 +0200117/*! \brief check if given timer is still pending
118 * \param[in] timer the to-be-checked timer
119 * \return 1 if pending, 0 otherwise
120 *
121 * This function can be used to determine whether a given timer
122 * has alredy expired (returns 0) or is still pending (returns 1)
123 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200124int osmo_timer_pending(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +0100125{
126 return timer->active;
127}
128
Harald Weltee30b6ac2012-07-13 12:21:04 +0200129/*! \brief compute the remaining time of a timer
130 * \param[in] timer the to-be-checked timer
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100131 * \param[in] now the current time (NULL if not known)
Harald Weltee30b6ac2012-07-13 12:21:04 +0200132 * \param[out] remaining remaining time until timer fires
133 * \return 0 if timer has not expired yet, -1 if it has
134 *
135 * This function can be used to determine the amount of time
136 * remaining until the expiration of the timer.
137 */
138int osmo_timer_remaining(const struct osmo_timer_list *timer,
139 const struct timeval *now,
140 struct timeval *remaining)
141{
142 struct timeval current_time;
143
Holger Hans Peter Freyther8e5435a2014-05-23 08:37:02 +0200144 if (!now)
Harald Weltee30b6ac2012-07-13 12:21:04 +0200145 gettimeofday(&current_time, NULL);
Holger Hans Peter Freyther8e5435a2014-05-23 08:37:02 +0200146 else
147 current_time = *now;
Harald Weltee30b6ac2012-07-13 12:21:04 +0200148
149 timersub(&timer->timeout, &current_time, remaining);
150
151 if (remaining->tv_sec < 0)
152 return -1;
153
154 return 0;
155}
156
Harald Weltede6e4982012-12-06 21:25:27 +0100157/*! \brief Determine time between now and the nearest timer
158 * \returns pointer to timeval of nearest timer, NULL if there is none
159 *
Harald Welteec8b4502010-02-20 20:34:29 +0100160 * if we have a nearest time return the delta between the current
161 * time and the time of the nearest timer.
162 * If the nearest timer timed out return NULL and then we will
163 * dispatch everything after the select
164 */
Harald Welte7e820202011-07-16 10:15:16 +0200165struct timeval *osmo_timers_nearest(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100166{
Sylvain Munaut0061ded2011-10-18 20:11:03 +0200167 /* nearest_p is exactly what we need already: NULL if nothing is
168 * waiting, {0,0} if we must dispatch immediately, and the correct
169 * delay if we need to wait */
170 return nearest_p;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200171}
Harald Welteec8b4502010-02-20 20:34:29 +0100172
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200173static void update_nearest(struct timeval *cand, struct timeval *current)
174{
175 if (cand->tv_sec != LONG_MAX) {
176 if (timercmp(cand, current, >))
177 timersub(cand, current, &nearest);
178 else {
179 /* loop again inmediately */
180 nearest.tv_sec = 0;
181 nearest.tv_usec = 0;
182 }
183 nearest_p = &nearest;
Harald Welteec8b4502010-02-20 20:34:29 +0100184 } else {
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200185 nearest_p = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +0100186 }
Harald Welteec8b4502010-02-20 20:34:29 +0100187}
188
Harald Weltede6e4982012-12-06 21:25:27 +0100189/*! \brief Find the nearest time and update nearest_p */
Harald Welte7e820202011-07-16 10:15:16 +0200190void osmo_timers_prepare(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100191{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200192 struct rb_node *node;
193 struct timeval current;
Harald Welteec8b4502010-02-20 20:34:29 +0100194
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200195 gettimeofday(&current, NULL);
196
197 node = rb_first(&timer_root);
198 if (node) {
199 struct osmo_timer_list *this;
200 this = container_of(node, struct osmo_timer_list, node);
201 update_nearest(&this->timeout, &current);
Harald Welteec8b4502010-02-20 20:34:29 +0100202 } else {
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200203 nearest_p = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +0100204 }
205}
206
Harald Weltede6e4982012-12-06 21:25:27 +0100207/*! \brief fire all timers... and remove them */
Harald Welte7e820202011-07-16 10:15:16 +0200208int osmo_timers_update(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100209{
210 struct timeval current_time;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200211 struct rb_node *node;
212 struct llist_head timer_eviction_list;
213 struct osmo_timer_list *this;
Harald Welteec8b4502010-02-20 20:34:29 +0100214 int work = 0;
215
216 gettimeofday(&current_time, NULL);
217
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200218 INIT_LLIST_HEAD(&timer_eviction_list);
219 for (node = rb_first(&timer_root); node; node = rb_next(node)) {
220 this = container_of(node, struct osmo_timer_list, node);
221
222 if (timercmp(&this->timeout, &current_time, >))
223 break;
224
225 llist_add(&this->list, &timer_eviction_list);
226 }
227
Harald Welteec8b4502010-02-20 20:34:29 +0100228 /*
229 * The callbacks might mess with our list and in this case
230 * even llist_for_each_entry_safe is not safe to use. To allow
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200231 * osmo_timer_del to be called from within the callback we need
232 * to restart the iteration for each element scheduled for removal.
Harald Welteec8b4502010-02-20 20:34:29 +0100233 *
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200234 * The problematic scenario is the following: Given two timers A
235 * and B that have expired at the same time. Thus, they are both
236 * in the eviction list in this order: A, then B. If we remove
237 * timer B from the A's callback, we continue with B in the next
238 * iteration step, leading to an access-after-release.
Harald Welteec8b4502010-02-20 20:34:29 +0100239 */
Harald Welteec8b4502010-02-20 20:34:29 +0100240restart:
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200241 llist_for_each_entry(this, &timer_eviction_list, list) {
242 osmo_timer_del(this);
243 this->cb(this->data);
244 work = 1;
245 goto restart;
Harald Welteec8b4502010-02-20 20:34:29 +0100246 }
247
248 return work;
249}
250
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200251int osmo_timers_check(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100252{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200253 struct rb_node *node;
Harald Welteec8b4502010-02-20 20:34:29 +0100254 int i = 0;
255
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200256 for (node = rb_first(&timer_root); node; node = rb_next(node)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100257 i++;
258 }
259 return i;
260}
Harald Welteba6988b2011-08-17 12:46:48 +0200261
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200262/*! @} */