blob: da16459c2bf75ae149db2c34cc627b091ae2ef16 [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>
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020041#include <osmocom/core/linuxlist.h>
Harald Welteec8b4502010-02-20 20:34:29 +010042
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020043static struct rb_root timer_root = RB_ROOT;
Harald Welteec8b4502010-02-20 20:34:29 +010044
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020045static void __add_timer(struct osmo_timer_list *timer)
46{
47 struct rb_node **new = &(timer_root.rb_node);
48 struct rb_node *parent = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +010049
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020050 while (*new) {
51 struct osmo_timer_list *this;
Harald Welteec8b4502010-02-20 20:34:29 +010052
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020053 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}
Harald Welteba6988b2011-08-17 12:46:48 +020065
66/*! \brief add a new timer to the timer management
67 * \param[in] timer the timer that should be added
68 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020069void osmo_timer_add(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +010070{
Harald Welteec8b4502010-02-20 20:34:29 +010071 timer->active = 1;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020072 INIT_LLIST_HEAD(&timer->list);
73 __add_timer(timer);
Harald Welteec8b4502010-02-20 20:34:29 +010074}
75
Harald Welteba6988b2011-08-17 12:46:48 +020076/*! \brief schedule a timer at a given future relative time
77 * \param[in] timer the to-be-added timer
78 * \param[in] seconds number of seconds from now
79 * \param[in] microseconds number of microseconds from now
80 *
81 * This function can be used to (re-)schedule a given timer at a
82 * specified number of seconds+microseconds in the future. It will
83 * internally add it to the timer management data structures, thus
84 * osmo_timer_add() is automatically called.
85 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020086void
87osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds)
Harald Welteec8b4502010-02-20 20:34:29 +010088{
89 struct timeval current_time;
90
91 gettimeofday(&current_time, NULL);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020092 timer->timeout.tv_sec = seconds;
93 timer->timeout.tv_usec = microseconds;
94 timeradd(&timer->timeout, &current_time, &timer->timeout);
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020095 osmo_timer_add(timer);
Harald Welteec8b4502010-02-20 20:34:29 +010096}
97
Harald Welteba6988b2011-08-17 12:46:48 +020098/*! \brief delete a timer from timer management
99 * \param[in] timer the to-be-deleted timer
100 *
101 * This function can be used to delete a previously added/scheduled
102 * timer from the timer management code.
103 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200104void osmo_timer_del(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +0100105{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200106 if (timer->active) {
Harald Welteec8b4502010-02-20 20:34:29 +0100107 timer->active = 0;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200108 rb_erase(&timer->node, &timer_root);
109 /* make sure this is not already scheduled for removal. */
110 if (!llist_empty(&timer->list))
111 llist_del_init(&timer->list);
Harald Welteec8b4502010-02-20 20:34:29 +0100112 }
113}
114
Harald Welteba6988b2011-08-17 12:46:48 +0200115/*! \brief check if given timer is still pending
116 * \param[in] timer the to-be-checked timer
117 * \return 1 if pending, 0 otherwise
118 *
119 * This function can be used to determine whether a given timer
120 * has alredy expired (returns 0) or is still pending (returns 1)
121 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200122int osmo_timer_pending(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +0100123{
124 return timer->active;
125}
126
127/*
128 * if we have a nearest time return the delta between the current
129 * time and the time of the nearest timer.
130 * If the nearest timer timed out return NULL and then we will
131 * dispatch everything after the select
132 */
Harald Welte7e820202011-07-16 10:15:16 +0200133struct timeval *osmo_timers_nearest(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100134{
Sylvain Munaut0061ded2011-10-18 20:11:03 +0200135 /* nearest_p is exactly what we need already: NULL if nothing is
136 * waiting, {0,0} if we must dispatch immediately, and the correct
137 * delay if we need to wait */
138 return nearest_p;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200139}
Harald Welteec8b4502010-02-20 20:34:29 +0100140
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200141static void update_nearest(struct timeval *cand, struct timeval *current)
142{
143 if (cand->tv_sec != LONG_MAX) {
144 if (timercmp(cand, current, >))
145 timersub(cand, current, &nearest);
146 else {
147 /* loop again inmediately */
148 nearest.tv_sec = 0;
149 nearest.tv_usec = 0;
150 }
151 nearest_p = &nearest;
Harald Welteec8b4502010-02-20 20:34:29 +0100152 } else {
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200153 nearest_p = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +0100154 }
Harald Welteec8b4502010-02-20 20:34:29 +0100155}
156
157/*
158 * Find the nearest time and update s_nearest_time
159 */
Harald Welte7e820202011-07-16 10:15:16 +0200160void osmo_timers_prepare(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100161{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200162 struct rb_node *node;
163 struct timeval current;
Harald Welteec8b4502010-02-20 20:34:29 +0100164
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200165 gettimeofday(&current, NULL);
166
167 node = rb_first(&timer_root);
168 if (node) {
169 struct osmo_timer_list *this;
170 this = container_of(node, struct osmo_timer_list, node);
171 update_nearest(&this->timeout, &current);
Harald Welteec8b4502010-02-20 20:34:29 +0100172 } else {
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200173 nearest_p = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +0100174 }
175}
176
177/*
178 * fire all timers... and remove them
179 */
Harald Welte7e820202011-07-16 10:15:16 +0200180int osmo_timers_update(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100181{
182 struct timeval current_time;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200183 struct rb_node *node;
184 struct llist_head timer_eviction_list;
185 struct osmo_timer_list *this;
Harald Welteec8b4502010-02-20 20:34:29 +0100186 int work = 0;
187
188 gettimeofday(&current_time, NULL);
189
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200190 INIT_LLIST_HEAD(&timer_eviction_list);
191 for (node = rb_first(&timer_root); node; node = rb_next(node)) {
192 this = container_of(node, struct osmo_timer_list, node);
193
194 if (timercmp(&this->timeout, &current_time, >))
195 break;
196
197 llist_add(&this->list, &timer_eviction_list);
198 }
199
Harald Welteec8b4502010-02-20 20:34:29 +0100200 /*
201 * The callbacks might mess with our list and in this case
202 * even llist_for_each_entry_safe is not safe to use. To allow
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200203 * osmo_timer_del to be called from within the callback we need
204 * to restart the iteration for each element scheduled for removal.
Harald Welteec8b4502010-02-20 20:34:29 +0100205 *
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200206 * The problematic scenario is the following: Given two timers A
207 * and B that have expired at the same time. Thus, they are both
208 * in the eviction list in this order: A, then B. If we remove
209 * timer B from the A's callback, we continue with B in the next
210 * iteration step, leading to an access-after-release.
Harald Welteec8b4502010-02-20 20:34:29 +0100211 */
Harald Welteec8b4502010-02-20 20:34:29 +0100212restart:
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200213 llist_for_each_entry(this, &timer_eviction_list, list) {
214 osmo_timer_del(this);
215 this->cb(this->data);
216 work = 1;
217 goto restart;
Harald Welteec8b4502010-02-20 20:34:29 +0100218 }
219
220 return work;
221}
222
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200223int osmo_timers_check(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100224{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200225 struct rb_node *node;
Harald Welteec8b4502010-02-20 20:34:29 +0100226 int i = 0;
227
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200228 for (node = rb_first(&timer_root); node; node = rb_next(node)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100229 i++;
230 }
231 return i;
232}
Harald Welteba6988b2011-08-17 12:46:48 +0200233
234/*! }@ */