[Xorp-hackers] Libxorp proposal: service.{hh,cc}

Orion Hodson orion@icir.org
Thu, 09 Oct 2003 08:19:22 -0700


This is a multipart MIME message.

--==_Exmh_16833281770
Content-Type: text/plain; charset=us-ascii


Attached is a patch for libxorp to add some new classes for "services".  A 
service is a class that provides some functionality to a process and requires 
transition time to startup, shutdown, etc.  The code is fairly trivial, it 
just keeps track of state, and nominally informs an observer.

Since I've found myself re-implementing this pattern in multiple places in RIP 
and think it might be useful other places, I've factored it out. The code has 
a little bit of fat relative to the functionality RIP uses, ie PAUSE, PAUSING, 
RESUMING states, but that may be useful elsewhere.

The is a patch below if you want to take a look.  Comments welcome.

Cheers
- Orion



--==_Exmh_16833281770
Content-Type: text/plain ; name="service.diff"; charset=us-ascii
Content-Description: service.diff
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="service.diff"

Index: libxorp/service.hh
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: libxorp/service.hh
diff -N libxorp/service.hh
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ libxorp/service.hh	9 Oct 2003 07:04:56 -0000
@@ -0,0 +1,222 @@
+// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-
+
+// Copyright (c) 2001-2003 International Computer Science Institute
+//
+// Permission is hereby granted, free of charge, to any person obtaining=
 a
