Re-implemented timer code to fix memory leaks and use generic linked list

This commit is contained in:
tedbullock 2007-09-23 21:29:52 +00:00
parent f23333a597
commit e9d31bb053
8 changed files with 312 additions and 128 deletions

View File

@ -1,3 +1,15 @@
2007-09-23 Ted Bullock <tbullock@canada.com>
* httperf.c: Add a check to return value of timer_schedule
* timer.c: Fix a crash associated with rescheduling a timer
* lib/list.c: NEW Generic linked list data structure implementation
* lib/list.h: NEW Generic linked list data structure API
2007-09-18 Ted Bullock <tbullock@canada.com>
* timer.c: Remove the last of the old timer logic code and replace it
with simpler functionality
2007-09-11 Ted Bullock <tbullock@canada.com> 2007-09-11 Ted Bullock <tbullock@canada.com>
* timer.c and timer.h: Use new heap and queue data * timer.c and timer.h: Use new heap and queue data

View File

@ -1,7 +1,10 @@
* New in version 1.0: * New in version 0.9.1:
** timer memory leak fix (Ted Bullock) ** timer re-write to reduce memory and fix memory leaks (Ted Bullock)
** Generic data structures and implementations (heap, queue, linked list)
** New options (see man-page for details):
--period=vT1,D1,T2,D2...Tn,Dn
* New in version 0.9: * New in version 0.9.0:
** Re-Factored build system now builds on the following platforms ** Re-Factored build system now builds on the following platforms
HP-UX 11i (64-bit PA-RISC and IA-64) HP-UX 11i (64-bit PA-RISC and IA-64)
Red Hat Enterprise Linux AS (AMD64 and IA-64) Red Hat Enterprise Linux AS (AMD64 and IA-64)

View File

@ -2,13 +2,13 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.60) AC_PREREQ(2.60)
AC_INIT(httperf, 0.9.99, httperf@linux.hpl.hp.com) AC_INIT(httperf, 0.9.1, httperf@linux.hpl.hp.com)
AC_CONFIG_SRCDIR([src/httperf.c]) AC_CONFIG_SRCDIR([src/httperf.c])
AC_CONFIG_HEADER([config.h]) AC_CONFIG_HEADER([config.h])
AC_CANONICAL_TARGET AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE(httperf, 0.9.99) AM_INIT_AUTOMAKE(httperf, 0.9.1)
# Checks for programs. # Checks for programs.
AC_GNU_SOURCE AC_GNU_SOURCE
@ -19,7 +19,7 @@ AC_PROG_INSTALL
AC_PROG_LIBTOOL AC_PROG_LIBTOOL
# Checks for libraries. # Checks for libraries.
AC_CHECK_LIB(event, event_init, , AC_MSG_ERROR([libevent is required to build httperf]) ) # AC_CHECK_LIB(event, event_init, , AC_MSG_ERROR([libevent is required to build httperf]) )
AC_CHECK_LIB(m, sqrt) AC_CHECK_LIB(m, sqrt)
AC_CHECK_LIB(crypto, main) AC_CHECK_LIB(crypto, main)
AC_CHECK_LIB(ssl, SSL_version, , AC_MSG_WARN([SSL Disabled]) ) AC_CHECK_LIB(ssl, SSL_version, , AC_MSG_WARN([SSL Disabled]) )

View File

@ -206,7 +206,8 @@ perf_sample (struct Timer *t, Any_Type regarg)
/* prepare for next sample interval: */ /* prepare for next sample interval: */
perf_sample_start = timer_now (); perf_sample_start = timer_now ();
timer_schedule (perf_sample, regarg, RATE_INTERVAL); if( timer_schedule (perf_sample, regarg, RATE_INTERVAL) == NULL)
panic("%s(%d): Received NULL from timer_schedule\n", __func__, __LINE__);
} }
int int

View File

@ -4,4 +4,4 @@ AM_LDFLAGS =
noinst_LIBRARIES = libutil.a noinst_LIBRARIES = libutil.a
libutil_a_SOURCES = getopt.c getopt.h getopt1.c ssl_writev.c generic_types.h \ libutil_a_SOURCES = getopt.c getopt.h getopt1.c ssl_writev.c generic_types.h \
queue.c queue.h heap.c heap.h queue.c queue.h heap.c heap.h list.c list.h

