[lib] Bug #134: threads should be more robust against backward time jumps

2006-08-25 Paul Jakma <paul.jakma@sun.com>

	* thread.c: (general) Add support for monotonic clock, it may still
	  jump forward by huge amounts, but should be immune to going
	  backwards. Fixes bug #134.
	  (quagga_gettimeofday_relative_adjust) helper, does what name
	  says - adjusts gettimeofday based relative timer.
	  (quagga_gettimeofday) helper to keep recent_time up to date.
	  (quagga_get_relative) helper, update and getch the relative
	  timer using gettimeofday(). POSIX CLOCK_MONOTONIC is also
	  supported, but the code is not enabled yet nor tested.
	  (quagga_real_stabilised) helper, retrieve absolute time but
	  stabilised so as to never decrease.
	  (quagga_gettime) Exported interface, analogous to POSIX
	  clock_gettime() in interface, supporting several clocks.
	  (quagga_time) Exported interface, analogous to traditional
	  time(), will never decrease.
	  (recent_relative_time) Convenience function to retrieve
	  relative_time timeval, similar to existing recent_time absolute
	  timeval, for when an approximately recent value will do.
	  (remainder) Update to use above helpers.
	  (thread_getrusage) Previously was a macro, but needs to be
	  a function to twiddle with thread.c private stuff.
	* thread.c: Point the GETRUSAGE macro at previous function.
	  Export quagga_gettime, quagga_time and recent_relative_time for
	  general use.
This commit is contained in:
Paul Jakma 2006-08-27 06:44:02 +00:00
parent f0894cf8c3
commit db9c0df934
3 changed files with 210 additions and 24 deletions

View File

@ -1,3 +1,30 @@
2006-08-25 Paul Jakma <paul.jakma@sun.com>
* thread.c: (general) Add support for monotonic clock, it may still
jump forward by huge amounts, but should be immune to going
backwards. Fixes bug #134.
(quagga_gettimeofday_relative_adjust) helper, does what name
says - adjusts gettimeofday based relative timer.
(quagga_gettimeofday) helper to keep recent_time up to date.
(quagga_get_relative) helper, update and getch the relative
timer using gettimeofday(). POSIX CLOCK_MONOTONIC is also
supported, but the code is not enabled yet nor tested.
(quagga_real_stabilised) helper, retrieve absolute time but
stabilised so as to never decrease.
(quagga_gettime) Exported interface, analogous to POSIX
clock_gettime() in interface, supporting several clocks.
(quagga_time) Exported interface, analogous to traditional
time(), will never decrease.
(recent_relative_time) Convenience function to retrieve
relative_time timeval, similar to existing recent_time absolute
timeval, for when an approximately recent value will do.
(remainder) Update to use above helpers.
(thread_getrusage) Previously was a macro, but needs to be
a function to twiddle with thread.c private stuff.
* thread.c: Point the GETRUSAGE macro at previous function.
Export quagga_gettime, quagga_time and recent_relative_time for
general use.
2006-07-25 Paul Jakma <paul.jakma@sun.com> 2006-07-25 Paul Jakma <paul.jakma@sun.com>
* thread.h: (struct thread) Add a cache pointer to the struct * thread.h: (struct thread) Add a cache pointer to the struct

View File