+// copy of this software and associated documentation files (the "Softwa=
re")
+// to deal in the Software without restriction, subject to the condition=
s
+// listed in the XORP LICENSE file. These conditions include: you must
+// preserve this copyright notice, and you cannot mention the copyright
+// holders in advertising related to the Software without their permissi=
on.
+// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. Th=
is
+// notice is a summary of the XORP LICENSE file; the license in that fil=
e is
+// legally binding.
+
+// $XORP$
+
+#ifndef __LIBXORP_SERVICE_HH__
+#define __LIBXORP_SERVICE_HH__
+
+/**
+ * Enumeration of states objects derived from ServiceBase may be in.
+ */
+enum ServiceStatus {
+    READY		=3D 0x001,	// Ready for startup
+    STARTING		=3D 0x002,	// Starting up
+    RUNNING		=3D 0x004,	// Running, service operational
+    PAUSING		=3D 0x008,	// Transitioning to paused state
+    PAUSED		=3D 0x010,	// Paused, non-operational
+    RESUMING		=3D 0x020,	// Resuming from pause
+    SHUTTING_DOWN	=3D 0x040,	// Transitioning to shutdown
+    SHUTDOWN		=3D 0x080,	// Shutdown, non-operational
+    FAILED		=3D 0x100,	// Failed, non-operational
+    ALL			=3D READY | STARTING | RUNNING |
+    			  PAUSING | PAUSED | RESUMING |
+			  SHUTTING_DOWN | SHUTDOWN | FAILED
+};
+
+/**
+ * Get text description of enumerated service status.
+ *
+ * @param s service status to recover name for.
+ */
+const char* service_status_name(ServiceStatus s);
+
+class ServiceChangeObserverBase;
+
+/**
+ * @short Base class for Services.
+ *
+ * This class provides a base for services within Xorp processes.  A
+ * service instance is an entity that can logically started and
+ * stopped and typically needs some asynchronous processing in order
+ * to start and stop.  An example service within a routing process
+ * would be a RIB communicator service, which needs to co-ordinate
+ * with the RIB which is within a different process and may be on a
+ * different machine.
+ *
+ * A service may be started and shutdown by calling @ref startup() and
+ * @ref shutdown().  The status of a service may be determined by
+ * calling @ref status().  Additional notes on the current status may be=

+ * obtained by calling @ref status_note().
+ *
+ * Synchronous service status changes may be received through the @ref
+ * ServiceChangeObserverBase class.  Instances of objects derived from
+ * this class can register for status change notifications in a
+ * Service instance by calling @ref set_observer().
+ */
+class ServiceBase {
+public:
+    ServiceBase();
+
+    virtual ~ServiceBase() =3D 0;
+
+    /**
+     * Start service.  Service should transition from READY to
+     * STARTING immediately and onto RUNNING or FAILED in the near
+     * future.
+     */
+    virtual void startup() =3D 0;
+
+    /**
+     * Shutdown service.  Service should transition from RUNNING to
+     * SHUTTING_DOWN immediately and onto SHUTDOWN or FAILED in the
+     * near future.
+     */
+    virtual void shutdown() =3D 0;
+
+    /**
+     * Reset service.  Service should transition in READY from
+     * whichever state it is in.
+     *
+     * The default implementation always returns false as there is no
+     * default behaviour.
+     *
+     * @return true on success, false on failure.
+     */
+    virtual bool reset();
+
+    /**
+     * Pause service.  Service should transition from RUNNING to
+     * PAUSING and asynchronously into PAUSED.
+     *
+     * The default implementation always returns false as there is no
+     * default behaviour.
+     *
+     * @return true on success, false on failure.
+     */
+    virtual bool pause();
+
+    /**
+     * Resume paused service.  Service should transition from PAUSED
+     * to PAUSING and asynchronously into RUNNING.
+     *
+     * The default implementation always returns false as there is no
+     * default behaviour.
+     *
+     * @return true on success, false on failure.
+     */
+    virtual bool resume();
+
+    /**
+     * Get the current status.
+     */
+    inline ServiceStatus status() const			{ return _status; }
+
+    /**
+     * Get annotation associated with current status.  The annotation wh=
en
+     * set is an explanation of the state, ie "waiting for Y"
+     */
+    inline const string& status_note() const		{ return _note; }
+
+    /**
+     * Get a character representation of the current service status.
+     */
+    const char* status_name() const;
+
+    /**
+     * Set service status change observer.  The observer will receive
+     * synchronous notifications of changes in service state.
+     *
+     * @param so service change observer to add.
+     * @return true on success, false if an observer is already set.
+     */
+    bool set_observer(ServiceChangeObserverBase* so);
+
+    /**
+     * Remove service status change observer.
+     *
+     * @param so observer to remove.
+     * @return true on success, false if supplied observer does match
+     * the last set observer.
+     */
+    bool unset_observer(ServiceChangeObserverBase* so);
+
+protected:
+    /**
+     * Set current status.
+     *
+     * @param status new status.
+     * @param note comment on new service status.
+     */
+    void set_status(ServiceStatus status, const string& note);
+
+    /**
+     * Set current status and clear status note.
+     *
+     * @param status new status.
+     */
+    void set_status(ServiceStatus status);
+
+protected:
+    ServiceStatus	 	_status;
+    string		 	_note;
+    ServiceChangeObserverBase*	_observer;
+};
+
+/**
+ * @short Base class for service status change observer.
+ */
+class ServiceChangeObserverBase {
+public:
+    virtual ~ServiceChangeObserverBase() =3D 0;
+
+    virtual void status_change(ServiceBase*  service,
+			       ServiceStatus old_status,
+			       ServiceStatus new_status) =3D 0;
+};
+
+/**
+ * @short Selective Change Observer.
+ *
+ * Forwards limited subset of status changes to a status change observer=
=2E
+ */
+class ServiceFilteredChangeObserver
+    : public ServiceChangeObserverBase {
+public:
+    /**
+     * Constructor.
+     *
+     * Only changes from the states represented in @ref from_mask to
+     * the states represented in @ref to_mask are reported.
+     *
+     * @param child recipient of status changes.
+     * @param from_mask mask of states left to trigger changes.
+     * @param to_mask mask of states entered to trigger changes.
+     */
+    ServiceFilteredChangeObserver(ServiceChangeObserverBase*	child,
+				  ServiceStatus			from_mask,
+				  ServiceStatus			to_mask);
+
+protected:
+    void status_change(ServiceBase*	service,
+		       ServiceStatus	old_status,
+		       ServiceStatus	new_status);
+
+protected:
+    ServiceChangeObserverBase*	_child;
+    ServiceStatus		_from_mask;
+    ServiceStatus		_to_mask;
+};
+
+#endif // __LIBXORP_SERVICE_HH__
Index: libxorp/service.cc
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: libxorp/service.cc
diff -N libxorp/service.cc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ libxorp/service.cc	9 Oct 2003 07:04:56 -0000
@@ -0,0 +1,156 @@
+// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-
+
+// Copyright (c) 2001-2003 International Computer Science Institute
+//
+// Permission is hereby granted, free of charge, to any person obtaining=
 a