153
httperf/src/lib/list.c Normal file
View File

@ -0,0 +1,153 @@
/*
* Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
*
* This file is part of httperf, a web server performance measurment tool.
*
* 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.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* 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., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include <stdlib.h>
#include <generic_types.h>
struct Node {
Any_Type data;
struct Node *next;
};
struct List {
struct Node *dummy_head;
};
int
is_list_empty(struct List *l)
{
return l->dummy_head->next == NULL;
}
struct List *
list_create()
{
struct List *l;
if ((l = malloc(sizeof(struct List))) == NULL)
goto create_error;
if ((l->dummy_head = malloc(sizeof(struct Node))) == NULL)
goto create_error;
l->dummy_head->next = NULL;
return l;
create_error:
if (l != NULL)
free(l);
return NULL;
}
void
list_free(struct List *l)
{
free(l->dummy_head);
l->dummy_head = NULL;
free(l);
}
int
list_push(struct List *l, Any_Type data)
{
struct Node *n;
/*
* TODO: Implement caching so that we don't have to call
* malloc every time we push a new node onto the list
*/
if ((n = malloc(sizeof(struct Node))) == NULL) {
return 0;
}
n->data = data;
n->next = l->dummy_head->next;
l->dummy_head->next = n;
return 1;
}
Any_Type
list_top(struct List * l)
{
return l->dummy_head->next->data;
}
Any_Type
list_pop(struct List * l)
{
Any_Type data;
struct Node *n;
n = l->dummy_head->next;
data = l->dummy_head->next->data;
l->dummy_head->next = l->dummy_head->next->next;
/*
* TODO: As per above, implement caching here so that this memory
* does not have to be freed
*/
free(n);
return data;
}
void
list_remove_if_true(struct List *l, int (*action) (Any_Type))
{
struct Node *n = l->dummy_head;
while (n->next != NULL) {
if ((*action) (n->next->data)) {
struct Node *oldnext = n->next;
n->next = n->next->next;
free(oldnext);
} else
n = n->next;
}
}
void
list_for_each(struct List *l, int (*action) (Any_Type))
{
struct Node *n = l->dummy_head->next;
while (n != NULL) {
(*action) (n->data);
n = n->next;
}
}

47
httperf/src/lib/list.h Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
*
* This file is part of httperf, a web server performance measurment tool.
*
* 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.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of this work with the OpenSSL project's "OpenSSL" library
* (or with modified versions of it that use the same license as the "OpenSSL"
* library), and distribute linked combinations including the two. You must
* obey the GNU General Public License in all respects for all of the code
* used other than "OpenSSL". If you modify this file, you may extend this
* exception to your version of the file, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* 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., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef list_h
#define list_h
typedef int (*list_action) (Any_Type);
struct List;
struct List *list_create();
void list_free(struct List *);
void list_push(struct List *, Any_Type);
int is_list_empty(struct List *);
Any_Type list_top(struct List *);
Any_Type list_pop(struct List *);
void list_remove_if_true(struct List *, list_action);
void list_for_each(struct List *, list_action);
#endif /* list_h */

View File

