summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am4
-rw-r--r--lib/memtypes.c4
-rw-r--r--lib/qpselect.c973
-rw-r--r--lib/qpselect.h197
-rw-r--r--lib/qtimers.c317
-rw-r--r--lib/qtimers.h172
6 files changed, 1665 insertions, 2 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 1a5c73d9..4ac42d37 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -13,7 +13,7 @@ libzebra_la_SOURCES = \
filter.c routemap.c distribute.c stream.c str.c log.c plist.c \
zclient.c sockopt.c smux.c md5.c if_rmap.c keychain.c privs.c \
sigevent.c pqueue.c jhash.c memtypes.c workqueue.c symtab.c heap.c \
- qtime.c qpthreads.c mqueue.c
+ qtime.c qpthreads.c mqueue.c qpselect.c qtimers.c
BUILT_SOURCES = memtypes.h route_types.h
@@ -29,7 +29,7 @@ pkginclude_HEADERS = \
plist.h zclient.h sockopt.h smux.h md5.h if_rmap.h keychain.h \
privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \
workqueue.h route_types.h symtab.h heap.h \
- qtime.h qpthreads.h mqueue.h
+ qtime.h qpthreads.h mqueue.h qpselect.h qtimers.h
EXTRA_DIST = regex.c regex-gnu.h memtypes.awk route_types.awk route_types.txt
diff --git a/lib/memtypes.c b/lib/memtypes.c
index 18d8b8f8..18d42a9e 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -35,6 +35,10 @@ struct memory_list memory_list_lib[] =
{ MTYPE_MQUEUE_QUEUE, "Mqueue queue structure" },
{ MTYPE_MQUEUE_BLOCKS, "Mqueue message blocks" },
{ MTYPE_MQUEUE_THREAD_SIGNAL, "Mqueue thread signal" },
+ { MTYPE_QPS_SELECTION, "qpselect selection" },
+ { MTYPE_QPS_FILE, "qpselect file" },
+ { MTYPE_QTIMER_PILE, "qtimer pile structure" },
+ { MTYPE_QTIMER, "qtimer timer" },
{ MTYPE_VTY, "VTY" },
{ MTYPE_VTY_OUT_BUF, "VTY output buffer" },
{ MTYPE_VTY_HIST, "VTY history" },
diff --git a/lib/qpselect.c b/lib/qpselect.c
new file mode 100644
index 00000000..e792bbaa
--- /dev/null
+++ b/lib/qpselect.c
@@ -0,0 +1,973 @@
+/* Quagga pselect support -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <signal.h>
+#include <string.h>
+
+#include "zassert.h"
+#include "qpselect.h"
+#include "qpthreads.h"
+#include "qtime.h"
+#include "memory.h"
+#include "vector.h"
+
+/*==============================================================================
+ * Quagga pselect -- qps_xxxx
+ *
+ * Here and in qpselect.h is a data structure for managing multiple file
+ * descriptors and running pselect to wait for I/O activity and to multiplex
+ * between the file descriptors.
+ *
+ * The qps_selection structure manages a collection of file descriptors which
+ * are to be waited on together in a pselect statement.
+ *
+ * NB: it is ASSUMED that a qps_selection will be private to the thread in
+ * which it is created and used.
+ *
+ * There is NO mutex handling here.
+ *
+ * This supports pselect, so supports:
+ *
+ * * waiting for file descriptors, which may each be expecting any combination
+ * of error/read/write events.
+ *
+ * Files may be added or removed from the selection. Files in the selection
+ * may then be enabled/disabled for any combination of error/read/write
+ * "mode" events.
+ *
+ * * a timeout
+ *
+ * Infinite timeouts are not supported.
+ *
+ * * an optional signal number and sigmask
+ *
+ * So that a signal may be used to interrupt a waiting pselect.
+ *
+ * For this to work there must be a signal which is generally masked, and
+ * is unmasked for the duration of the pselect.
+ *
+ * When a pselect returns there may be a number of files with events pending.
+ * The qps_dispatch_next() calls the action routine for the next event to be
+ * dealt with. Events are dispatched in the order: error, read and write, and
+ * then in file descriptor order. (So all error events in fd order, then all
+ * read events, and so on.)
+ *
+ * Note that at no time are any modes automatically disabled. So the system is
+ * level triggered. So, for example, a read event that is not dealt with will
+ * be triggered again on the next pselect -- unless the read mode is explicitly
+ * disabled for the file.
+ *
+ * Action Functions
+ * ----------------
+ *
+ * There is a separate action function for each mode. Each file has its own
+ * set of action functions -- so these may be used to implement a form of
+ * state machine for the file.
+ *
+ * When the action function is called it is passed the qps_file structure and
+ * the file_info pointer from that structure.
+ *
+ * During an action function modes may be enabled/disabled, actions changed,
+ * the file removed from the selection... there are no restrictions.
+ *
+ *
+ * TODO: worry about closing down a selection.
+ *
+ * TODO: worry about closing down files
+ */
+
+static int qps_super_set_map_made = 0 ;
+
+static void qps_make_super_set_map(void) ;
+
+/*==============================================================================
+ * qps_selection handling
+ */
+
+/* Forward references */
+static qps_file qps_file_lookup_fd(qps_selection qps, int fd, qps_file insert) ;
+static void qps_file_remove(qps_selection qps, qps_file qf) ;
+static int qps_file_check(qps_selection qps, qps_file qf) ;
+static void qps_super_set_zero(fd_super_set* p_set, int n) ;
+static int qps_next_fd_pending(fd_super_set* pending, int fd, int fd_last) ;
+
+/* See qps_make_super_set_map() and qps_pselect() below. */
+static short fd_byte_count[FD_SETSIZE] ; /* number of bytes for fds 0..fd */
+
+/* Initialise a selection -- allocating it if required.
+ *
+ * Returns the qps_selection.
+ */
+qps_selection
+qps_selection_init_new(qps_selection qps)
+{
+ if (!qps_super_set_map_made)
+ qps_make_super_set_map() ; /* map the fd_super_set */
+
+ if (qps == NULL)
+ qps = XCALLOC(MTYPE_QPS_SELECTION, sizeof(struct qps_selection)) ;
+ else
+ memset(qps, 0, sizeof(struct qps_selection)) ;
+
+ /* Zeroising initialises:
+ *
+ * fd_count -- no fd's yet
+ * fd_direct -- not direct lookup
+ *
+ * files -- empty vector
+ *
+ * fd_last -- unset
+ * enabled_count -- no fd's enabled in any mode
+ * enabled -- empty bit vectors
+ *
+ * tried_fd_last -- nothing tried yet
+ * tried_count -- nothing tried yet
+ * results -- empty bit vectors
+ *
+ * pend_count -- no results to dispatch
+ * pend_mnum -- unset
+ * pend_fd -- unset
+ *
+ * signum -- no signal to be enabled
+ * sigmask -- unset
+ *
+ * So nothing else to do.
+ */
+
+ return qps ;
+} ;
+
+/* Add given file to the selection, setting its fd and pointer to further
+ * file information. All modes are disabled.
+ *
+ * This initialises most of the qps_file structure, but not the actions.
+ *
+ * Adding a file using the same fd as an existing file is a fatal error.
+ */
+void
+qps_add_file(qps_selection qps, qps_file qf, int fd, void* file_info)
+{
+ qf->selection = qps ;
+
+ qf->file_info = file_info ;
+ qf->fd = fd ;
+
+ qf->enabled_bits = 0 ;
+
+ qps_file_lookup_fd(qps, fd, qf) ; /* Add. */
+} ;
+
+/* Remove given file from the selection.
+ *
+ * It is the callers responsibility to ensure that the file is in a suitable
+ * state to be removed from the selection.
+ *
+ * The file is disabled in all modes.
+ *
+ * NB: it is a FATAL error to remove a file that is not a member of its
+ * selection.
+ */
+void
+qps_remove_file(qps_file qf)
+{
+ passert(qf->selection != NULL) ;
+
+ qps_file_remove(qf->selection, qf) ;
+
+ qf->selection = NULL ; /* no longer a selection member */
+} ;
+
+/* Set the signal mask for the selection.
+ *
+ * This supports the unmasking of a single signal for the duration of the
+ * pselect operation.
+ *
+ * It is assumed that the set of signals generally masked by a thread is
+ * essentially static. So this function is passed that set. (So the sigmask
+ * argument must have the signum signal masked.)
+ *
+ * If the set of signals masked by the thread changes, then this function
+ * should be called again.
+ *
+ * Setting a signum == 0 turns OFF the use of the sigmask.
+ */
+void
+qps_set_signal(qps_selection qps, int signum, sigset_t sigmask)
+{
+ qps->signum = signum ;
+
+ if (signum != 0)
+ {
+ assert(sigismember(&sigmask, signum)) ;
+ sigdelset(&sigmask, signum) ;
+ qps->sigmask = sigmask ;
+ } ;
+} ;
+
+/* Execute a pselect for the given selection -- subject to the given timeout
+ * *time*.
+ *
+ * The time-out time is an "absolute" time, as measured by qt_get_monotonic().
+ *
+ * A timeout time <= the current qt_get_monotonic() is treated as a zero
+ * timeout period, and will return immediately from the pselect.
+ *
+ * There is no support for an infinite timeout.
+ *
+ * Returns: -1 => EINTR occurred
+ * 0 => hit timeout -- no files are ready
+ * > 0 => there are this many files ready in one or more modes
+ *
+ * All other errors are FATAL.
+ *
+ * The qps_dispatch_next() processes the returns from pselect().
+ */
+int
+qps_pselect(qps_selection qps, qtime_t timeout)
+{
+ struct timespec ts ;
+ qps_mnum_t mnum ;
+ fd_set* p_fds[qps_mnum_count] ;
+ int n ;
+
+ /* Convert timeout time to interval for pselect() */
+ timeout -= qt_get_monotonic() ;
+ if (timeout < 0)
+ timeout = 0 ;
+
+ /* Prepare the argument/result bitmaps */
+ /* Capture pend_mnum and tried_count[] */
+
+ n = fd_byte_count[qps->fd_last] ; /* copy up to last sig. byte */
+
+ qps->pend_mnum = qps_mnum_count ;
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if ((qps->tried_count[mnum] = qps->enabled_count[mnum]) != 0)
+ {
+ memcpy(&(qps->results[mnum].bytes), &(qps->enabled[mnum].bytes), n) ;
+ p_fds[mnum] = &(qps->results[mnum].fdset) ;
+ if (mnum < qps->pend_mnum)
+ qps->pend_mnum = mnum ;
+ }
+ else
+ p_fds[mnum] = NULL ;
+
+ /* Capture tried_fd_last and set initial pend_fd. */
+ qps->tried_fd_last = qps->fd_last ;
+ qps->pend_fd = 0 ;
+
+ /* Finally ready for the main event */
+ n = pselect(qps->fd_last + 1, p_fds[qps_read_mnum],
+ p_fds[qps_write_mnum],
+ p_fds[qps_error_mnum],
+ qtime2timespec(&ts, timeout),
+ (qps->signum != 0) ? &qps->sigmask : NULL) ;
+
+ qps->pend_count = (n >= 0) ? n : 0 ; /* avoid setting -ve pend_count */
+
+ /* if 1 or more pending results, then must have at least one pending mode */
+ assert((n <= 0) || (qps->pend_mnum < qps_mnum_count)) ;
+
+ /* Return appropriately, if we can */
+ if ((n >= 0) || (errno == EINTR))
+ return n ;
+
+ zabort_errno("Failed in pselect") ;
+} ;
+
+/* Dispatch the next errored/readable/writeable file, as returned by the
+ * most recent qps_pselect().
+ *
+ * Processes the errored files, then the readable and lastly the writeable.
+ *
+ * Processes one file per call of this function, by invoking the file's
+ * "action" routine.
+ *
+ * If a given file is ready in more than one mode, all modes will be processed,
+ * unless the action routine for one mode disables the file for other modes, or
+ * removes it from the selection.
+ *
+ * Returns the number of files left to process (after the one just processed).
+ */
+int
+qps_dispatch_next(qps_selection qps)
+{
+ int fd ;
+ qps_file qf ;
+ qps_mnum_t mnum ;
+
+ if (qps->pend_count == 0)
+ return 0 ; /* quit immediately of nothing to do. */
+
+ fd = qps->pend_fd ;
+ mnum = qps->pend_mnum ;
+
+ while (1)
+ {
+ fd = qps_next_fd_pending(&(qps->results[mnum]), fd, qps->tried_fd_last) ;
+ if (fd >= 0)
+ break ; /* easy if have another fd in current mode. */
+
+ do /* step to next mode that was not empty */
+ {
+ ++mnum ;
+ if (mnum >= qps_mnum_count)
+ zabort("Unexpectedly ran out of pending stuff") ;
+ } while (qps->tried_count[mnum] == 0) ;
+
+ qps->pend_mnum = mnum ; /* update mode */
+ fd = 0 ; /* back to the beginning */
+ } ;
+
+ qps->pend_count -= 1 ; /* one less pending */
+ qps->pend_fd = fd ; /* update scan */
+
+ qf = qps_file_lookup_fd(qps, fd, NULL) ;
+
+ dassert( ((qf->enabled_bits && qps_mbit(mnum)) != 0) &&
+ (qf->actions[mnum] != NULL) ) ;
+
+ qf->actions[mnum](qf, qf->file_info) ; /* dispatch the required action */
+
+ return qps->pend_count ;
+} ;
+
+/*==============================================================================
+ * qps_file structure handling
+ */
+
+/* Initialise qps_file structure -- allocating one if required.
+ *
+ * If a template is given, then the action functions are copied from there to
+ * the new structure. See above for discussion of action functions.
+ *
+ * Once initialised, the file may be added to a selection.
+ *
+ * Returns the qps_file.
+ */
+qps_file
+qps_file_init_new(qps_file qf, qps_file template)
+{
+ if (qf == NULL)
+ qf = XCALLOC(MTYPE_QPS_FILE, sizeof(struct qps_file)) ;
+ else
+ memset(qf, 0, sizeof(struct qps_file)) ;
+
+ /* Zeroising has initialised:
+ *
+ * selection -- NULL -- ditto
+ *
+ * file_info -- NULL -- is set by qps_add_file()
+ * fd -- unset -- ditto
+ *
+ * enabled_bits -- nothing enabled
+ *
+ * actions[] -- all set to NULL
+ */
+
+ if (template != NULL)
+ memcpy(qf->actions, template->actions, sizeof(qf->actions)) ;
+
+ return qf ;
+} ;
+
+/* Free dynamically allocated qps_file structure.
+ *
+ * It is the caller's responsibility to have removed it from any selection it
+ * may have been in.
+ */
+void
+qps_file_free(qps_file qf)
+{
+ assert(qf->selection == NULL) ; /* Mustn't be a selection member ! */
+
+ XFREE(MTYPE_QPS_FILE, qf) ;
+} ;
+
+/* Enable (or re-enable) file for the given mode.
+ *
+ * If the action argument is not NULL, set the action for the mode.
+ *
+ * NB: It is a FATAL error to enable a mode with a NULL action.
+ *
+ * NB: It is a FATAL error to enable modes for a file which is not in a
+ * selection.
+ */
+void
+qps_enable_mode(qps_file qf, qps_mnum_t mnum, qps_action* action)
+{
+ qps_mbit_t mbit = qps_mbit(mnum) ;
+ qps_selection qps = qf->selection ;
+
+ dassert((qps != NULL) && (qps_file_check(qps, qf))) ;
+ dassert(mnum <= qps_mnum_count) ;
+
+ if (action != NULL)
+ qf->actions[mnum] = action ;
+ else
+ dassert(qf->actions[mnum] != NULL) ;
+
+ if (qf->enabled_bits & mbit)
+ dassert(FD_ISSET(qf->fd, &(qps->enabled[mnum].fdset))) ;
+ else
+ {
+ dassert( ! FD_ISSET(qf->fd, &(qps->enabled[mnum].fdset))) ;
+ FD_SET(qf->fd, &(qps->enabled[mnum].fdset)) ;
+ ++qps->enabled_count[mnum] ;
+ qf->enabled_bits |= mbit ;
+ } ;
+} ;
+
+/* Set action for given mode -- does not enable/disable.
+ *
+ * May unset an action by setting it NULL !
+ *
+ * See above for discussion of action functions.
+ *
+ * NB: it is a fatal error to unset an action for a mode which is enabled.
+ */
+void
+qps_set_action(qps_file qf, qps_mnum_t mnum, qps_action* action)
+{
+ qps_selection qps = qf->selection ;
+
+ dassert((qps != NULL) && (qps_file_check(qps, qf))) ;
+ dassert(mnum < qps_mnum_count) ;
+
+ if (action == NULL)
+ passert((qf->enabled_bits & qps_mbit(mnum)) == 0) ;
+
+ qf->actions[mnum] = action ;
+} ;
+
+/* Disable file for one or more modes.
+ *
+ * If there are any pending pending results for the modes, those are discarded.
+ *
+ * Note that this is modestly "optimised" to deal with disabling a single mode.
+ * (Much of the time only the write mode will be being disabled !)
+ */
+
+static qps_mnum_t qps_first_mnum[qps_mbit(qps_mnum_count)] =
+ {
+ -1, /* 0 -> -1 -- no bit set */
+ 0, /* 1 -> 0 -- B0 is first bit */
+ 1, /* 2 -> 1 -- B1 is first bit */
+ 1, /* 3 -> 1 -- B1 is first bit */
+ 2, /* 4 -> 2 -- B2 is first bit */
+ 2, /* 5 -> 2 -- B2 is first bit */
+ 2, /* 6 -> 2 -- B2 is first bit */
+ 2 /* 7 -> 2 -- B2 is first bit */
+ } ;
+
+static qps_mbit_t qps_first_mbit[qps_mbit(qps_mnum_count)] =
+ {
+ 0, /* 0 -> 0 -- no bit set */
+ 1, /* 1 -> 1 -- B0 is first bit */
+ 2, /* 2 -> 2 -- B1 is first bit */
+ 2, /* 3 -> 2 -- B1 is first bit */
+ 4, /* 4 -> 4 -- B2 is first bit */
+ 4, /* 5 -> 4 -- B2 is first bit */
+ 4, /* 6 -> 4 -- B2 is first bit */
+ 4 /* 7 -> 4 -- B2 is first bit */
+ } ;
+
+CONFIRM(qps_mbit(qps_mnum_count) == 8) ;
+
+void
+qps_disable_modes(qps_file qf, qps_mbit_t mbits)
+{
+ qps_mnum_t mnum ;
+
+ qps_selection qps = qf->selection ;
+
+ dassert((qps != NULL) && (qps_file_check(qps, qf))) ;
+ dassert(mbits <= qps_all_mbits) ;
+
+ mbits &= qf->enabled_bits ; /* don't bother with any not enabled */
+ qf->enabled_bits ^= mbits ; /* unset what we're about to disable */
+
+ while (mbits != 0)
+ {
+ mnum = qps_first_mnum[mbits] ;
+
+ if (FD_ISSET(qf->fd, &(qps->enabled[mnum].fdset)))
+ {
+ FD_CLR(qf->fd, &(qps->enabled[mnum].fdset)) ;
+ dassert(qps->enabled_count[mnum] > 0) ;
+ --qps->enabled_count[mnum] ;
+ } ;
+ if ((qps->pend_count != 0) && (qps->tried_count[mnum] != 0)
+ && (FD_ISSET(qf->fd, &(qps->results[mnum].fdset))))
+ {
+ FD_CLR(qf->fd, &(qps->results[mnum].fdset)) ;
+ dassert(qps->pend_count > 0) ;
+ --qps->pend_count ;
+ } ;
+
+ mbits ^= qps_first_mbit[mnum] ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Handling the files vector.
+ *
+ * For small numbers of fd's, the files vector is kept as a list, in fd order.
+ * Files are found by binary chop, and added/removed by insert/delete in the
+ * list.
+ *
+ * For large numbers of fd's, the files vector is kept as an array, indexed by
+ * fd.
+ */
+
+/* Comparison function for binary chop */
+static int
+qps_fd_cmp(const int** pp_fd, const qps_file* p_qf)
+{
+ if (**pp_fd < (*p_qf)->fd)
+ return -1 ;
+ if (**pp_fd > (*p_qf)->fd)
+ return +1 ;
+ return 0 ;
+}
+
+/* Lookup/Insert file by file-descriptor.
+ *
+ * Inserts if insert argument is not NULL.
+ *
+ * Returns the file we found (if any) or the file we just inserted.
+ *
+ * NB: FATAL error to insert file with same fd as an existing one.
+ */
+static qps_file
+qps_file_lookup_fd(qps_selection qps, int fd, qps_file insert)
+{
+ qps_file qf ;
+
+ dassert((fd >= 0) && (fd < FD_SETSIZE)) ;
+
+ /* If we are expecting to insert, then now may be the time to change */
+ /* up to a directly address files vector. */
+ if ((insert != NULL) && !qps->fd_direct && (qps->fd_count > 9))
+ {
+ vector_index i ;
+ vector tmp ;
+
+ tmp = vector_move_here(NULL, &qps->files) ;
+
+ for (VECTOR_ITEMS(tmp, qf, i))
+ vector_set_item(&qps->files, qf->fd, qf) ;
+
+ vector_free(tmp) ;
+
+ qps->fd_direct = 1 ;
+ } ;
+
+ /* Look-up and perhaps insert. */
+ if (qps->fd_direct)
+ {
+ qf = vector_get_item(&qps->files, fd) ; /* NULL if not there */
+ if (insert != NULL)
+ {
+ if (qf != NULL)
+ zabort("File with given fd already exists in qps_selection") ;
+ qf = insert ;
+ vector_set_item(&qps->files, fd, insert) ;
+ } ;
+ }
+ else
+ {
+ int ret ;
+ vector_index i = vector_bsearch(&qps->files,
+ (vector_bsearch_cmp*)qps_fd_cmp,
+ &fd, &ret) ;
+ if (insert != NULL)
+ {
+ if (ret == 0)
+ zabort("File with given fd already exists in qps_selection") ;
+ qf = insert ;
+ vector_insert_item_here(&qps->files, i, ret, insert) ;
+ } ;
+ } ;
+
+ /* If we inserted, keep fd_count and fd_last up to date. */
+ if (insert != NULL)
+ {
+ ++qps->fd_count ;
+ if (fd > qps->fd_last)
+ qps->fd_last = fd ;
+ } ;
+
+ /* Sanity checking. */
+ dassert( (qf == NULL) || ((qps == qf->selection) && (fd == qf->fd)) ) ;
+
+ /* Return the file we found or inserted. */
+ return qf ;
+} ;
+
+/* Check file in selection.
+ *
+ * This looks up the file in the selection and performs a number of sanity
+ * checks.
+ *
+ * NB: it is a FATAL error if the file has an invalid fd, or the selection does
+ * not natch the file's selection, or the files selection is NULL.
+ *
+ * NB: it is a FATAL error for the file not to be in the selection when looked
+ * up by its fd.
+ *
+ * When (or if) returns, returns 1.
+ */
+static int
+qps_file_check(qps_selection qps, qps_file qf)
+{
+ qps_file qff ;
+ int fd = qf->fd ;
+
+ passert((fd >= 0) && (fd < FD_SETSIZE)
+ && (qf->selection != NULL) && (qps == qf->selection)) ;
+
+ if (qps->fd_direct)
+ qff = vector_get_item(&qps->files, fd) ; /* NULL if not there */
+ else
+ {
+ int ret ;
+ vector_index i = vector_bsearch(&qps->files,
+ (vector_bsearch_cmp*)qps_fd_cmp,
+ &fd, &ret) ;
+ if (ret == 0)
+ qff = vector_get_item(&qps->files, i) ;
+ else
+ qff = NULL ;
+ } ;
+
+ passert(qff == qf) ;
+
+ return 1 ;
+} ;
+
+/* Remove file from selection by file-descriptor.
+ *
+ * Returns the file we found and have removed (if any).
+ *
+ * TODO: should deleting a non existent file be fatal ?
+ */
+static qps_file
+qps_file_remove_fd(qps_selection qps, int fd)
+{
+ qps_file qf ;
+ int fd_last ;
+
+ dassert((fd >= 0) && (fd < FD_SETSIZE)) ;
+
+ /* Look-up and remove. */
+ if (qps->fd_direct)
+ {
+ qf = vector_unset_item(&qps->files, fd) ; /* NULL if not there */
+ fd_last = vector_end(&qps->files) - 1 ;
+ }
+ else
+ {
+ int ret ;
+ vector_index i = vector_bsearch(&qps->files,
+ (vector_bsearch_cmp*)qps_fd_cmp,
+ &fd, &ret) ;
+ if (ret == 0)
+ {
+ qps_file qf_last ;
+ qf = vector_delete_item(&qps->files, i) ;
+
+ qf_last = vector_get_last_item(&qps->files) ;
+ if (qf_last == NULL)
+ fd_last = -1 ;
+ else
+ fd_last = qf_last->fd ;
+ }
+ else
+ qf = NULL ;
+ } ;
+
+ /* If we removed, keep fd_count and fd_last up to date. */
+ /* Also, remove the fd from all vectors. */
+ if (qf != NULL)
+ {
+ dassert(qps->fd_count != 0) ;
+ ++qps->fd_count ;
+
+ dassert( ((qps->fd_count != 0) && (fd_last >= 0)) ||
+ ((qps->fd_count == 0) && (fd_last < 0)) ) ;
+
+ qps->fd_last = (fd_last >= 0) ? fd_last : 0 ;
+
+ qps_disable_modes(qf, qps_all_mbits) ;
+ } ;
+
+ /* Return the file we found and have removed. */
+ return qf ;
+} ;
+
+/* Remove file from selection.
+ *
+ * NB: FATAL error if file is not in the selection, or the file-descriptor
+ * is invalid (or refers to some other file !).
+ */
+static void
+qps_file_remove(qps_selection qps, qps_file qf)
+{
+ qps_file qfd ;
+
+ passert((qf->fd >= 0) && (qf->fd < FD_SETSIZE) && (qps == qf->selection)) ;
+
+ qfd = qps_file_remove_fd(qps, qf->fd) ;
+
+ passert(qfd == qf) ;
+} ;
+
+ /*==============================================================================
+ * fd_super_set support.
+ *
+ * For large sets of file descriptors something faster than testing for all
+ * possible bits is required. The fd_super_set assumes that the fd_set is a
+ * straightforward bit-vector, and overlays a 32-bit word array and a byte
+ * array over that.
+ *
+ * Cannot tell if the underlying bit vector is arranged in bytes, or some
+ * longer words. Cannot tell if words are held big or little endian. Cannot
+ * tell if lowest numbered fd will be highest or lowest in whatever unit it's
+ * held in.
+ *
+ * So... we have maps for fd -> our word index, and fd -> byte index.
+ *
+ * we have a map for fd -> mask for bit used in its byte.
+ *
+ * We require that fds will be numbered consistently in bytes. That is,
+ * every (fd mod 8) == n will appear in the same bit in a byte, for all fd (
+ * for n = 0..7). This allows the final map, which takes a byte value and
+ * returns the lowest numbered fd in the byte, mod 8.
+ *
+ * To copy all the bytes for all descriptors 0..fd, also construct
+ * fd_byte_count[] -- which copes with the fact that on a big-endian machine
+ * it is possible that descriptor fd - 8 may be in a higher numbered byte than
+ * fd ! Using this count assumes that the underlying system really does not
+ * look at bits beyond the given maximum fd.
+ */
+
+static short fd_word_map[FD_SETSIZE] ; /* maps fd to word index */
+static short fd_byte_map[FD_SETSIZE] ; /* maps fd to byte index */
+static uint8_t fd_bit_map [FD_SETSIZE] ; /* maps fd to bit in byte */
+
+static int8_t fd_first_map[256] ; /* maps byte value to 0..7, where that */
+ /* is the lowest fd bit set in byte. */
+
+/* Scan for next fd in given fd set, and clear it.
+ *
+ * Starts at the given fd, will not consider anything above fd_last.
+ *
+ * Returns next fd, or -1 if none.
+ */
+static int
+qps_next_fd_pending(fd_super_set* pending, int fd, int fd_last)
+{
+ uint8_t b ;
+
+ while (pending->words[fd_word_map[fd]] == 0) /* step past zero words */
+ {
+ fd = (fd & ~ 0x001F) + FD_WORD_BITS ;
+ /* step to start of next word */
+ if (fd > fd_last)
+ return -1 ; /* quit if past last */
+ } ;
+
+ fd &= ~0x0007 ; /* step back to first in byte */
+ while (1)
+ {
+ b = pending->bytes[fd_byte_map[fd]] ;
+ /* byte associated with fd */
+ if (b != 0)
+ break ; /* stop looking */
+
+ fd += 8 ;
+ if (fd > fd_last)
+ return -1 ;
+ } ;
+
+ fd += fd_first_map[b] ;
+
+ dassert(fd <= fd_last) ;
+ dassert((b & fd_bit_map[fd]) == fd_bit_map[fd]) ;
+
+ FD_CLR(fd, &pending->fdset) ;
+
+ dassert((b ^ fd_bit_map[fd]) == pending->bytes[fd_byte_map[fd]]) ;
+
+ return fd ;
+} ;
+
+/* Make a map of the fd_super_set.
+ *
+ * The form of an fd_set is not defined. This code verifies that it is, in
+ * fact a bit vector, and hence that the fd_super_set works here !
+ *
+ * It is a FATAL error if things don't work out.
+ */
+static void
+qps_make_super_set_map(void)
+{
+ fd_super_set test ;
+ int fd, i, iw, ib ;
+
+ /* (1) check that a zeroised fd_super_set is an empty one. */
+ qps_super_set_zero(&test, 1) ;
+
+ for (fd = 0 ; fd < FD_SETSIZE ; ++fd)
+ if (FD_ISSET(fd, &test.fdset))
+ zabort("Zeroised fd_super_set is not empty") ;
+
+ /* (2) check that zeroising the fd_set doesn't change things */
+ FD_ZERO(&test.fdset) ;
+ for (iw = 0 ; iw < FD_SUPER_SET_WORD_SIZE ; ++iw)
+ if (test.words[iw] != 0)
+ zabort("Zeroised fd_super_set is not all zero words") ;
+
+ /* (3) check that setting one fd sets one bit, and construct the */
+ /* fd_word_map[], fd_byte_map[] and fd_bit_map[]. */
+ for (fd = 0 ; fd < FD_SETSIZE ; ++fd)
+ {
+ fd_word_t w ;
+
+ FD_SET(fd, &test.fdset) ;
+
+ w = 0 ;
+ for (iw = 0 ; iw < FD_SUPER_SET_WORD_SIZE ; ++iw)
+ {
+ if (test.words[iw] != 0)
+ {
+ if (w != 0)
+ zabort("FD_SET set a bit in more than one word") ;
+
+ w = test.words[iw] ;
+ if ((w == 0) || ((w & (w - 1)) != 0))
+ zabort("FD_SET set more than one bit in a word") ;
+
+ fd_word_map[fd] = iw ;
+
+ ib = iw * FD_WORD_BYTES ;
+ while (test.bytes[ib] == 0)
+ {
+ ++ib ;
+ if (ib >= ((iw + 1) * FD_WORD_BYTES))
+ zabort("FD_SET set something beyond the expected bytes") ;
+ } ;
+ fd_byte_map[fd] = ib ;
+ fd_bit_map[fd] = test.bytes[ib] ;
+ } ;
+ } ;
+
+ if (w == 0)
+ zabort("FD_SET did not set any bit in any word") ;
+
+ FD_CLR(fd, &test.fdset) ;
+
+ for (iw = 0 ; iw < FD_SUPER_SET_WORD_SIZE ; ++iw)
+ if (test.words[iw] != 0)
+ zabort("FD_CLR did not leave the fd_super_set empty") ;
+ } ;
+
+ /* (4) check the fd_byte_map. */
+ /* make sure that have 8 contiguous fd to a byte. */
+ /* make sure that have 32 contiguous fd to a word. */
+
+ for (fd = 0 ; fd < FD_SETSIZE ; fd += 8)
+ {
+ int fds ;
+ ib = fd_byte_map[fd] ;
+ iw = fd_word_map[fd] ;
+
+ /* Must share the same byte as the next 7 fds */
+ for (fds = fd + 1 ; fds < (fd + 8) ; ++fds)
+ if (fd_byte_map[fds] != ib)
+ zabort("Broken fd_byte_map -- not 8 contiguous fd's in a byte") ;
+
+ /* Must not share the same byte as any other set of 8 fd's */
+ for (fds = 0 ; fds < FD_SETSIZE ; fds += 8)
+ if ((fd_byte_map[fds] == ib) && (fds != fd))
+ zabort("Broken fd_byte_map -- fd's not in expected bytes") ;
+
+ /* Must be one of the bytes in the current word's fd's */
+ if ( (ib < (iw * FD_WORD_BYTES)) || (ib >= ((iw + 1) * FD_WORD_BYTES)) )
+ zabort("Broken fd_byte_map -- fd's not in expected words") ;
+ } ;
+
+ /* (5) check the fd_bit_map */
+ /* make sure that all fd mod 8 map to the same byte value */
+
+ for (i = 0 ; i < 8 ; ++i)
+ {
+ uint8_t b = fd_bit_map[i] ;
+ for (fd = 8 + i ; fd < FD_SETSIZE ; fd += 8)
+ if (fd_bit_map[fd] != b)
+ zabort("Broken fd_bit_map -- inconsistent bit mapping") ;
+ } ;
+
+ /* (6) construct fd_first_map, to get lowest numbered fd (mod 8) from */
+ /* a given byte value. */
+
+ for (i = 0 ; i < 256 ; ++i)
+ fd_first_map[i] = -1 ;
+
+ for (i = 0 ; i < 8 ; ++i)
+ {
+ uint8_t fdb = fd_bit_map[i] ;
+ uint8_t b ;
+ for (b = 0 ; b < 256 ; ++b)
+ if ((fd_first_map[b] == -1) && ((b & fdb) != 0))
+ fd_first_map[b] = i ;
+ } ;
+
+ /* (7) construct fd_byte_count[] -- number of bytes required to */
+ /* include fds 0..fd. */
+
+ i = 0 ;
+ for (fd = 0 ; fd < FD_SETSIZE ; ++fd)
+ {
+ fd_byte_count[fd] = fd_byte_map[fd] + 1 ;
+ if (fd_byte_count[fd] < i)
+ fd_byte_count[fd] = i ;
+ else
+ i = fd_byte_count[fd] ;
+ } ;
+
+ /* Phew -- we're all set now */
+ qps_super_set_map_made = 1 ;
+} ;
+
+/* Zeroise 'n' contiguous fd_super_sets
+ *
+ * NB: this MUST be used in place of FD_ZERO because the fd_set may be shorter
+ * than the overlayed words/bytes vectors.
+ *
+ * NB: it is CONFIRMED elsewhere that the fd_set is no longer than the overlays.
+ */
+static void
+qps_super_set_zero(fd_super_set* p_set, int n)
+{
+ memset(p_set, 0, SIZE(fd_super_set, n)) ;
+} ;
diff --git a/lib/qpselect.h b/lib/qpselect.h
new file mode 100644
index 00000000..03180f23
--- /dev/null
+++ b/lib/qpselect.h
@@ -0,0 +1,197 @@
+/* Quagga pselect support -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_QPSELECT_H
+#define _ZEBRA_QPSELECT_H
+
+#include <sys/select.h>
+#include <errno.h>
+
+#include "zassert.h"
+#include "qtime.h"
+#include "vector.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Quagga pselect -- qps_xxxx
+ *
+ * Here and in qpselect.c is a data structure for managing multiple file
+ * descriptors and running pselect to wait for I/O activity and to multiplex
+ * between the file descriptors.
+ */
+
+enum qps_mnum /* "mode" numbers: error/read/write */
+{
+ qps_mnum_first = 0,
+
+ qps_error_mnum = 0,
+ qps_read_mnum = 1,
+ qps_write_mnum = 2,
+
+ qps_mnum_count = 3
+} ;
+
+typedef enum qps_mnum qps_mnum_t ;
+
+#define qps_mbit(mnum) (1 << mnum)
+
+enum qps_mbits /* "mode" bits: error/read/write */
+{
+ qps_error_mbit = qps_mbit(qps_error_mnum),
+ qps_read_mbit = qps_mbit(qps_read_mnum),
+ qps_write_mbit = qps_mbit(qps_write_mnum),
+
+ qps_all_mbits = qps_mbit(qps_mnum_count) - 1
+};
+
+typedef enum qps_mbits qps_mbit_t ;
+
+/* Forward references */
+typedef struct qps_selection* qps_selection ;
+typedef struct qps_file* qps_file ;
+
+/*==============================================================================
+ * fd_super_set.
+ *
+ * To speed up scanning of large fd_set's this structure overlays a 32-bit
+ * word and a byte array over the (assumed) fd_set bit vector.
+ *
+ * There is no guarantee that FD_SETSIZE is a multiple of 32 (or of 8, for
+ * that matter) -- so some care must be taken.
+ */
+
+typedef uint32_t fd_word_t ;
+
+#define FD_WORD_BITS 32
+#define FD_WORD_BYTES (FD_WORD_BITS / 8)
+
+CONFIRM(FD_WORD_BITS == (FD_WORD_BYTES * 8)) ; /* for completeness */
+
+#define FD_SUPER_SET_WORD_SIZE ((FD_SETSIZE + FD_WORD_BITS - 1) / FD_WORD_BITS)
+#define FD_SUPER_SET_BYTE_SIZE ((FD_SETSIZE + FD_WORD_BITS - 1) / 8)
+
+/* Make sure that the overlay is at least as big as the fd_set ! */
+CONFIRM(FD_SUPER_SET_BYTE_SIZE >= sizeof(fd_set)) ;
+
+typedef union /* see qps_make_super_set_map() */
+{
+ fd_word_t words[FD_SUPER_SET_WORD_SIZE] ;
+ uint8_t bytes[FD_SUPER_SET_BYTE_SIZE] ;
+ fd_set fdset ;
+} fd_super_set ;
+
+/*==============================================================================
+ * Action function.
+ *
+ * Each file has three action functions, to be called in qps_dispatch_next()
+ * when pselect() has reported error/read/write for the file.
+ *
+ * For further discussion, see: qps_file_init.
+ */
+
+typedef void qps_action(qps_file qf, void* file_info) ;
+
+/*==============================================================================
+ * Data Structures.
+ */
+
+typedef fd_super_set fd_full_set[qps_mnum_count] ;
+
+struct qps_selection
+{
+ int fd_count ; /* number of fds we are looking after */
+ int fd_direct ; /* direct lookup in vector or not */
+
+ struct vector files ; /* mapping fd to qps_file */
+
+ int fd_last ; /* highest numbered fd we are looking after */
+ int enabled_count[qps_mnum_count] ; /* no. enabled fds in each mode */
+ fd_full_set enabled ; /* bit vectors for select enabled stuff */
+
+ int tried_fd_last ; /* highest numbered fd on last pselect */
+ int tried_count[qps_mnum_count] ; /* enabled_count on last pselect */
+ fd_full_set results ; /* last set of results from pselect */
+
+ int pend_count ; /* results pending (if any) */
+ qps_mnum_t pend_mnum ; /* error/read/write mode pending (if any) */
+ int pend_fd ; /* fd pending (if any) */
+
+ int signum ; /* signal that sigmask is enabling -- 0 => none */
+ sigset_t sigmask ; /* sigmask to use for duration of pselect */
+} ;
+
+struct qps_file
+{
+ qps_selection selection ;
+
+ void* file_info ;
+ int fd ;
+
+ qps_mbit_t enabled_bits ;
+
+ qps_action* actions[qps_mnum_count] ;
+} ;
+
+/*==============================================================================
+ * qps_selection handling
+ */
+
+qps_selection
+qps_selection_init_new(qps_selection qps) ;
+
+void
+qps_add_file(qps_selection qps, qps_file qf, int fd, void* file_info) ;
+
+void
+qps_remove_file(qps_file qf) ;
+
+void
+qps_set_signal(qps_selection qps, int signum, sigset_t sigmask) ;
+
+int
+qps_pselect(qps_selection qps, qtime_t timeout) ;
+
+int
+qps_dispatch_next(qps_selection qps) ;
+
+/*==============================================================================
+ * qps_file structure handling
+ */
+
+qps_file
+qps_file_init_new(qps_file qf, qps_file template) ;
+
+void
+qps_file_free(qps_file qf) ;
+
+void
+qps_enable_mode(qps_file qf, qps_mnum_t mnum, qps_action* action) ;
+
+void
+qps_set_action(qps_file qf, qps_mnum_t mnum, qps_action* action) ;
+
+void
+qps_disable_modes(qps_file qf, qps_mbit_t mbits) ;
+
+#endif /* _ZEBRA_QPSELECT_H */
diff --git a/lib/qtimers.c b/lib/qtimers.c
new file mode 100644
index 00000000..6d3932fc
--- /dev/null
+++ b/lib/qtimers.c
@@ -0,0 +1,317 @@
+/* Quagga timers support -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include "zassert.h"
+#include "qtimers.h"
+#include "memory.h"
+#include "heap.h"
+
+/*==============================================================================
+ * Quagga Timers -- qtimer_xxxx
+ *
+ * Here and in qtimers.h is a data structure for managing multiple timers
+ * each with an action to be executed when the timer expires.
+ *
+ * The qtime_pile structure manages a "pile" of qtimer structures which are
+ * waiting for the right time to go off.
+ *
+ * NB: it is ASSUMED that a qtime_pile will be private to the thread in which
+ * it is created and used.
+ *
+ * There is NO mutex handling here.
+ *
+ * Timers are triggered by calling qtimer_dispatch_next(). This is given the
+ * current qtimer time (see below), and it dispatches the first timer whose
+ * time has come (or been passed). Dispatching a timer means calling its
+ * action function (see below). Each call of qtimer_dispatch_next triggers at
+ * most one timer.
+ *
+ * Time Base
+ * ---------
+ *
+ * The time base for qtimers is the monotonic time provided in qtime.c/.h.
+ *
+ * The qtimer_time_now(), qtimer_time_future(), timer_time_from_realtime(),
+ * qtimer_time_from_timeofday() functions return qtimer times.
+ *
+ * Action Functions
+ * ----------------
+ *
+ * There is a separate action function for each timer.
+ *
+ * When the action function is called it is passed the qtimer structure, the
+ * timer_info pointer from that structure and the time which triggered the
+ * timer (which may, or may not, be the current qtimer time).
+ *
+ * During an action function timers may be set/unset, actions changed, and so
+ * on... there are no restrictions.
+ */
+
+static int
+qtimer_cmp(qtimer* a, qtimer* b) /* the heap discipline */
+{
+ if ((**a).time < (**b).time)
+ return -1 ;
+ if ((**a).time > (**b).time)
+ return +1 ;
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * qtimer_pile handling
+ */
+
+/* Initialise a timer pile -- allocating it if required.
+ *
+ * Returns the qtimer_pile.
+ */
+qtimer_pile
+qtimer_pile_init_new(qtimer_pile qtp)
+{
+ if (qtp == NULL)
+ qtp = XCALLOC(MTYPE_QTIMER_PILE, sizeof(struct qtimer_pile)) ;
+ else
+ memset(qtp, 0, sizeof(struct qtimer_pile)) ;
+
+ /* Zeroising has initialised:
+ *
+ * timers -- invalid heap -- need to properly initialise
+ *
+ * unset_pending -- NULL -- nothing pending
+ */
+
+ /* Eclipse flags offsetof(struct qtimer, backlink) as a syntax error :-( */
+ typedef struct qtimer qtimer_t ;
+
+ heap_init_new_backlinked(&qtp->timers, 0, (heap_cmp*)qtimer_cmp,
+ offsetof(qtimer_t, backlink)) ;
+ return qtp ;
+} ;
+
+/* Dispatch the next timer whose time is <= the given "upto" time.
+ *
+ * The upto time must be a qtimer time (!) -- see qtimer_time_now().
+ *
+ * The upto argument allows the caller to get a qtimer_time_now() value, and
+ * then process all timers upto that time.
+ *
+ * Returns true <=> dispatched a timer, and there may be more to do.
+ * false <=> nothing to do (and nothing done).
+ */
+int
+qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_t upto)
+{
+ qtimer qtr ;
+
+ if (qtp->unset_pending != NULL)
+ qtimer_unset(qtp->unset_pending) ; /* just in case recurse through here */
+
+ qtr = heap_top_item(&qtp->timers) ;
+ if ((qtr != NULL) && (qtr->time <= upto))
+ {
+ qtp->unset_pending = qtr ; /* delay unset of top item, pro tem...*/
+
+ qtr->action(qtr, qtr->timer_info, upto) ;
+
+ if (qtp->unset_pending != NULL) /* ...now must unset if not yet done */
+ qtimer_unset(qtp->unset_pending) ;
+
+ return 1 ;
+ }
+ else
+ return 0 ;
+} ;
+
+/* Ream out (another) item from qtimer_pile.
+ *
+ * If pile is empty, release the qtimer_pile structure, if required.
+ *
+ * Useful for emptying out and discarding a pile of timers:
+ *
+ * while ((p_qtr = qtimer_pile_ream(qtp, 1)))
+ * ... do what's required to release the item p_qtr
+ *
+ * Returns NULL when timer pile is empty (and has been released, if required).
+ *
+ * If the timer pile is not released, it may be reused without reinitialisation.
+ *
+ * NB: once reaming has started, the timer pile MUST NOT be used for anything,
+ * and the process MUST be run to completion.
+ */
+qtimer
+qtimer_pile_ream(qtimer_pile qtp, int free_structure)
+{
+ qtimer qtr ;
+
+ qtr = heap_ream_keep(&qtp->timers) ; /* ream, keeping the heap structure */
+ if (qtr != NULL)
+ qtr->active = 0 ; /* already removed from pile */
+ else
+ {
+ if (free_structure)
+ XFREE(MTYPE_QTIMER_PILE, qtp) ;
+ else
+ qtp->unset_pending = NULL ; /* heap is empty, so this is last thing */
+ /* to be tidied up. */
+ } ;
+
+ return qtr ;
+} ;
+
+/*==============================================================================
+ * qtimer handling
+ */
+
+/* Initialise qtimer structure -- allocating one if required.
+ *
+ * Associates qtimer with the given pile of timers, and sets up the action and
+ * the timer_info ready for use.
+ *
+ * Once initialised, the timer may be set.
+ *
+ * Returns the qtimer.
+ */
+qtimer
+qtimer_init_new(qtimer qtr, qtimer_pile qtp,
+ qtimer_action* action, void* timer_info)
+{
+ if (qtr == NULL)
+ qtr = XCALLOC(MTYPE_QTIMER, sizeof(struct qtimer)) ;
+ else
+ memset(qtr, 0, sizeof(struct qtimer)) ;
+
+ /* Zeroising has initialised:
+ *
+ * pile -- NULL -- not in any pile (yet)
+ * backlink -- unset
+ *
+ * active -- not active
+ *
+ * time -- unset
+ * action -- NULL -- no action set (yet)
+ * timer_info -- NULL -- no timer info set (yet)
+ */
+
+ qtr->pile = qtp ;
+ qtr->action = action ;
+ qtr->timer_info = timer_info ;
+
+ return qtr ;
+} ;
+
+/* Free given timer.
+ *
+ * Unsets it first if it is active.
+ */
+void
+qtimer_free(qtimer qtr)
+{
+ if (qtr->active)
+ qtimer_unset(qtr) ;
+
+ XFREE(MTYPE_QTIMER, qtr) ;
+} ;
+
+/* Set pile in which given timer belongs.
+ *
+ * Unsets the timer if active in another pile.
+ * (Does nothing if active in the "new" pile.)
+ */
+void
+qtimer_set_pile(qtimer qtr, qtimer_pile qtp)
+{
+ if ((qtr->active) && (qtr->pile != qtp))
+ qtimer_unset(qtr) ;
+ qtr->pile = qtp ;
+}
+
+/* Set action for given timer.
+ */
+void
+qtimer_set_action(qtimer qtr, qtimer_action* action)
+{
+ qtr->action = action ;
+} ;
+
+/* Set timer_info for given timer.
+ */
+void
+qtimer_set_info(qtimer qtr, void* timer_info)
+{
+ qtr->timer_info = timer_info ;
+} ;
+
+/* Set given timer.
+ *
+ * Setting a -ve time => qtimer_unset.
+ *
+ * If the timer is already active, sets the new time & updates pile.
+ *
+ * Otherwise, sets the time and adds to pile -- making timer active.
+ */
+void
+qtimer_set(qtimer qtr, qtime_t when)
+{
+ qtimer_pile qtp ;
+
+ if (when < 0)
+ return qtimer_unset(qtr) ;
+
+ qtp = qtr->pile ;
+ dassert(qtp != NULL) ;
+
+ qtr->time = when ;
+
+ if (qtr->active)
+ {
+ heap_update_item(&qtp->timers, qtr) ; /* update in heap */
+ if (qtr == qtp->unset_pending)
+ qtp->unset_pending = NULL ; /* dealt with. */
+ }
+ else
+ {
+ heap_push_item(&qtp->timers, qtr) ; /* add to heap */
+ qtr->active = 1 ;
+ } ;
+} ;
+
+/* Unset given timer
+ *
+ * If the timer is active, removes from pile and sets inactive.
+ */
+void
+qtimer_unset(qtimer qtr)
+{
+ if (qtr->active)
+ {
+ qtimer_pile qtp = qtr->pile ;
+ dassert(qtp != NULL) ;
+
+ heap_delete_item(&qtp->timers, qtr) ;
+ if (qtr == qtp->unset_pending)
+ qtp->unset_pending = NULL ;
+
+ qtr->active = 0 ;
+ } ;
+} ;
diff --git a/lib/qtimers.h b/lib/qtimers.h
new file mode 100644
index 00000000..65154a7a
--- /dev/null
+++ b/lib/qtimers.h
@@ -0,0 +1,172 @@
+/* Quagga timers support -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_QTIMERS_H
+#define _ZEBRA_QTIMERST_H
+
+#include "zassert.h"
+#include "qtime.h"
+#include "heap.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Quagga Timers -- qtimer_xxxx
+ *
+ * Here and in qtimers.c is a data structure for managing multiple timers
+ * each with an action to be executed when the timer expires.
+ */
+
+/*==============================================================================
+ * Data Structures.
+ */
+
+typedef struct qtimer* qtimer ;
+typedef struct qtimer_pile* qtimer_pile ;
+
+typedef void (qtimer_action)(qtimer qtr, void* timer_info, qtime_t when) ;
+
+struct qtimer
+{
+ qtimer_pile pile ;
+ heap_backlink_t backlink ;
+
+ int active ; /* timer is active in the current pile. */
+
+ qtime_t time ;
+ qtimer_action* action ;
+ void* timer_info ;
+} ;
+
+struct qtimer_pile
+{
+ struct heap timers ;
+
+ qtimer unset_pending ;
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+
+qtimer_pile
+qtimer_pile_init_new(qtimer_pile qtp) ;
+
+int
+qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_t upto) ;
+
+qtimer
+qtimer_pile_ream(qtimer_pile qtp, int free_structure) ;
+
+Inline qtime_t
+qtimer_time_now() ;
+
+Inline qtime_t
+qtimer_time_future(qtime_t interval) ;
+
+Inline qtime_t
+qtimer_time_from_realtime(qtime_t realtime) ;
+
+Inline qtime_t
+qtimer_time_from_timeofday(qtime_t timeofday) ;
+
+qtimer
+qtimer_init_new(qtimer qtr, qtimer_pile qtp,
+ qtimer_action* action, void* timer_info) ;
+void
+qtimer_set_pile(qtimer qtr, qtimer_pile qtp) ;
+
+void
+qtimer_set_action(qtimer qtr, qtimer_action* action) ;
+
+void
+qtimer_set_info(qtimer qtr, void* timer_info) ;
+
+void
+qtimer_free(qtimer qtr) ;
+
+void
+qtimer_set(qtimer qtr, qtime_t when) ;
+
+void
+qtimer_unset(qtimer qtr) ;
+
+Inline void
+qtimer_add(qtimer qtr, qtime_t interval) ;
+
+Inline qtime_t
+qtimer_get(qtimer qtr) ;
+
+/*==============================================================================
+ * Inline functions
+ */
+
+/* The current time according to the qtimer time-base (monotonic time).
+ */
+Inline qtime_t
+qtimer_time_now()
+{
+ return qt_get_monotonic() ;
+}
+
+/* What the qtimer time will be 'interval' qtime_t units from now..
+ */
+Inline qtime_t
+qtimer_time_future(qtime_t interval)
+{
+ return qtimer_time_now() + interval ;
+} ;
+
+/* Get a qtimer time from a CLOCK_REALTIME time
+ */
+Inline qtime_t
+qtimer_time_from_realtime(qtime_t realtime)
+{
+ return qt_realtime2monotonic(realtime) ;
+} ;
+
+/* Get a qtimer time from a gettimeofday() time (in case != CLOCK_REALTIME)
+ */
+Inline qtime_t
+qtimer_time_from_timeofday(qtime_t timeofday)
+{
+ return qt_timeofday2monotonic(timeofday) ;
+} ;
+
+/* Set given timer to given time later than *its* current time.
+ */
+Inline void
+qtimer_add(qtimer qtr, qtime_t interval)
+{
+ qtimer_set(qtr, qtimer_get(qtr) + interval);
+} ;
+
+/* Get the given timer's time.
+ */
+Inline qtime_t
+qtimer_get(qtimer qtr)
+{
+ return qtr->time ;
+} ;
+
+#endif /* _ZEBRA_QPSELECT_H */