blob: 067bd8758a967704c6bf6ade3ef0e3256051aa67 [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 *
Harald Welteec8b4502010-02-20 20:34:29 +010022 */
23
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020024
Harald Welteba6988b2011-08-17 12:46:48 +020025/*! \addtogroup timer
26 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020027 * Osmocom timer abstraction; modelled after linux kernel timers
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020028 *
29 * \file timer.c */
Harald Welteba6988b2011-08-17 12:46:48 +020030
Harald Welteec8b4502010-02-20 20:34:29 +010031#include <assert.h>
32#include <string.h>
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020033#include <limits.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010034#include <osmocom/core/timer.h>
Sylvain Munaut07f11032011-10-21 21:55:29 +020035#include <osmocom/core/timer_compat.h>
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020036#include <osmocom/core/linuxlist.h>
Harald Welteec8b4502010-02-20 20:34:29 +010037
Holger Hans Peter Freyther3f838b72015-08-20 18:17:15 +000038/* These store the amount of time that we wait until next timer expires. */
Eric06747d52020-04-19 02:09:28 +020039static __thread struct timeval nearest;
40static __thread struct timeval *nearest_p;
Holger Hans Peter Freyther3f838b72015-08-20 18:17:15 +000041
Eric06747d52020-04-19 02:09:28 +020042static __thread struct rb_root timer_root = RB_ROOT;
Harald Welteec8b4502010-02-20 20:34:29 +010043
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020044static void __add_timer(struct osmo_timer_list *timer)
45{
46 struct rb_node **new = &(timer_root.rb_node);
47 struct rb_node *parent = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +010048
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020049 while (*new) {
50 struct osmo_timer_list *this;
Harald Welteec8b4502010-02-20 20:34:29 +010051
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020052 this = container_of(*new, struct osmo_timer_list, node);
53
54 parent = *new;
55 if (timercmp(&timer->timeout, &this->timeout, <))
56 new = &((*new)->rb_left);
57 else
58 new = &((*new)->rb_right);
Harald Welteaafc27c2017-11-20 20:22:22 +010059 }
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020060
Harald Welteaafc27c2017-11-20 20:22:22 +010061 rb_link_node(&timer->node, parent, new);
62 rb_insert_color(&timer->node, &timer_root);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020063}
Harald Welteba6988b2011-08-17 12:46:48 +020064
Neels Hofmeyr87e45502017-06-20 00:17:59 +020065/*! set up timer callback and data
Pablo Neira Ayuso44f423f2017-05-08 18:00:28 +020066 * \param[in] timer the timer that should be added
Vadim Yanitskiybc29b262019-03-26 01:12:43 +070067 * \param[in] cb function to be called when timer expires
68 * \param[in] data pointer to data that passed to the callback function
Pablo Neira Ayuso44f423f2017-05-08 18:00:28 +020069 */
70void osmo_timer_setup(struct osmo_timer_list *timer, void (*cb)(void *data),
71 void *data)
72{
73 timer->cb = cb;
74 timer->data = data;
75}
76
Neels Hofmeyr87e45502017-06-20 00:17:59 +020077/*! add a new timer to the timer management
Harald Welteba6988b2011-08-17 12:46:48 +020078 * \param[in] timer the timer that should be added
79 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020080void osmo_timer_add(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +010081{
Pablo Neira Ayusoa71b8ea2011-11-13 10:11:31 +010082 osmo_timer_del(timer);
83 timer->active = 1;
84 INIT_LLIST_HEAD(&timer->list);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +020085 __add_timer(timer);
Harald Welteec8b4502010-02-20 20:34:29 +010086}
87
Neels Hofmeyr87e45502017-06-20 00:17:59 +020088/*! schedule a timer at a given future relative time
Harald Welteba6988b2011-08-17 12:46:48 +020089 * \param[in] timer the to-be-added timer
90 * \param[in] seconds number of seconds from now
91 * \param[in] microseconds number of microseconds from now
92 *
93 * This function can be used to (re-)schedule a given timer at a
94 * specified number of seconds+microseconds in the future. It will
95 * internally add it to the timer management data structures, thus
96 * osmo_timer_add() is automatically called.
97 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +020098void
99osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds)
Harald Welteec8b4502010-02-20 20:34:29 +0100100{
101 struct timeval current_time;
102
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200103 osmo_gettimeofday(&current_time, NULL);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200104 timer->timeout.tv_sec = seconds;
105 timer->timeout.tv_usec = microseconds;
106 timeradd(&timer->timeout, &current_time, &timer->timeout);
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200107 osmo_timer_add(timer);
Harald Welteec8b4502010-02-20 20:34:29 +0100108}
109
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200110/*! delete a timer from timer management
Harald Welteba6988b2011-08-17 12:46:48 +0200111 * \param[in] timer the to-be-deleted timer
112 *
113 * This function can be used to delete a previously added/scheduled
114 * timer from the timer management code.
115 */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200116void osmo_timer_del(struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +0100117{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200118 if (timer->active) {
Harald Welteec8b4502010-02-20 20:34:29 +0100119 timer->active = 0;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200120 rb_erase(&timer->node, &timer_root);
121 /* make sure this is not already scheduled for removal. */
122 if (!llist_empty(&timer->list))
123 llist_del_init(&timer->list);
Harald Welteec8b4502010-02-20 20:34:29 +0100124 }
125}
126
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200127/*! check if given timer is still pending
Harald Welteba6988b2011-08-17 12:46:48 +0200128 * \param[in] timer the to-be-checked timer
129 * \return 1 if pending, 0 otherwise
130 *
131 * This function can be used to determine whether a given timer
132 * has alredy expired (returns 0) or is still pending (returns 1)
133 */
Pau Espin Pedrola4886392021-04-29 16:36:53 +0200134int osmo_timer_pending(const struct osmo_timer_list *timer)
Harald Welteec8b4502010-02-20 20:34:29 +0100135{
136 return timer->active;
137}
138
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200139/*! compute the remaining time of a timer
Harald Weltee30b6ac2012-07-13 12:21:04 +0200140 * \param[in] timer the to-be-checked timer
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100141 * \param[in] now the current time (NULL if not known)
Harald Weltee30b6ac2012-07-13 12:21:04 +0200142 * \param[out] remaining remaining time until timer fires
143 * \return 0 if timer has not expired yet, -1 if it has
144 *
145 * This function can be used to determine the amount of time
146 * remaining until the expiration of the timer.
147 */
148int osmo_timer_remaining(const struct osmo_timer_list *timer,
149 const struct timeval *now,
150 struct timeval *remaining)
151{
152 struct timeval current_time;
153
Holger Hans Peter Freyther8e5435a2014-05-23 08:37:02 +0200154 if (!now)
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200155 osmo_gettimeofday(&current_time, NULL);
Holger Hans Peter Freyther8e5435a2014-05-23 08:37:02 +0200156 else
157 current_time = *now;
Harald Weltee30b6ac2012-07-13 12:21:04 +0200158
159 timersub(&timer->timeout, &current_time, remaining);
160
161 if (remaining->tv_sec < 0)
162 return -1;
163
164 return 0;
165}
166
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200167/*! Determine time between now and the nearest timer
Harald Weltede6e4982012-12-06 21:25:27 +0100168 * \returns pointer to timeval of nearest timer, NULL if there is none
169 *
Harald Welteec8b4502010-02-20 20:34:29 +0100170 * if we have a nearest time return the delta between the current
171 * time and the time of the nearest timer.
172 * If the nearest timer timed out return NULL and then we will
173 * dispatch everything after the select
174 */
Harald Welte7e820202011-07-16 10:15:16 +0200175struct timeval *osmo_timers_nearest(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100176{
Sylvain Munaut0061ded2011-10-18 20:11:03 +0200177 /* nearest_p is exactly what we need already: NULL if nothing is
178 * waiting, {0,0} if we must dispatch immediately, and the correct
179 * delay if we need to wait */
180 return nearest_p;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200181}
Harald Welteec8b4502010-02-20 20:34:29 +0100182
Harald Welteb904e422020-10-18 21:24:13 +0200183/*! Determine time between now and the nearest timer in milliseconds
184 * \returns number of milliseconds until nearest timer expires; -1 if no timers pending
185 */
186int osmo_timers_nearest_ms(void)
187{
188 int nearest_ms;
189
190 if (!nearest_p)
191 return -1;
192
193 nearest_ms = nearest_p->tv_sec * 1000;
Andreas Eversberge8f05482024-01-24 17:12:27 +0100194#ifndef EMBEDDED
195 /* By adding 999 milliseconds, we ensure rounding up to the nearest
196 * whole millisecond. This approach prevents the return of 0 when the
197 * timer is still active, and it guarantees that the calling process
198 * does not wait for a duration shorter than the time remaining on the
199 * timer. */
200 nearest_ms += (nearest_p->tv_usec + 999) / 1000;
201#else
Harald Welteb904e422020-10-18 21:24:13 +0200202 nearest_ms += nearest_p->tv_usec / 1000;
Andreas Eversberge8f05482024-01-24 17:12:27 +0100203#endif
Harald Welteb904e422020-10-18 21:24:13 +0200204
205 return nearest_ms;
206}
207
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200208static void update_nearest(struct timeval *cand, struct timeval *current)
209{
210 if (cand->tv_sec != LONG_MAX) {
211 if (timercmp(cand, current, >))
212 timersub(cand, current, &nearest);
213 else {
214 /* loop again inmediately */
Harald Welte00476022017-05-15 14:29:13 +0200215 timerclear(&nearest);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200216 }
217 nearest_p = &nearest;
Harald Welteec8b4502010-02-20 20:34:29 +0100218 } else {
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200219 nearest_p = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +0100220 }
Harald Welteec8b4502010-02-20 20:34:29 +0100221}
222
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200223/*! Find the nearest time and update nearest_p */
Harald Welte7e820202011-07-16 10:15:16 +0200224void osmo_timers_prepare(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100225{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200226 struct rb_node *node;
227 struct timeval current;
Harald Welteec8b4502010-02-20 20:34:29 +0100228
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200229 osmo_gettimeofday(&current, NULL);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200230
231 node = rb_first(&timer_root);
232 if (node) {
233 struct osmo_timer_list *this;
234 this = container_of(node, struct osmo_timer_list, node);
235 update_nearest(&this->timeout, &current);
Harald Welteec8b4502010-02-20 20:34:29 +0100236 } else {
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200237 nearest_p = NULL;
Harald Welteec8b4502010-02-20 20:34:29 +0100238 }
239}
240
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200241/*! fire all timers... and remove them */
Harald Welte7e820202011-07-16 10:15:16 +0200242int osmo_timers_update(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100243{
244 struct timeval current_time;
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200245 struct rb_node *node;
246 struct llist_head timer_eviction_list;
247 struct osmo_timer_list *this;
Harald Welteec8b4502010-02-20 20:34:29 +0100248 int work = 0;
249
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200250 osmo_gettimeofday(&current_time, NULL);
Harald Welteec8b4502010-02-20 20:34:29 +0100251
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200252 INIT_LLIST_HEAD(&timer_eviction_list);
253 for (node = rb_first(&timer_root); node; node = rb_next(node)) {
254 this = container_of(node, struct osmo_timer_list, node);
255
256 if (timercmp(&this->timeout, &current_time, >))
257 break;
258
259 llist_add(&this->list, &timer_eviction_list);
260 }
261
Harald Welteec8b4502010-02-20 20:34:29 +0100262 /*
263 * The callbacks might mess with our list and in this case
264 * even llist_for_each_entry_safe is not safe to use. To allow
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200265 * osmo_timer_del to be called from within the callback we need
266 * to restart the iteration for each element scheduled for removal.
Harald Welteec8b4502010-02-20 20:34:29 +0100267 *
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200268 * The problematic scenario is the following: Given two timers A
269 * and B that have expired at the same time. Thus, they are both
270 * in the eviction list in this order: A, then B. If we remove
271 * timer B from the A's callback, we continue with B in the next
272 * iteration step, leading to an access-after-release.
Harald Welteec8b4502010-02-20 20:34:29 +0100273 */
Harald Welteec8b4502010-02-20 20:34:29 +0100274restart:
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200275 llist_for_each_entry(this, &timer_eviction_list, list) {
276 osmo_timer_del(this);
Alexander Couzensec9bd522016-11-28 23:22:14 +0100277 if (this->cb)
278 this->cb(this->data);
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200279 work = 1;
280 goto restart;
Harald Welteec8b4502010-02-20 20:34:29 +0100281 }
282
283 return work;
284}
285
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200286/*! Check how many timers we have in the system
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200287 * \returns number of \ref osmo_timer_list registered */
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200288int osmo_timers_check(void)
Harald Welteec8b4502010-02-20 20:34:29 +0100289{
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200290 struct rb_node *node;
Harald Welteec8b4502010-02-20 20:34:29 +0100291 int i = 0;
292
Pablo Neira Ayuso066c9122011-09-26 11:45:03 +0200293 for (node = rb_first(&timer_root); node; node = rb_next(node)) {
Harald Welteec8b4502010-02-20 20:34:29 +0100294 i++;
295 }
296 return i;
297}
Harald Welteba6988b2011-08-17 12:46:48 +0200298
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200299/*! @} */