+// copy of this software and associated documentation files (the "Softwa=
re")
+// to deal in the Software without restriction, subject to the condition=
s
+// listed in the XORP LICENSE file. These conditions include: you must
+// preserve this copyright notice, and you cannot mention the copyright
+// holders in advertising related to the Software without their permissi=
on.
+// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. Th=
is
+// notice is a summary of the XORP LICENSE file; the license in that fil=
e is
+// legally binding.
+
+#ident "$XORP$"
+
+#include "config.h"
+#include <string>
+
+#include "service.hh"
+
+// ---------------------------------------------------------------------=
-------
+// ServiceStatus related
+
+const char*
+service_status_name(ServiceStatus s)
+{
+    switch (s) {
+    case READY:		return "Ready";
+    case STARTING:	return "Starting";
+    case RUNNING:	return "Running";
+    case PAUSING:	return "Pausing";
+    case PAUSED:	return "Paused";
+    case RESUMING:	return "Resuming";
+    case SHUTTING_DOWN: return "Shutting down";
+    case SHUTDOWN:	return "Shutdown";
+    case FAILED:	return "Failed";
+    case ALL:		return "All";				// Invalid
+    }
+    return "Unknown";
+}
+
+// ---------------------------------------------------------------------=
-------
+// ServiceBase implmentation
+
+ServiceBase::ServiceBase()
+    : _status(READY), _observer(0)
+{
+}
+
+ServiceBase::~ServiceBase()
+{
+}
+
+bool
+ServiceBase::reset()
+{
+    return false;
+}
+
+bool
+ServiceBase::pause()
+{
+    return false;
+}
+
+bool
+ServiceBase::resume()
+{
+    return false;
+}
+
+=0C
+const char*
+ServiceBase::status_name() const
+{
+    return service_status_name(_status);
+}
+
+bool
+ServiceBase::set_observer(ServiceChangeObserverBase* so)
+{
+    if (_observer) {
+	return false;
+    }
+    _observer =3D so;
+    return true;
+}
+
+bool
+ServiceBase::unset_observer(ServiceChangeObserverBase* so)
+{
+    if (_observer =3D=3D so) {
+	_observer =3D 0;
+	return true;
+    }
+    return false;
+}
+
+void
+ServiceBase::set_status(ServiceStatus status, const string& note)
+{
+    ServiceStatus ost =3D _status;
+    _status =3D status;
+
+    bool note_changed =3D (_note !=3D note);
+    _note =3D note;
+
+    if (_observer && (ost !=3D _status || note_changed)) {
+	_observer->status_change(this, ost, _status);
+    }
+}
+
+void
+ServiceBase::set_status(ServiceStatus status)
+{
+    ServiceStatus ost =3D _status;
+    _status =3D status;
+
+    _note.erase();
+
+    if (_observer && ost !=3D _status) {
+	_observer->status_change(this, ost, _status);
+    }
+}
+
+=0C
+// ---------------------------------------------------------------------=
-------
+// ServiceChangeObserverBase
+
+ServiceChangeObserverBase::~ServiceChangeObserverBase()
+{
+}
+
+// ---------------------------------------------------------------------=
-------
+// ServiceFilteredChangeObserver
+
+ServiceFilteredChangeObserver::ServiceFilteredChangeObserver(
+					ServiceChangeObserverBase* child,
+					ServiceStatus		   from,
+					ServiceStatus		   to
+					)
+    : _child(child), _from_mask(from), _to_mask(to)
+{
+}
+
+void
+ServiceFilteredChangeObserver::status_change(ServiceBase*	service,
+					     ServiceStatus 	old_status,
+					     ServiceStatus 	new_status)
+{
+    if (old_status & _from_mask &&
+	new_status & _to_mask) {
+	_child->status_change(service, old_status, new_status);
+    }
+}
Index: libxorp/test_service.cc
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: libxorp/test_service.cc
diff -N libxorp/test_service.cc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ libxorp/test_service.cc	9 Oct 2003 07:04:56 -0000
@@ -0,0 +1,299 @@
+// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-
+
+// Copyright (c) 2001-2003 International Computer Science Institute
+//
+// Permission is hereby granted, free of charge, to any person obtaining=
 a
