Commit 431e1db8 authored by Pekka Pessi's avatar Pekka Pessi

Using red-black tree in su_timer.c.

Added test using 500000 timers in su_timer_test.c.

darcs-hash:20051201002934-65a35-3026eb94a5d0d690efb6710b8b9aa1bcceb2dadf.gz
parent 6652b99d
......@@ -25,32 +25,33 @@
/**@CFILE su_timer.c
*
* Timer interface for su_root.
*
*
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
* Created: Fri Apr 28 15:45:41 2000 ppessi
*/
#include "config.h"
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "su.h"
#include "su_wait.h"
#include "su_alloc.h"
#include "su_module_debug.h"
#include "rbtree.h"
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
/**@ingroup su_wait
*
*
* @page su_timer_t Timer Objects
*
* Timers are used to schedule some task to be executed at given time or
* after a default interval. The default interval is specified when the
* timer is created. We call timer activation "setting the timer", and
* deactivation "resetting the timer" (as in SDL). When the given time has
* arrived or the default interval has elapsed, the timer expires and
* arrived or the default interval has elapsed, the timer expires and
* it is ready for execution.
*
* The functions used to create, destroy, activate, and manage timers are
......@@ -63,7 +64,7 @@
* - su_timer_reset(), and
* - su_timer_root().
*
* @note
* @note
* Timers use poll() to wake up waiting thread. On Linux, the timer
* granularity is determined by HZ kernel parameter, which decided when the
* kernel was compiled. With kernel 2.4 the default granularity is 10
......@@ -100,12 +101,12 @@
* @endcode
*
* Timer ceases running when su_timer_reset() is called.
*
*
* @note While the timer tries to compensate for delays occurred before and
* during the callback, it cannot be used as an exact source of timing
* information.
*
* Alternatively, the timer can be @b set for one-time event invocation.
* Alternatively, the timer can be @b set for one-time event invocation.
* When the timer is set, it is given the wakeup function and pointer to
* context data. The actual duration can also be specified using
* su_timer_set_at(). @code su_timer_set(timer, timer_wakeup, args);
......@@ -134,31 +135,99 @@
*/
struct su_timer_s {
su_timer_t *sut_next; /**< Pointer to next */
su_timer_t **sut_prev; /**< Pointer to previous */
su_timer_t *sut_left, *sut_right, *sut_parent;
su_task_r sut_task; /**< Task reference */
su_time_t sut_when; /**< When timer should be waken up next time */
su_duration_t sut_duration; /**< Timer duration */
su_timer_f sut_wakeup; /**< Function to call when waken up */
su_timer_arg_t *sut_arg; /**< Pointer to argument data */
unsigned sut_running; /**< Timer is running */
su_time_t sut_run; /**< When this timer started to run */
unsigned sut_woken; /**< Timer has waken up this many times */
unsigned short sut_running; /**< Timer is running */
unsigned char sut_black; /**< Black node */
unsigned char sut_set; /**< Timer is set (inserted in tree) */
};
enum {
enum sut_running {
reset = 0,
run_at_intervals = 1,
run_for_ever = 2
};
#define SU_TIMER_IS_SET(sut) ((sut)->sut_set)
/* Accessor macros for rbtree */
#define LEFT(sut) ((sut)->sut_left)
#define RIGHT(sut) ((sut)->sut_right)
#define PARENT(sut) ((sut)->sut_parent)
#define SET_RED(sut) ((sut)->sut_black = 0)
#define SET_BLACK(sut) ((sut)->sut_black = 1)
#define CMP(a, b) SU_TIME_CMP((a)->sut_when, (b)->sut_when)
#define IS_RED(sut) ((sut) && (sut)->sut_black == 0)
#define IS_BLACK(sut) (!(sut) || (sut)->sut_black == 1)
#define COPY_COLOR(dst, src) ((dst)->sut_black = (src)->sut_black)
#define INSERT(sut) ((sut)->sut_set = 1)
#define REMOVE(sut) ((sut)->sut_set = 0, \
(sut)->sut_left = (sut)->sut_right = (sut)->sut_parent = NULL)
RBTREE_PROTOS(static inline, timers, su_timer_t);
static inline int timers_append(su_timer_t **, su_timer_t *);
static inline void timers_remove(su_timer_t **, su_timer_t *);
static inline su_timer_t *timers_succ(su_timer_t const *);
static inline su_timer_t *timers_prec(su_timer_t const *);
static inline su_timer_t *timers_first(su_timer_t const *);
static inline su_timer_t *timers_last(su_timer_t const *);
RBTREE_BODIES(static inline, timers, su_timer_t,
LEFT, RIGHT, PARENT,
IS_RED, SET_RED, IS_BLACK, SET_BLACK, COPY_COLOR,
CMP, INSERT, REMOVE);
/** Set the timer. */
static inline int
su_timer_set0(su_timer_t **timers,
su_timer_t *t,
su_timer_f wakeup,
su_wakeup_arg_t *arg,
su_time_t when)
{
if (SU_TIMER_IS_SET(t))
timers_remove(timers, t);
t->sut_wakeup = wakeup;
t->sut_arg = arg;
t->sut_when = when;
return timers_append(timers, t);
}
/** Reset the timer. */
static inline int
su_timer_reset0(su_timer_t **timers,
su_timer_t *t)
{
if (SU_TIMER_IS_SET(t))
timers_remove(timers, t);
t->sut_wakeup = NULL;
t->sut_arg = NULL;
t->sut_running = reset;
memset(&t->sut_run, 0, sizeof(t->sut_run));
return 0;
}
/**Create a timer.
*
* Allocate and initialize an instance of su_timer_t.
*
*
* @param task a task for root object with which the timer will be associated
* @param msec the default duration of the timer
*
*
* @return A pointer to allocated timer instance, NULL on error.
*/
su_timer_t *su_timer_create(su_task_r const task, su_duration_t msec)
......@@ -183,88 +252,92 @@ su_timer_t *su_timer_create(su_task_r const task, su_duration_t msec)
/** Destroy a timer.
*
* Deinitialize and free an instance of su_timer_t.
*
*
* @param t pointer to the timer object
*/
void su_timer_destroy(su_timer_t *t)
{
if (t) {
su_timer_t **timers = su_task_timers(t->sut_task);
if (timers)
su_timer_reset0(timers, t);
su_task_deinit(t->sut_task);
su_timer_reset(t);
su_free(NULL, t);
}
}
/** Set the timer for an interval.
*
* Sets (starts) the given timer to expire after the default duration.
* Sets (starts) the given timer to expire after the default duration.
*
* The timer must have an default duration.
*
*
* @param t pointer to the timer object
* @param wakeup pointer to the wakeup function
* @param arg argument given to the wakeup function
*
*
* @return 0 if successful, -1 otherwise.
*/
int su_timer_set(su_timer_t *t,
su_timer_f wakeup,
int su_timer_set(su_timer_t *t,
su_timer_f wakeup,
su_timer_arg_t *arg)
{
char const *func = "su_timer_set";
su_timer_t **timers;
if (t == NULL) {
SU_DEBUG_1(("su_timer_set: NULL argument\n"));
SU_DEBUG_1(("%s(%p): %s\n", func, t, "NULL argument"));
return -1;
}
assert(t->sut_duration > 0);
if (t->sut_duration == 0) {
SU_DEBUG_1(("su_timer_set: timer without default duration\n"));
SU_DEBUG_1(("%s(%p): %s\n", func, t, "timer without default duration"));
return -1;
}
timers = su_task_timers(t->sut_task);
if (timers == NULL) {
SU_DEBUG_1(("%s(%p): %s\n", func, t, "invalid timer"));
return -1;
}
return su_timer_set_at(t, wakeup, arg,
su_time_add(su_now(), t->sut_duration));
su_timer_set0(timers, t, wakeup, arg, su_time_add(su_now(), t->sut_duration));
return 0;
}
/** Set timer at known time.
*
* Sets the timer to expire at given time.
*
* Sets the timer to expire at given time.
*
* @param t pointer to the timer object
* @param wakeup pointer to the wakeup function
* @param arg argument given to the wakeup function
* @param when time structure defining the wakeup time
*
*
* @return 0 if successful, -1 otherwise.
*/
int su_timer_set_at(su_timer_t *t,
su_timer_f wakeup,
int su_timer_set_at(su_timer_t *t,
su_timer_f wakeup,
su_wakeup_arg_t *arg,
su_time_t when)
{
su_timer_t **t0;
char const *func = "su_timer_set_at";
su_timer_t **timers;
if (t == NULL) {
SU_DEBUG_1(("su_timer_set_at: NULL argument\n"));
SU_DEBUG_1(("%s(%p): %s\n", func, t, "NULL argument"));
return -1;
}
if (t->sut_prev)
su_timer_reset(t);
t->sut_wakeup = wakeup;
t->sut_arg = arg;
t->sut_when = when;
timers = su_task_timers(t->sut_task);
if (timers == NULL) {
SU_DEBUG_1(("%s(%p): %s\n", func, t, "invalid timer"));
return -1;
}
for (t0 = su_task_timers(t->sut_task);
t0 && *t0 && su_time_cmp((*t0)->sut_when, when) <= 0;
t0 = &(*t0)->sut_next)
;
if ((t->sut_next = *t0))
t->sut_next->sut_prev = &t->sut_next;
t->sut_prev = t0;
*t0 = t;
su_timer_set0(timers, t, wakeup, arg, when);
return 0;
}
......@@ -274,43 +347,50 @@ int su_timer_set_at(su_timer_t *t,
* Run the given timer continuously, call wakeup function repeately in the
* default interval. If a wakeup call is missed, try to make it up (in other
* words, this kind of timer fails miserably if time is adjusted and it
* should really use /proc/uptime instead of gettimeofday()).
* should really use /proc/uptime instead of gettimeofday()).
*
* While a continously running timer is active it @b must @b not @b be @b
* set using su_timer_set() or su_timer_set_at().
*
* The timer must have an non-zero default interval.
*
*
* @param t pointer to the timer object
* @param wakeup pointer to the wakeup function
* @param arg argument given to the wakeup function
*
*
* @return 0 if successful, -1 otherwise.
*/
int su_timer_run(su_timer_t *t,
su_timer_f wakeup,
int su_timer_run(su_timer_t *t,
su_timer_f wakeup,
su_timer_arg_t *arg)
{
char const *func = "su_timer_run";
su_timer_t **timers;
su_time_t now = su_now();
if (t == NULL) {
SU_DEBUG_1(("su_timer_run: NULL argument\n"));
SU_DEBUG_1(("%s(%p): %s\n", func, t, "NULL argument"));
return -1;
}
assert(t->sut_duration > 0);
if (t->sut_duration == 0) {
SU_DEBUG_1(("su_timer_run: timer without default duration\n"));
SU_DEBUG_1(("%s(%p): %s\n", func, t, "timer without default duration"));
return -1;
}
if (su_timer_set_at(t, wakeup, arg, su_time_add(now, t->sut_duration)) < 0)
timers = su_task_timers(t->sut_task);
if (timers == NULL) {
SU_DEBUG_1(("%s(%p): %s\n", func, t, "invalid timer"));
return -1;
}
t->sut_running = run_at_intervals;
t->sut_run = now;
t->sut_woken = 0;
su_timer_set0(timers, t, wakeup, arg, su_time_add(now, t->sut_duration));
return 0;
}
......@@ -319,38 +399,46 @@ int su_timer_run(su_timer_t *t,
* Run the given timer continuously, call wakeup function repeately in the
* default interval. While a continously running timer is active it @b must
* @b not @b be @b set using su_timer_set() or su_timer_set_at(). Unlike
* su_timer_run(), the timer does not try to catchup missed callbacks..
* su_timer_run(), set for ever timer does not try to catchup missed
* callbacks.
*
* The timer must have an non-zero default interval.
*
*
* @param t pointer to the timer object
* @param wakeup pointer to the wakeup function
* @param arg argument given to the wakeup function
*
*
* @return 0 if successful, -1 otherwise.
*/
int su_timer_set_for_ever(su_timer_t *t,
su_timer_f wakeup,
int su_timer_set_for_ever(su_timer_t *t,
su_timer_f wakeup,
su_timer_arg_t *arg)
{
char const *func = "su_timer_run";
su_timer_t **timers;
su_time_t now = su_now();
if (t == NULL) {
SU_DEBUG_1(("su_timer_run: NULL argument\n"));
SU_DEBUG_1(("%s(%p): %s\n", func, t, "NULL argument"));
return -1;
}
assert(t->sut_duration > 0);
if (t->sut_duration == 0) {
SU_DEBUG_1(("su_timer_run: timer without default duration\n"));
SU_DEBUG_1(("%s(%p): %s\n", func, t, "timer without default duration"));
return -1;
}
if (su_timer_set_at(t, wakeup, arg, su_time_add(now, t->sut_duration)) < 0)
timers = su_task_timers(t->sut_task);
if (timers == NULL) {
SU_DEBUG_1(("%s(%p): %s\n", func, t, "invalid timer"));
return -1;
}
t->sut_running = run_for_ever;
su_timer_set0(timers, t, wakeup, arg, su_time_add(now, t->sut_duration));
return 0;
}
......@@ -360,90 +448,85 @@ int su_timer_set_for_ever(su_timer_t *t,
* Resets (stops) the given timer.
*
* @param t pointer to the timer object
*
*
* @return 0 if successful, -1 otherwise.
*/
int su_timer_reset(su_timer_t *t)
{
char const *func = "su_timer_reset";
su_timer_t **timers;
if (t == NULL) {
SU_DEBUG_1(("su_timer_reset: NULL argument\n"));
SU_DEBUG_1(("%s(%p): %s\n", func, t, "NULL argument"));
return -1;
}
if (t) {
if (t->sut_prev) {
su_timer_t **t0 = t->sut_prev;
assert(*t0 == t);
timers = su_task_timers(t->sut_task);
if ((*t0 = t->sut_next)) t->sut_next->sut_prev = t0;
t->sut_next = NULL; t->sut_prev = NULL;
}
t->sut_wakeup = NULL;
t->sut_arg = NULL;
t->sut_running = reset;
memset(&t->sut_run, 0, sizeof(t->sut_run));
return 0;
}
su_timer_reset0(timers, t);
return -1;
return 0;
}
/** @internal Check for expired timers in queue.
*
*
* The function su_timer_expire() checks a timer queue and executes and
* removes expired timers from the queue. It also calculates the time when
* the next timer expires.
*
* @param t0 pointer to the timer queue
* @param timers pointer to the timer queue
* @param timeout timeout in milliseconds [IN/OU]
* @param now current timestamp
*
* @return
*
* @return
* The number of expired timers.
*/
int su_timer_expire(su_timer_t ** const t0,
int su_timer_expire(su_timer_t ** const timers,
su_duration_t *timeout,
su_time_t now)
{
su_timer_t *t;
su_timer_f f;
su_time_t next;
int n = 0;
if (!*t0)
if (!*timers)
return n;
while ((t = *t0) != NULL) {
if (su_time_cmp(t->sut_when, now) > 0)
for (;;) {
t = timers_first(*timers);
if (t == NULL || SU_TIME_CMP(t->sut_when, now) > 0)
break;
if ((*t0 = t->sut_next)) t->sut_next->sut_prev = t0;
t->sut_next = NULL; t->sut_prev = NULL;
timers_remove(timers, t);
f = t->sut_wakeup; t->sut_wakeup = NULL;
t->sut_when = now;
assert(f);
if (t->sut_running == run_for_ever) {
f(su_root_magic(su_task_root(t->sut_task)), t, t->sut_arg); n++;
if (t->sut_running)
su_timer_set_at(t, f, t->sut_arg,
su_time_add(t->sut_run,
(t->sut_woken + 1) * t->sut_duration));
}
else if (t->sut_running == run_at_intervals) {
unsigned times = (unsigned)
((1000.0 * su_time_diff(now, t->sut_run) + t->sut_duration * 0.5) /
(double)t->sut_duration);
while (t->sut_woken < times) {
t->sut_woken++;
f(su_root_magic(su_task_root(t->sut_task)), t, t->sut_arg); n++;
if (t->sut_running) {
if (t->sut_running == run_at_intervals) {
unsigned times = (unsigned)
((1000.0 * su_time_diff(now, t->sut_run) + t->sut_duration * 0.5) /
(double)t->sut_duration);
while (t->sut_woken < times && t->sut_running) {
t->sut_woken++;
f(su_root_magic(su_task_root(t->sut_task)), t, t->sut_arg), n++;
}
}
if (t->sut_running)
su_timer_set_at(t, f, t->sut_arg,
su_time_add(t->sut_run,
(t->sut_woken + 1) * t->sut_duration));
else
f(su_root_magic(su_task_root(t->sut_task)), t, t->sut_arg), n++;
if (t->sut_running) {
next = su_time_add(t->sut_run, (t->sut_woken + 1) * t->sut_duration);
su_timer_set0(timers, t, f, t->sut_arg, next);
}
}
else
if (t->sut_running) {
next = su_time_add(t->sut_run, (t->sut_woken + 1) * t->sut_duration);
su_timer_set0(timers, t, f, t->sut_arg, next);
}
else {
f(su_root_magic(su_task_root(t->sut_task)), t, t->sut_arg); n++;
......@@ -464,7 +547,7 @@ int su_timer_expire(su_timer_t ** const t0,
su_duration_t su_timer_next_expires(su_timer_t const * t, su_time_t now)
{
su_duration_t tout;
if (!t)
return SU_DURATION_MAX;
......@@ -479,25 +562,28 @@ su_duration_t su_timer_next_expires(su_timer_t const * t, su_time_t now)
* The function su_timer_destroy_all() resets and frees all timers belonging
* to the specified task in the queue.
*
* @param t0 pointer to the timer queue
* @param task task owning the timers
* @param timers pointer to the timers
* @param task task owning the timers
*
* @return Number of timers reset.
* @return Number of timers reset.
*/
int su_timer_reset_all(su_timer_t **t0, su_task_r task)
int su_timer_reset_all(su_timer_t **timers, su_task_r task)
{
su_timer_t *t;
su_timer_t *t, *t_next;
int n = 0;
while ((t = *t0) != NULL) {
if (su_task_cmp(task, t->sut_task)) {
t0 = &t->sut_next;
}
else {
if ((*t0 = t->sut_next)) t->sut_next->sut_prev = t0;
n++;
su_free(NULL, t);
}
if (!timers || !*timers)
return 0;
for (t = timers_first(*timers); t; t = t_next) {
t_next = timers_succ(t);
if (su_task_cmp(task, t->sut_task))
continue;
n++;
timers_remove(timers, t);
su_free(NULL, t);
}
return n;
......@@ -509,7 +595,7 @@ int su_timer_reset_all(su_timer_t **t0, su_task_r task)
* timer.
*
* @param t pointer to the timer
*
*
* @return Pointer to the root object owning the timer.
*/
su_root_t *su_timer_root(su_timer_t const *t)
......
......@@ -30,7 +30,7 @@
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
*
* @internal
*
*
* @date Created: Fri Oct 19 08:53:55 2001 pessi
*/
......@@ -63,6 +63,8 @@ struct tester
{
su_root_t *root;
su_timer_t *t, *t1;
unsigned times;
void *sentinel;
};
void
......@@ -109,6 +111,15 @@ end_test(struct tester *tester, su_timer_t *t, struct timing *ti)
su_root_break(tester->root);
}