@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2000-2007 Hewlett-Packard Company
* Copyright (C) 2007 Ted Bullock <tbullock@canada.com> * Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
* Copyright (C) 2000-2007 Hewlett-Packard Company
* *
* This file is part of httperf, a web server performance measurment tool. * This file is part of httperf, a web server performance measurment tool.
* *
@ -38,20 +38,18 @@
#include <sys/time.h> #include <sys/time.h>
#include <generic_types.h> #include <generic_types.h>
#include <heap.h> #include <list.h>
#include <queue.h>
#include <httperf.h> #include <httperf.h>
#define HEAP_SIZE 4096 #define NUM_TIMERS 0
#define WHEEL_SIZE 4096
#define TIMER_INTERVAL (1.0/1000) /* timer granularity in seconds */
static Time now; static Time now;
static Time next_tick; static Time next_tick;
struct Timer { struct Timer {
u_long delta; Time time_started;
Time timeout_delay;
bool has_expired;
/* /*
* Callback function called when timer expires (timeout) * Callback function called when timer expires (timeout)
@ -65,26 +63,17 @@ struct Timer {
}; };
/* /*
* FIFO Queue of inactive timers * inactive timers
*/ */
static struct Queue *passive_timers = NULL; static struct List *passive_timers = NULL;
/* /*
* Min heap of active timers * active timers
*/ */
static struct Heap *active_timers = NULL; static struct List *active_timers = NULL;
/* /*
* Executed once a timer has expired, enqueues the timer back into * active timeoutless timers
* the passive_timers queue for later use
*/ */
static void static struct List *persistent_timers = NULL;
done(struct Timer *t)
{
/*
* Double cast. Irritating but does the trick
*/
enqueue((Any_Type) (void *) t, passive_timers);
}
/* /*
* Returns the time and calls the syscall gettimeofday. This is an expensive * Returns the time and calls the syscall gettimeofday. This is an expensive
@ -112,21 +101,6 @@ timer_now(void)
return timer_now_forced(); return timer_now_forced();
} }
/*
* Comparison callback function used by the heap data structure to correctly
* insert the timer in the proper order (min order as in this case).
*/
_Bool
comparator(Any_Type a, Any_Type b)
{
struct Timer *timer_a, *timer_b;
timer_a = (struct Timer *) a.vp;
timer_b = (struct Timer *) b.vp;
return timer_a->delta < timer_b->delta;
}
/* /*
* Initializes a large timer pool cache * Initializes a large timer pool cache
* This is a very expensive function. Call before beginning measurements. * This is a very expensive function. Call before beginning measurements.
@ -135,22 +109,26 @@ comparator(Any_Type a, Any_Type b)
_Bool _Bool
timer_init(void) timer_init(void)
{ {
passive_timers = create_queue(WHEEL_SIZE); passive_timers = list_create();
if (passive_timers == NULL) if (passive_timers == NULL)
goto init_failure; goto init_failure;
active_timers = create_heap(HEAP_SIZE, &comparator); active_timers = list_create();
if (active_timers == NULL) if (active_timers == NULL)
goto init_failure; goto init_failure;
while (!is_queue_full(passive_timers)) { persistent_timers = list_create();
if (persistent_timers == NULL)
goto init_failure;
for (int i = 0; i < NUM_TIMERS; i++) {
Any_Type a; Any_Type a;
a.vp = malloc(sizeof(struct Timer)); a.vp = malloc(sizeof(struct Timer));
if (a.vp == NULL) if (a.vp == NULL)
goto init_failure; goto init_failure;
enqueue(a, passive_timers); list_push(passive_timers, a);
} }
now = timer_now_forced(); now = timer_now_forced();
@ -162,63 +140,78 @@ timer_init(void)
return false; return false;
} }
/* /*
* Frees all allocated timers, and timer queues * Frees all allocated timers, and timer queues
*/ */
void void
timer_free_all(void) timer_free_all(void)
{ {
while (!is_queue_empty(passive_timers)) { int count = 0;
Any_Type a = get_front_and_dequeue(passive_timers);
free(a.vp);
}
free_queue(passive_timers);
while (!is_heap_empty(active_timers)) { while (!is_list_empty(passive_timers)) {
Any_Type a = remove_min(active_timers); Any_Type a = list_pop(passive_timers);
fprintf(stderr,
"passive_timers Freeing counter at address %p\n",
a.vp);
free(a.vp); free(a.vp);
count++;
} }
free_heap(active_timers); list_free(passive_timers);
passive_timers = NULL;
while (!is_list_empty(active_timers)) {
Any_Type a = list_pop(active_timers);
fprintf(stderr,
"active_timers Freeing counter at address %p\n", a.vp);
free(a.vp);
count++;
}
list_free(active_timers);
active_timers = NULL;
while (!is_list_empty(persistent_timers)) {
Any_Type a = list_pop(persistent_timers);
fprintf(stderr,
"persistent_timers Freeing counter at address %p\n",
a.vp);
free(a.vp);
count++;
}
list_free(persistent_timers);
persistent_timers = NULL;
if (DBG > 2)
fprintf(stderr, "Freed a total of %d counters\n", count);
} }
/* static int
* Checks for timers which have had their timeout value pass and executes their timer_has_expired(Any_Type a)
* callback function. The timer is then removed from the active timer list
* and then enqueued back into the passive timer queue
*/
static void
expire_complete_timers(Any_Type a)
{ {
struct Timer *t = (struct Timer *) a.vp; struct Timer *t = a.vp;
if (t->delta == 0) { /*
(*t->timeout_callback) (t, t->timer_subject); * Only expire currently processing timers
*/
Any_Type verify = remove_min(active_timers); if (t->has_expired == false) {
if (t->time_started + t->timeout_delay < timer_now()) {
if (verify.vp != t) t->has_expired = true;
fprintf(stderr, (*t->timeout_callback) (t, t->timer_subject);
"Active timer heap is out of min order!\n\t%s.%s(%d): %s\n", return 1;
__FILE__, __func__, __LINE__, strerror(errno)); }
} else
/* return 0;
* Double cast. Irritating but does the trick
*/
enqueue((Any_Type) (void *) t, passive_timers);
}
} }
/* static int
* To be used to decrement a single timer delta value with the heap_for_each timer_deactivate(Any_Type a)
* function via a function pointer
*/
static void
decrement_timers(Any_Type a)
{ {
struct Timer *t = (struct Timer *) a.vp; struct Timer *t = a.vp;
if (t != 0) if (t->has_expired == true)
t->delta--; list_push(passive_timers, a);
return t->has_expired;
} }
/* /*
@ -230,20 +223,8 @@ void
timer_tick(void) timer_tick(void)
{ {
now = timer_now_forced(); now = timer_now_forced();
list_for_each(active_timers, &timer_has_expired);
while (timer_now() >= next_tick) { list_remove_if_true(active_timers, &timer_deactivate);
/*
* Check for timers that have timed out and expire them
*/
heap_for_each(active_timers, &expire_complete_timers);
/*
* Decrement remaining timers
*/
heap_for_each(active_timers, &decrement_timers);
next_tick += TIMER_INTERVAL;
}
} }
struct Timer * struct Timer *
@ -251,37 +232,24 @@ timer_schedule(void (*timeout) (struct Timer * t, Any_Type arg),
Any_Type subject, Time delay) Any_Type subject, Time delay)
{ {
struct Timer *t; struct Timer *t;
u_long ticks;
u_long delta;
Time behind;
if (!is_queue_empty(passive_timers)) { if (!is_list_empty(passive_timers)) {
Any_Type a = get_front_and_dequeue(passive_timers); Any_Type a = list_pop(passive_timers);
t = (struct Timer *) a.vp; t = (struct Timer *) a.vp;
} else } else if ((t = malloc(sizeof(struct Timer))) == NULL)
return NULL; return NULL;
memset(t, 0, sizeof(struct Timer)); memset(t, 0, sizeof(struct Timer));
t->timeout_callback = timeout; t->timeout_callback = timeout;
t->has_expired = false;
t->timer_subject = subject; t->timer_subject = subject;
t->time_started = timer_now();
t->timeout_delay = delay;
behind = (timer_now() - next_tick); if (delay > 0)
if (behind > 0.0) list_push(active_timers, (Any_Type) (void *) t);
delay += behind; else
list_push(persistent_timers, (Any_Type) (void *) t);
if (delay < 0.0)
ticks = 1;
else {
ticks =
(delay + TIMER_INTERVAL / 2.0) * (1.0 / TIMER_INTERVAL);
if (!ticks)
ticks = 1; /* minimum delay is a tick */
}
delta = ticks / WHEEL_SIZE;
t->delta = delta;
insert((Any_Type) (void *) t, active_timers);
if (DBG > 2) if (DBG > 2)
fprintf(stderr, fprintf(stderr,
@ -302,5 +270,5 @@ timer_cancel(struct Timer *t)
* being processed (whose timeout has expired). * being processed (whose timeout has expired).
*/ */
done(t); t->has_expired = true;
} }