+// copy of this software and associated documentation files (the "Softwa=
re")
+// to deal in the Software without restriction, subject to the condition=
s
+// listed in the XORP LICENSE file. These conditions include: you must
+// preserve this copyright notice, and you cannot mention the copyright
+// holders in advertising related to the Software without their permissi=
on.
+// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. Th=
is
+// notice is a summary of the XORP LICENSE file; the license in that fil=
e is
+// legally binding.
+
+#ident "$XORP: xorp/devnotes/template.cc,v 1.2 2003/01/16 19:08:48 mjh E=
xp $"
+
+#include "config.h"
+
+#include <string>
+
+#include "eventloop.hh"
+#include "service.hh"
+
+#include "libxorp_module.h"
+#include "xlog.h"
+#include "exceptions.hh"
+
+//
+// This test is fairly straightforward.  We implement two classes
+// TestService and TestServiceChangeObserver.  TestService is derived
+// from ServiceBase and implements ServiceBase::startup() and
+// ServiceBase::shutdown().  With both methods, invocation sets the
+// current status to the relevant intermediate state (STARTING_UP or
+// SHUTTING_DOWN) and also sets a timer.  When the timer expires it
+// sets the current state to the requested state (RUNNING or
+// SHUTDOWN).  The timer expiry time is after TRANS_MS milliseconds.
+//
+// TestServiceChangeObserver assumes it is going to be informed of
+// these changes and verifies that the origin and order of changes
+// matches those it expects.
+//
+// The main body of the test is in test_main.  It instantiates and
+// associates instances of TestService and TestServiceChangeObserver.
+// It uses timers to trigger the calling of TestService::startup() and
+// TestService::shutdown().
+//
+
+// ---------------------------------------------------------------------=
-------
+// Constants
+
+static const uint32_t TRANS_MS =3D 100;		// Transition time (millisecs)
+static const uint32_t EXIT_MS =3D 5 * TRANS_MS;	// Time to exit (millise=
cs)
+
+// ---------------------------------------------------------------------=
-------
+// Verbose output
+
+static bool s_verbose =3D false;
+
+inline bool verbose() 		{ return s_verbose; }
+inline void set_verbose(bool v)	{ s_verbose =3D v; }
+
+#define verbose_log(x...) 						      \
+do {									      \
+    if (verbose()) {							      \
+	printf("From %s:%d: ", __FILE__, __LINE__);			      \
+	printf(x);							      \
+    }									      \
+} while(0)
+
+=0C
+// ---------------------------------------------------------------------=
-------
+// TestService implementation
+
+class TestService : public ServiceBase
+{
+public:
+    TestService(EventLoop& e)
+	: _e(e)
+    {}
+
+    void
+    startup()
+    {
+	set_status(STARTING, "Waiting for timed start event");
+	_xt =3D _e.new_oneoff_after_ms(TRANS_MS,
+				     callback(this, &TestService::go_running));
+
+    }
+
+    void
+    shutdown()
+    {
+	set_status(SHUTTING_DOWN, "Waiting for timed shutdown event");
+	_xt =3D _e.new_oneoff_after_ms(TRANS_MS,
+				     callback(this, &TestService::go_shutdown));
+    }
+
+protected:
+    void go_running()
+    {
+	set_status(RUNNING);
+    }
+
+    void go_shutdown()
+    {
+	set_status(SHUTDOWN);
+    }
+
+protected:
+    EventLoop&	_e;
+    XorpTimer	_xt;	// Timer for simulated status transitions
+};
+
+=0C
+// ---------------------------------------------------------------------=
-------
+// TestServiceChangeObserver implementation
+
+class TestServiceChangeObserver : public ServiceChangeObserverBase
+{
+public:
+    TestServiceChangeObserver(const TestService* expected_service)
+	: _s(expected_service), _cc(0), _bc(0)
+    {
+    }
+
+    void
+    status_change(ServiceBase*	service,
+		  ServiceStatus	old_status,
+		  ServiceStatus	new_status)
+    {
+	if (service !=3D _s) {
+	    verbose_log("Wrong service argument\n");
+	    _bc++;
+	    return;
+	}
+
+	ServiceStatus e_old, e_new;
+	switch (_cc++) {
+	case 0:
+	    // First change expected READY -> STARTING
+	    e_old =3D READY;
+	    e_new =3D STARTING;
+	    break;
+
+	case 1:
+	    // Second change expected STARTING -> RUNNING
+	    e_old =3D STARTING;
+	    e_new =3D RUNNING;
+	    break;
+
+	case 2:
+	    // Third change expected RUNNING -> SHUTTING_DOWN
+	    e_old =3D RUNNING;
+	    e_new =3D SHUTTING_DOWN;
+	    break;
+
+	case 3:
+	    // Fourth change expected SHUTTING_DOWN -> SHUTDOWN
+	    e_old =3D SHUTTING_DOWN;
+	    e_new =3D SHUTDOWN;
+	    break;
+	default:
+	    verbose_log("%d. Too many changes.\n", _cc);
+	}
+
+	if (e_old =3D=3D old_status && e_new =3D=3D new_status) {
+	    verbose_log("%d. Good transition: %s -> %s (%s)\n",
+			_cc,
+			service_status_name(e_old),
+			service_status_name(e_new),
+			service->status_note().c_str());
+	    return;
+	}
+	verbose_log("%d. Bad transition: Got %s -> %s (%d) Expected %s -> %s\n"=
,
+		    _cc,
+		    service_status_name(old_status),
+		    service_status_name(new_status),
+		    service_status_name(e_old),
+		    service_status_name(e_new),
+		    service->status_note().c_str());
+	// Record bad change
+	_bc++;
+    }
+
+    bool changes_okay() const
+    {
+	return _cc =3D=3D 4 && _bc =3D=3D 0;
+    }
+
+protected:
+    const TestService* const	_s;	// Expected service
+    uint32_t	 		_cc;	// Change count
+    uint32_t	 		_bc;	// number of bad changes
+};
+
+=0C
+// ---------------------------------------------------------------------=
-------
+// Guts of Test
+
+static void
+startup_test_service(TestService* ts)
+{
+    ts->startup();
+}
+
+static void
+shutdown_test_service(TestService* ts)
+{
+    ts->shutdown();
+}
+
+static int
+run_test()
+{
+    EventLoop e;
+
+    TestService ts(e);
+    TestServiceChangeObserver o(&ts);
+    ts.set_observer(&o);
+
+    XorpTimer startup =3D e.new_oneoff_after_ms(TRANS_MS,
+					      callback(&startup_test_service,
+						       &ts));
+    XorpTimer shutdown =3D e.new_oneoff_after_ms(EXIT_MS,
+					       callback(&shutdown_test_service,
+							&ts));
+
+    bool timed_out =3D false;
+    XorpTimer timeout =3D e.set_flag_after_ms(EXIT_MS + 3 * TRANS_MS, &t=
imed_out);
+
+    while (timed_out =3D=3D false && ts.status() !=3D SHUTDOWN) {
+	e.run();
+    }
+
+    if (timed_out) {
+	verbose_log("Test timed out.\n");
+	return -1;
+    }
+
+    if (o.changes_okay() =3D=3D false) {
+	verbose_log("Changes not okay.\n");
+	return -1;
+    }
+    return 0;
+}
+
+static void
+usage(const char* argv0)
+{
+    fprintf(stderr, "usage: %s [-v]\n", argv0);
+    fprintf(stderr, "A test program for XORP service classes\n");
+}
+
+int
+main(int argc, char* const* argv)
+{
+    //
+    // Initialize and start xlog
+    //
+    xlog_init(argv[0], NULL);
+    xlog_set_verbose(XLOG_VERBOSE_LOW);         // Least verbose message=
s
+    // XXX: verbosity of the error messages temporary increased
+    xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH);
+    xlog_add_default_output();
+    xlog_start();
+
+    int ch;
+    while ((ch =3D getopt(argc, argv, "hv")) !=3D -1) {
+	switch (ch) {
+	case 'v':
+	    set_verbose(true);
+	    break;
+	case 'h':
+	case '?':
+	default:
+	    usage(argv[0]);
+	    xlog_stop();
+	    xlog_exit();
+	    return -1;
+	}
+    }
+    argc -=3D optind;
+    argv +=3D optind;
+
+    int r =3D 0;
+    XorpUnexpectedHandler x(xorp_unexpected_handler);
+    try {
+	r =3D run_test();
+    } catch (...) {
+	xorp_catch_standard_exceptions();
+    }
+    //
+    // Gracefully stop and exit xlog
+    //
+    xlog_stop();
+    xlog_exit();
+
+    return r;
+}
Index: libxorp/Makefile.am
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /usr/local/www/data/cvs/xorp/libxorp/Makefile.am,v
retrieving revision 1.13
diff -u -r1.13 Makefile.am
--- libxorp/Makefile.am	1 Aug 2003 02:11:48 -0000	1.13
+++ libxorp/Makefile.am	9 Oct 2003 07:04:56 -0000
@@ -20,14 +20,15 @@
 TESTS		+=3D test_mac
 TESTS		+=3D test_observers
 TESTS		+=3D test_ref_ptr
