blob: 006721a208d83fbf9d4c68220914e200145aa22c [file] [log] [blame]
/*
* (C) 2006-2008 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "alarm.h"
#include "date.h"
#include <stdlib.h>
#include <limits.h>
static struct rb_root alarm_root = RB_ROOT;
void init_alarm(struct alarm_block *t,
void *data,
void (*fcn)(struct alarm_block *a, void *data))
{
/* initialize the head to check whether a node is inserted */
RB_CLEAR_NODE(&t->node);
timerclear(&t->tv);
t->data = data;
t->function = fcn;
}
static void __add_alarm(struct alarm_block *alarm)
{
struct rb_node **new = &(alarm_root.rb_node);
struct rb_node *parent = NULL;
while (*new) {
struct alarm_block *this;
this = container_of(*new, struct alarm_block, node);
parent = *new;
if (timercmp(&alarm->tv, &this->tv, <))
new = &((*new)->rb_left);
else
new = &((*new)->rb_right);
}
rb_link_node(&alarm->node, parent, new);
rb_insert_color(&alarm->node, &alarm_root);
}
void add_alarm(struct alarm_block *alarm, unsigned long sc, unsigned long usc)
{
struct timeval tv;
del_alarm(alarm);
alarm->tv.tv_sec = sc;
alarm->tv.tv_usec = usc;
gettimeofday_cached(&tv);
timeradd(&alarm->tv, &tv, &alarm->tv);
__add_alarm(alarm);
}
void del_alarm(struct alarm_block *alarm)
{
/* don't remove a non-inserted node */
if (!RB_EMPTY_NODE(&alarm->node)) {
rb_erase(&alarm->node, &alarm_root);
RB_CLEAR_NODE(&alarm->node);
}
}
int alarm_pending(struct alarm_block *alarm)
{
if (RB_EMPTY_NODE(&alarm->node))
return 0;
return 1;
}
static struct timeval *
calculate_next_run(struct timeval *cand,
struct timeval *tv,
struct timeval *next_run)
{
if (cand->tv_sec != LONG_MAX) {
if (timercmp(cand, tv, >))
timersub(cand, tv, next_run);
else {
/* loop again inmediately */
next_run->tv_sec = 0;
next_run->tv_usec = 0;
}
return next_run;
}
return NULL;
}
struct timeval *
get_next_alarm_run(struct timeval *next_run)
{
struct rb_node *node;
struct timeval tv;
gettimeofday_cached(&tv);
node = rb_first(&alarm_root);
if (node) {
struct alarm_block *this;
this = container_of(node, struct alarm_block, node);
return calculate_next_run(&this->tv, &tv, next_run);
}
return NULL;
}
struct timeval *
do_alarm_run(struct timeval *next_run)
{
struct list_head alarm_run_queue;
struct rb_node *node;
struct alarm_block *this, *tmp;
struct timeval tv;
gettimeofday_cached(&tv);
INIT_LIST_HEAD(&alarm_run_queue);
for (node = rb_first(&alarm_root); node; node = rb_next(node)) {
this = container_of(node, struct alarm_block, node);
if (timercmp(&this->tv, &tv, >))
break;
list_add(&this->list, &alarm_run_queue);
}
/* must be safe as entries can vanish from the callback */
list_for_each_entry_safe(this, tmp, &alarm_run_queue, list) {
rb_erase(&this->node, &alarm_root);
RB_CLEAR_NODE(&this->node);
this->function(this, this->data);
}
return get_next_alarm_run(next_run);
}