@ -30,7 +30,15 @@
#include "command.h" #include "command.h"
#include "sigevent.h" #include "sigevent.h"
/* Recent absolute time of day */
struct timeval recent_time; struct timeval recent_time;
static struct timeval last_recent_time;
/* Relative time, since startup */
static struct timeval relative_time;
static struct timeval relative_time_base;
/* init flag */
static unsigned short timers_inited;
static struct hash *cpu_record = NULL; static struct hash *cpu_record = NULL;
/* Struct timeval's tv_usec one second value. */ /* Struct timeval's tv_usec one second value. */
@ -85,6 +93,129 @@ timeval_elapsed (struct timeval a, struct timeval b)
+ (a.tv_usec - b.tv_usec)); + (a.tv_usec - b.tv_usec));
} }
#ifndef HAVE_CLOCK_MONOTONIC
static void
quagga_gettimeofday_relative_adjust (void)
{
struct timeval diff;
if (timeval_cmp (recent_time, last_recent_time) < 0)
{
relative_time.tv_sec++;
relative_time.tv_usec = 0;
}
else
{
diff = timeval_subtract (recent_time, last_recent_time);
relative_time.tv_sec += diff.tv_sec;
relative_time.tv_usec += diff.tv_usec;
relative_time = timeval_adjust (relative_time);
}
last_recent_time = recent_time;
}
#endif /* !HAVE_CLOCK_MONOTONIC */
/* gettimeofday wrapper, to keep recent_time updated */
static int
quagga_gettimeofday (struct timeval *tv)
{
int ret;
assert (tv);
if (!(ret = gettimeofday (&recent_time, NULL)))
{
/* init... */
if (!timers_inited)
{
relative_time_base = last_recent_time = recent_time;
timers_inited = 1;
}
/* avoid copy if user passed recent_time pointer.. */
if (tv != &recent_time)
*tv = recent_time;
return 0;
}
return ret;
}
static int
quagga_get_relative (struct timeval *tv)
{
int ret;
#ifdef HAVE_CLOCK_MONOTONIC
{
struct timespec tp;
if (!(ret = clock_gettime (CLOCK_MONOTONIC, &tp)))
{
relative_time.tv_sec = tp.tv_sec;
relative_time.tv_usec = tp.tv_nsec / 1000;
}
}
#else /* !HAVE_CLOCK_MONOTONIC */
if (!(ret = quagga_gettimeofday (&recent_time)))
quagga_gettimeofday_relative_adjust();
#endif /* HAVE_CLOCK_MONOTONIC */
if (tv)
*tv = relative_time;
return ret;
}
/* Get absolute time stamp, but in terms of the internal timer
* Could be wrong, but at least won't go back.
*/
static void
quagga_real_stabilised (struct timeval *tv)
{
*tv = relative_time_base;
tv->tv_sec += relative_time.tv_sec;
tv->tv_usec += relative_time.tv_usec;
*tv = timeval_adjust (*tv);
}
/* Exported Quagga timestamp function.
* Modelled on POSIX clock_gettime.
*/
int
quagga_gettime (enum quagga_clkid clkid, struct timeval *tv)
{
switch (clkid)
{
case QUAGGA_CLK_REALTIME:
return quagga_gettimeofday (tv);
case QUAGGA_CLK_MONOTONIC:
return quagga_get_relative (tv);
case QUAGGA_CLK_REALTIME_STABILISED:
quagga_real_stabilised (tv);
return 0;
default:
errno = EINVAL;
return -1;
}
}
/* time_t value in terms of stabilised absolute time.
* replacement for POSIX time()
*/
time_t
quagga_time (time_t *t)
{
struct timeval tv;
quagga_real_stabilised (&tv);
if (t)
*t = tv.tv_sec;
return tv.tv_sec;
}
/* Public export of recent_relative_time by value */
struct timeval
recent_relative_time (void)
{
return relative_time;
}
static unsigned int static unsigned int
cpu_record_hash_key (struct cpu_thread_history *a) cpu_record_hash_key (struct cpu_thread_history *a)
{ {
@ -396,10 +527,10 @@ thread_trim_head (struct thread_list *list)
unsigned long unsigned long
thread_timer_remain_second (struct thread *thread) thread_timer_remain_second (struct thread *thread)
{ {
gettimeofday (&recent_time, NULL); quagga_get_relative (NULL);
if (thread->u.sands.tv_sec - recent_time.tv_sec > 0) if (thread->u.sands.tv_sec - relative_time.tv_sec > 0)
return thread->u.sands.tv_sec - recent_time.tv_sec; return thread->u.sands.tv_sec - relative_time.tv_sec;
else else
return 0; return 0;
} }
@ -515,8 +646,8 @@ funcname_thread_add_timer_timeval (struct thread_master *m,
const char* funcname) const char* funcname)
{ {
struct thread *thread; struct thread *thread;
struct timeval alarm_time;
struct thread_list *list; struct thread_list *list;
struct timeval alarm_time;
struct thread *tt; struct thread *tt;
assert (m != NULL); assert (m != NULL);
@ -528,9 +659,9 @@ funcname_thread_add_timer_timeval (struct thread_master *m,
thread = thread_get (m, type, func, arg, funcname); thread = thread_get (m, type, func, arg, funcname);
/* Do we need jitter here? */ /* Do we need jitter here? */
gettimeofday (&recent_time, NULL); quagga_gettimeofday (&recent_time);
alarm_time.tv_sec = recent_time.tv_sec + time_relative->tv_sec; alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec;
alarm_time.tv_usec = recent_time.tv_usec + time_relative->tv_usec; alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec;
thread->u.sands = timeval_adjust(alarm_time); thread->u.sands = timeval_adjust(alarm_time);
/* Sort by timeval. */ /* Sort by timeval. */
@ -693,7 +824,7 @@ thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val)
{ {
if (!thread_empty (tlist)) if (!thread_empty (tlist))
{ {
*timer_val = timeval_subtract (tlist->head->u.sands, recent_time); *timer_val = timeval_subtract (tlist->head->u.sands, relative_time);
return timer_val; return timer_val;
} }
return NULL; return NULL;
@ -790,7 +921,7 @@ thread_fetch (struct thread_master *m, struct thread *fetch)
exceptfd = m->exceptfd; exceptfd = m->exceptfd;
/* Calculate select wait timer if nothing else to do */ /* Calculate select wait timer if nothing else to do */
gettimeofday (&recent_time, NULL); quagga_get_relative (NULL);
timer_wait = thread_timer_wait (&m->timer, &timer_val); timer_wait = thread_timer_wait (&m->timer, &timer_val);
timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg); timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg);
@ -812,8 +943,8 @@ thread_fetch (struct thread_master *m, struct thread *fetch)
/* Check foreground timers. Historically, they have had higher /* Check foreground timers. Historically, they have had higher
priority than I/O threads, so let's push them onto the ready priority than I/O threads, so let's push them onto the ready
list in front of the I/O threads. */ list in front of the I/O threads. */
gettimeofday (&recent_time, NULL); quagga_get_relative (NULL);
thread_timer_process (&m->timer, &recent_time); thread_timer_process (&m->timer, &relative_time);
/* Got IO, process it */ /* Got IO, process it */
if (num > 0) if (num > 0)
@ -834,7 +965,7 @@ thread_fetch (struct thread_master *m, struct thread *fetch)
#endif #endif
/* Background timer/events, lowest priority */ /* Background timer/events, lowest priority */
thread_timer_process (&m->background, &recent_time); thread_timer_process (&m->background, &relative_time);
if ((thread = thread_trim_head (&m->ready)) != NULL) if ((thread = thread_trim_head (&m->ready)) != NULL)
return thread_run (m, thread, fetch); return thread_run (m, thread, fetch);
@ -866,11 +997,29 @@ thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
int int
thread_should_yield (struct thread *thread) thread_should_yield (struct thread *thread)
{ {
gettimeofday(&recent_time, NULL); quagga_get_relative (NULL);
return (timeval_elapsed(recent_time, thread->ru.real) > return (timeval_elapsed(relative_time, thread->ru.real) >
THREAD_YIELD_TIME_SLOT); THREAD_YIELD_TIME_SLOT);
} }
void
thread_getrusage (RUSAGE_T *r)
{
quagga_get_relative (NULL);
#ifdef HAVE_RUSAGE
getrusage(RUSAGE_SELF, &(r->cpu));
#endif
r->real = relative_time;
#ifdef HAVE_CLOCK_MONOTONIC
/* quagga_get_relative() only updates recent_time if gettimeofday
* based, not when using CLOCK_MONOTONIC. As we export recent_time
* and guarantee to update it before threads are run...
*/
quagga_gettimeofday(&recent_time);
#endif /* HAVE_CLOCK_MONOTONIC */
}
/* We check thread consumed time. If the system has getrusage, we'll /* We check thread consumed time. If the system has getrusage, we'll
use that to get in-depth stats on the performance of the thread in addition use that to get in-depth stats on the performance of the thread in addition
to wall clock time stats from gettimeofday. */ to wall clock time stats from gettimeofday. */

View File

@ -31,14 +31,7 @@ struct rusage_t
}; };
#define RUSAGE_T struct rusage_t #define RUSAGE_T struct rusage_t
#ifdef HAVE_RUSAGE #define GETRUSAGE(X) thread_getrusage(X)
#define GETRUSAGE(X) \
getrusage(RUSAGE_SELF, &((X)->cpu)); \
gettimeofday(&recent_time, NULL); (X)->real = recent_time
#else
#define GETRUSAGE(X) \
gettimeofday(&recent_time, NULL); (X)->real = recent_time
#endif /* HAVE_RUSAGE */
/* Linked list of thread. */ /* Linked list of thread. */
struct thread_list struct thread_list
@ -99,6 +92,13 @@ struct cpu_thread_history
unsigned char types; unsigned char types;
}; };
/* Clocks supported by Quagga */
enum quagga_clkid {
QUAGGA_CLK_REALTIME = 0, /* ala gettimeofday() */
QUAGGA_CLK_MONOTONIC, /* monotonic, against an indeterminate base */
QUAGGA_CLK_REALTIME_STABILISED, /* like realtime, but non-decrementing */
};
/* Thread types. */ /* Thread types. */
#define THREAD_READ 0 #define THREAD_READ 0
#define THREAD_WRITE 1 #define THREAD_WRITE 1
@ -192,8 +192,17 @@ extern void thread_call (struct thread *);
extern unsigned long thread_timer_remain_second (struct thread *); extern unsigned long thread_timer_remain_second (struct thread *);
extern int thread_should_yield (struct thread *); extern int thread_should_yield (struct thread *);
/* Internal libzebra exports */
extern void thread_getrusage (RUSAGE_T *);
extern struct cmd_element show_thread_cpu_cmd; extern struct cmd_element show_thread_cpu_cmd;
/* replacements for the system gettimeofday(), clock_gettime() and
* time() functions, providing support for non-decrementing clock on
* all systems, and fully monotonic on /some/ systems.
*/
extern int quagga_gettime (enum quagga_clkid, struct timeval *);
extern time_t quagga_time (time_t *);
/* Returns elapsed real (wall clock) time. */ /* Returns elapsed real (wall clock) time. */
extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before, extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before,
unsigned long *cpu_time_elapsed); unsigned long *cpu_time_elapsed);
@ -202,5 +211,6 @@ extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before,
be used instead of calling gettimeofday if a recent value is sufficient. be used instead of calling gettimeofday if a recent value is sufficient.
This is guaranteed to be refreshed before a thread is called. */ This is guaranteed to be refreshed before a thread is called. */
extern struct timeval recent_time; extern struct timeval recent_time;
/* Similar to recent_time, but a monotonically increasing time value */
extern struct timeval recent_relative_time (void);
#endif /* _ZEBRA_THREAD_H */ #endif /* _ZEBRA_THREAD_H */