-TESTS		+=3D test_ref_trie =

+TESTS		+=3D test_ref_trie
+TESTS		+=3D test_service
 TESTS		+=3D test_test_main
 TESTS		+=3D test_time_slice
 TESTS		+=3D test_timer
-TESTS		+=3D test_timeval =

+TESTS		+=3D test_timeval
 TESTS		+=3D test_trie
 TESTS		+=3D test_types
-TESTS		+=3D test_vif =

+TESTS		+=3D test_vif
 =

 # -- No-install Programs
 noinst_PROGRAMS	 =3D test_asyncio
@@ -43,14 +44,15 @@
 noinst_PROGRAMS	+=3D test_mac
 noinst_PROGRAMS	+=3D test_observers
 noinst_PROGRAMS	+=3D test_ref_ptr
-noinst_PROGRAMS	+=3D test_ref_trie =

+noinst_PROGRAMS	+=3D test_ref_trie
+noinst_PROGRAMS	+=3D test_service
 noinst_PROGRAMS	+=3D test_test_main
 noinst_PROGRAMS	+=3D test_time_slice
 noinst_PROGRAMS	+=3D test_timer
-noinst_PROGRAMS	+=3D test_timeval =

+noinst_PROGRAMS	+=3D test_timeval
 noinst_PROGRAMS	+=3D test_trie
 noinst_PROGRAMS	+=3D test_types
-noinst_PROGRAMS	+=3D test_vif =

+noinst_PROGRAMS	+=3D test_vif
 =

 # -- No-install Libraries
 noinst_LIBRARIES =3D libxorp.a
@@ -73,6 +75,7 @@
 test_observers_SOURCES  =3D test_observers.cc
 test_ref_ptr_SOURCES	=3D test_ref_ptr.cc
 test_ref_trie_SOURCES	=3D test_ref_trie.cc
+test_service_SOURCES	=3D test_service.cc
 test_test_main_SOURCES	=3D test_test_main.cc
 test_time_slice_SOURCES	=3D test_time_slice.cc
 test_timer_SOURCES	=3D test_timer.cc
@@ -111,6 +114,7 @@
 libxorp_a_SOURCES +=3D old_trie.hh
 libxorp_a_SOURCES +=3D ref_ptr.hh
 libxorp_a_SOURCES +=3D selector.hh
+libxorp_a_SOURCES +=3D service.hh
 libxorp_a_SOURCES +=3D time_slice.hh
 libxorp_a_SOURCES +=3D timer.hh
 libxorp_a_SOURCES +=3D timeval.hh
@@ -135,6 +139,7 @@
 libxorp_a_SOURCES +=3D nexthop.cc
 libxorp_a_SOURCES +=3D ref_ptr.cc
 libxorp_a_SOURCES +=3D selector.cc
+libxorp_a_SOURCES +=3D service.cc
 libxorp_a_SOURCES +=3D time_slice.cc
 libxorp_a_SOURCES +=3D timer.cc
 libxorp_a_SOURCES +=3D token.cc
Index: libxorp/Makefile.in
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /usr/local/www/data/cvs/xorp/libxorp/Makefile.in,v
retrieving revision 1.22
diff -u -r1.22 Makefile.in
--- libxorp/Makefile.in	30 Sep 2003 20:28:50 -0000	1.22
+++ libxorp/Makefile.in	9 Oct 2003 07:04:56 -0000
@@ -95,10 +95,10 @@
 bin_PROGRAMS =3D =

 =

 # -- Test Programs
-TESTS =3D test_asyncio test_callback test_config_param test_ipnet test_i=
pv4 test_ipv4net test_ipv6 test_ipv6net test_ipvx test_ipvxnet test_mac t=
est_observers test_ref_ptr test_ref_trie  test_test_main test_time_slice =
test_timer test_timeval  test_trie test_types test_vif =

+TESTS =3D test_asyncio test_callback test_config_param test_ipnet test_i=
pv4 test_ipv4net test_ipv6 test_ipv6net test_ipvx test_ipvxnet test_mac t=
est_observers test_ref_ptr test_ref_trie test_service test_test_main test=
_time_slice test_timer test_timeval test_trie test_types test_vif
 =

 # -- No-install Programs
-noinst_PROGRAMS =3D test_asyncio test_callback test_config_param test_ip=
net test_ipv4 test_ipv4net test_ipv6 test_ipv6net test_ipvx test_ipvxnet =
test_mac test_observers test_ref_ptr test_ref_trie  test_test_main test_t=
ime_slice test_timer test_timeval  test_trie test_types test_vif =

+noinst_PROGRAMS =3D test_asyncio test_callback test_config_param test_ip=
net test_ipv4 test_ipv4net test_ipv6 test_ipv6net test_ipvx test_ipvxnet =
test_mac test_observers test_ref_ptr test_ref_trie test_service test_test=
_main test_time_slice test_timer test_timeval test_trie test_types test_v=
if
 =

 # -- No-install Libraries
 noinst_LIBRARIES =3D libxorp.a
@@ -121,6 +121,7 @@
 test_observers_SOURCES =3D test_observers.cc
 test_ref_ptr_SOURCES =3D test_ref_ptr.cc
 test_ref_trie_SOURCES =3D test_ref_trie.cc
+test_service_SOURCES =3D test_service.cc
 test_test_main_SOURCES =3D test_test_main.cc
 test_time_slice_SOURCES =3D test_time_slice.cc
 test_timer_SOURCES =3D test_timer.cc
@@ -130,7 +131,7 @@
 test_vif_SOURCES =3D test_vif.cc
 =

 # -- Library Sources
-libxorp_a_SOURCES =3D libxorp_module.h debug.h ether_compat.h xlog.h xor=
p.h utility.h asnum.hh asyncio.hh buffer.hh c_format.hh callback.hh confi=
g_param.hh eventloop.hh exceptions.hh heap.hh ipnet.hh ipv4.hh ipv4net.hh=
 ipv6.hh ipv6net.hh ipvx.hh ipvxnet.hh mac.hh nexthop.hh old_trie.hh ref_=
ptr.hh selector.hh time_slice.hh timer.hh timeval.hh token.hh transaction=
=2Ehh trie.hh utils.hh vif.hh debug.c xlog.c asyncio.cc c_format.cc event=
loop.cc exceptions.cc heap.cc ipv4.cc ipv6.cc ipvx.cc mac.cc nexthop.cc r=
ef_ptr.cc selector.cc time_slice.cc timer.cc token.cc transaction.cc vif.=
cc
+libxorp_a_SOURCES =3D libxorp_module.h debug.h ether_compat.h xlog.h xor=
p.h utility.h asnum.hh asyncio.hh buffer.hh c_format.hh callback.hh confi=
g_param.hh eventloop.hh exceptions.hh heap.hh ipnet.hh ipv4.hh ipv4net.hh=
 ipv6.hh ipv6net.hh ipvx.hh ipvxnet.hh mac.hh nexthop.hh old_trie.hh ref_=
ptr.hh selector.hh service.hh time_slice.hh timer.hh timeval.hh token.hh =
transaction.hh trie.hh utils.hh vif.hh debug.c xlog.c asyncio.cc c_format=
=2Ecc eventloop.cc exceptions.cc heap.cc ipv4.cc ipv6.cc ipvx.cc mac.cc n=
exthop.cc ref_ptr.cc selector.cc service.cc time_slice.cc timer.cc token.=
cc transaction.cc vif.cc
 =

 BUILT_SOURCES =3D callback.hh
 =

@@ -152,8 +153,9 @@
 	c_format.$(OBJEXT) eventloop.$(OBJEXT) exceptions.$(OBJEXT) \
 	heap.$(OBJEXT) ipv4.$(OBJEXT) ipv6.$(OBJEXT) ipvx.$(OBJEXT) \
 	mac.$(OBJEXT) nexthop.$(OBJEXT) ref_ptr.$(OBJEXT) \
-	selector.$(OBJEXT) time_slice.$(OBJEXT) timer.$(OBJEXT) \
-	token.$(OBJEXT) transaction.$(OBJEXT) vif.$(OBJEXT)
+	selector.$(OBJEXT) service.$(OBJEXT) time_slice.$(OBJEXT) \
+	timer.$(OBJEXT) token.$(OBJEXT) transaction.$(OBJEXT) \
+	vif.$(OBJEXT)
 libxorp_a_OBJECTS =3D $(am_libxorp_a_OBJECTS)
 bin_PROGRAMS =3D
 noinst_PROGRAMS =3D test_asyncio$(EXEEXT) test_callback$(EXEEXT) \
@@ -161,10 +163,10 @@
 	test_ipv4$(EXEEXT) test_ipv4net$(EXEEXT) test_ipv6$(EXEEXT) \
 	test_ipv6net$(EXEEXT) test_ipvx$(EXEEXT) test_ipvxnet$(EXEEXT) \
 	test_mac$(EXEEXT) test_observers$(EXEEXT) test_ref_ptr$(EXEEXT) \
-	test_ref_trie$(EXEEXT) test_test_main$(EXEEXT) \
-	test_time_slice$(EXEEXT) test_timer$(EXEEXT) \
-	test_timeval$(EXEEXT) test_trie$(EXEEXT) test_types$(EXEEXT) \
-	test_vif$(EXEEXT)
+	test_ref_trie$(EXEEXT) test_service$(EXEEXT) \
+	test_test_main$(EXEEXT) test_time_slice$(EXEEXT) \
+	test_timer$(EXEEXT) test_timeval$(EXEEXT) test_trie$(EXEEXT) \
+	test_types$(EXEEXT) test_vif$(EXEEXT)
 PROGRAMS =3D $(bin_PROGRAMS) $(noinst_PROGRAMS)
 =

 am_test_asyncio_OBJECTS =3D test_asyncio.$(OBJEXT)
@@ -237,6 +239,11 @@
 test_ref_trie_LDADD =3D $(LDADD)
 test_ref_trie_DEPENDENCIES =3D libxorp.a
 test_ref_trie_LDFLAGS =3D
+am_test_service_OBJECTS =3D test_service.$(OBJEXT)
+test_service_OBJECTS =3D $(am_test_service_OBJECTS)
+test_service_LDADD =3D $(LDADD)
+test_service_DEPENDENCIES =3D libxorp.a
+test_service_LDFLAGS =3D
 am_test_test_main_OBJECTS =3D test_test_main.$(OBJEXT)
 test_test_main_OBJECTS =3D $(am_test_test_main_OBJECTS)
 test_test_main_LDADD =3D $(LDADD)
@@ -285,7 +292,8 @@
 @AMDEP_TRUE@	$(DEPDIR)/ipv4.Po $(DEPDIR)/ipv6.Po \
 @AMDEP_TRUE@	$(DEPDIR)/ipvx.Po $(DEPDIR)/mac.Po \
 @AMDEP_TRUE@	$(DEPDIR)/nexthop.Po $(DEPDIR)/ref_ptr.Po \
-@AMDEP_TRUE@	$(DEPDIR)/selector.Po $(DEPDIR)/test_asyncio.Po \
+@AMDEP_TRUE@	$(DEPDIR)/selector.Po $(DEPDIR)/service.Po \
+@AMDEP_TRUE@	$(DEPDIR)/test_asyncio.Po \
 @AMDEP_TRUE@	$(DEPDIR)/test_callback.Po \
 @AMDEP_TRUE@	$(DEPDIR)/test_config_param.Po \
 @AMDEP_TRUE@	$(DEPDIR)/test_ipnet.Po $(DEPDIR)/test_ipv4.Po \
@@ -295,6 +303,7 @@
 @AMDEP_TRUE@	$(DEPDIR)/test_observers.Po \
 @AMDEP_TRUE@	$(DEPDIR)/test_ref_ptr.Po \
 @AMDEP_TRUE@	$(DEPDIR)/test_ref_trie.Po \
+@AMDEP_TRUE@	$(DEPDIR)/test_service.Po \
 @AMDEP_TRUE@	$(DEPDIR)/test_test_main.Po \
 @AMDEP_TRUE@	$(DEPDIR)/test_time_slice.Po \
 @AMDEP_TRUE@	$(DEPDIR)/test_timer.Po $(DEPDIR)/test_timeval.Po \
@@ -321,12 +330,12 @@
 	$(test_ipv6net_SOURCES) $(test_ipvx_SOURCES) \
 	$(test_ipvxnet_SOURCES) $(test_mac_SOURCES) \
 	$(test_observers_SOURCES) $(test_ref_ptr_SOURCES) \
-	$(test_ref_trie_SOURCES) $(test_test_main_SOURCES) \
-	$(test_time_slice_SOURCES) $(test_timer_SOURCES) \
-	$(test_timeval_SOURCES) $(test_trie_SOURCES) \
-	$(test_types_SOURCES) $(test_vif_SOURCES)
+	$(test_ref_trie_SOURCES) $(test_service_SOURCES) \
+	$(test_test_main_SOURCES) $(test_time_slice_SOURCES) \
+	$(test_timer_SOURCES) $(test_timeval_SOURCES) \
+	$(test_trie_SOURCES) $(test_types_SOURCES) $(test_vif_SOURCES)
 DIST_COMMON =3D README Makefile.am Makefile.in TODO
-SOURCES =3D $(libxorp_a_SOURCES) $(test_asyncio_SOURCES) $(test_callback=
_SOURCES) $(test_config_param_SOURCES) $(test_ipnet_SOURCES) $(test_ipv4_=
SOURCES) $(test_ipv4net_SOURCES) $(test_ipv6_SOURCES) $(test_ipv6net_SOUR=
CES) $(test_ipvx_SOURCES) $(test_ipvxnet_SOURCES) $(test_mac_SOURCES) $(t=
est_observers_SOURCES) $(test_ref_ptr_SOURCES) $(test_ref_trie_SOURCES) $=
(test_test_main_SOURCES) $(test_time_slice_SOURCES) $(test_timer_SOURCES)=
 $(test_timeval_SOURCES) $(test_trie_SOURCES) $(test_types_SOURCES) $(tes=
t_vif_SOURCES)
+SOURCES =3D $(libxorp_a_SOURCES) $(test_asyncio_SOURCES) $(test_callback=
_SOURCES) $(test_config_param_SOURCES) $(test_ipnet_SOURCES) $(test_ipv4_=
SOURCES) $(test_ipv4net_SOURCES) $(test_ipv6_SOURCES) $(test_ipv6net_SOUR=
CES) $(test_ipvx_SOURCES) $(test_ipvxnet_SOURCES) $(test_mac_SOURCES) $(t=
est_observers_SOURCES) $(test_ref_ptr_SOURCES) $(test_ref_trie_SOURCES) $=
(test_service_SOURCES) $(test_test_main_SOURCES) $(test_time_slice_SOURCE=
S) $(test_timer_SOURCES) $(test_timeval_SOURCES) $(test_trie_SOURCES) $(t=
est_types_SOURCES) $(test_vif_SOURCES)
 =

 all: $(BUILT_SOURCES)
 	$(MAKE) $(AM_MAKEFLAGS) all-am
@@ -417,6 +426,9 @@
 test_ref_trie$(EXEEXT): $(test_ref_trie_OBJECTS) $(test_ref_trie_DEPENDE=
NCIES) =

 	@rm -f test_ref_trie$(EXEEXT)
 	$(CXXLINK) $(test_ref_trie_LDFLAGS) $(test_ref_trie_OBJECTS) $(test_ref=
_trie_LDADD) $(LIBS)
+test_service$(EXEEXT): $(test_service_OBJECTS) $(test_service_DEPENDENCI=
ES) =

+	@rm -f test_service$(EXEEXT)
+	$(CXXLINK) $(test_service_LDFLAGS) $(test_service_OBJECTS) $(test_servi=
ce_LDADD) $(LIBS)
 test_test_main$(EXEEXT): $(test_test_main_OBJECTS) $(test_test_main_DEPE=
NDENCIES) =

 	@rm -f test_test_main$(EXEEXT)
 	$(CXXLINK) $(test_test_main_LDFLAGS) $(test_test_main_OBJECTS) $(test_t=
est_main_LDADD) $(LIBS)
@@ -458,6 +470,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/nexthop.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ref_ptr.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/selector.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/service.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/test_asyncio.Po@am__quote=
@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/test_callback.Po@am__quot=
e@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/test_config_param.Po@am__=
quote@
@@ -472,6 +485,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/test_observers.Po@am__quo=
te@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/test_ref_ptr.Po@am__quote=
@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/test_ref_trie.Po@am__quot=
e@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/test_service.Po@am__quote=
@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/test_test_main.Po@am__quo=
te@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/test_time_slice.Po@am__qu=
ote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/test_timer.Po@am__quote@

--==_Exmh_16833281770--