]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1096] Switched to boost asio signal set
authorFrancis Dupont <fdupont@isc.org>
Sun, 29 Mar 2020 15:06:55 +0000 (17:06 +0200)
committerFrancis Dupont <fdupont@isc.org>
Wed, 22 Apr 2020 14:13:29 +0000 (16:13 +0200)
src/lib/process/d_controller.cc
src/lib/process/d_controller.h
src/lib/process/io_service_signal.cc
src/lib/process/io_service_signal.h
src/lib/process/tests/d_controller_unittests.cc
src/lib/process/tests/io_service_signal_unittests.cc
src/lib/process/testutils/d_test_stubs.h

index d6c97e3eb07fea18e4cb4774334527ff8f1aacfc..f971eb01191259dfc1d6ebf6b0f3f6f7fa1beb33 100644 (file)
@@ -32,7 +32,7 @@ DControllerBase::DControllerBase(const char* app_name, const char* bin_name)
     : app_name_(app_name), bin_name_(bin_name),
       verbose_(false), check_only_(false),
       io_service_(new isc::asiolink::IOService()),
-      io_signal_queue_() {
+      io_signal_set_() {
 }
 
 void
@@ -723,37 +723,15 @@ void
 DControllerBase::initSignalHandling() {
     /// @todo block everything we don't handle
 
-    // Create our signal queue.
-    io_signal_queue_.reset(new IOSignalQueue(io_service_));
-
-    // Install the on-receipt handler
-    util::SignalSet::setOnReceiptHandler(boost::bind(&DControllerBase::
-                                                     osSignalHandler,
-                                                     this, _1));
+    // Create our signal set.
+    io_signal_set_.reset(new IOSignalSet(io_service_,
+                                         boost::bind(&DControllerBase::
+                                                     processSignal,
+                                                     this, _1)));
     // Register for the signals we wish to handle.
-    signal_set_.reset(new util::SignalSet(SIGHUP,SIGINT,SIGTERM));
-}
-
-bool
-DControllerBase::osSignalHandler(int signum) {
-    // Create a IOSignal to propagate the signal to IOService.
-    io_signal_queue_->pushSignal(signum, boost::bind(&DControllerBase::
-                                                     ioSignalHandler,
-                                                     this, _1));
-    return (true);
-}
-
-void
-DControllerBase::ioSignalHandler(IOSignalId sequence_id) {
-    // Pop the signal instance off the queue.  This should make us
-    // the only one holding it, so when we leave it should be freed.
-    // (note that popSignal will throw if signal is not found, which
-    // in turn will caught, logged, and swallowed by IOSignal callback
-    // invocation code.)
-    IOSignalPtr io_signal = io_signal_queue_->popSignal(sequence_id);
-
-    // Now call virtual signal processing method.
-    processSignal(io_signal->getSignum());
+    io_signal_set_->add(SIGHUP);
+    io_signal_set_->add(SIGINT);
+    io_signal_set_->add(SIGTERM);
 }
 
 void
index e6c6354205e19d1603ebf971846b4454f31aaa86..16db298b5d60f203df58031b10723d11768d95c9 100644 (file)
@@ -407,9 +407,8 @@ protected:
 
     /// @brief Application-level signal processing method.
     ///
-    /// This method is the last step in processing a OS signal occurrence.  It
-    /// is invoked when an IOSignal's internal timer callback is executed by
-    /// IOService.  It currently supports the following signals as follows:
+    /// This method is the last step in processing a OS signal occurrence.
+    /// It currently supports the following signals as follows:
     /// -# SIGHUP - instigates reloading the configuration file
     /// -# SIGINT - instigates a graceful shutdown
     /// -# SIGTERM - instigates a graceful shutdown
@@ -580,32 +579,6 @@ protected:
     /// which listens for SIGHUP, SIGINT, and SIGTERM.
     void initSignalHandling();
 
-    /// @brief Handler for processing OS-level signals
-    ///
-    /// This method is installed as the SignalSet "on-receipt" handler. Upon
-    /// invocation, it uses the controller's IOSignalQueue to schedule an
-    /// IOSignal with for the given signal value.
-    ///
-    /// @param signum OS signal value (e.g. SIGINT, SIGUSR1 ...) to received
-    ///
-    /// @return SignalSet "on-receipt" handlers are required to return a
-    /// boolean indicating if the OS signal has been processed (true) or if it
-    /// should be saved for deferred processing (false).  Currently this
-    /// method processes all received signals, so it always returns true.
-    bool osSignalHandler(int signum);
-
-    /// @brief Handler for processing IOSignals
-    ///
-    /// This method is supplied as the callback when IOSignals are scheduled.
-    /// It fetches the IOSignal for the given sequence_id and then invokes
-    /// the virtual method, @c processSignal() passing it the signal value
-    /// obtained from the IOSignal.  This allows derivations to supply a
-    /// custom signal processing method, while ensuring IOSignalQueue
-    /// integrity.
-    ///
-    /// @param sequence_id id of the IOSignal instance "received"
-    void ioSignalHandler(IOSignalId sequence_id);
-
     /// @brief Fetches the current process
     ///
     /// @return a pointer to the current process instance.
@@ -664,8 +637,8 @@ private:
     /// @brief Shared pointer to an IOService object, used for ASIO operations.
     asiolink::IOServicePtr io_service_;
 
-    /// @brief Queue for propagating caught signals to the IOService.
-    IOSignalQueuePtr io_signal_queue_;
+    /// @brief ASIO signal set.
+    IOSignalSetPtr io_signal_set_;
 
     /// @brief Singleton instance value.
     static DControllerBasePtr controller_;
index 70c2e3e70fed4631e41dc9eb3ba14646cfe2f205..5df59797a9020b851346bb24d78bae5d1943aa67 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2020 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 
 #include <config.h>
 
-#include <asiolink/interval_timer.h>
 #include <process/d_log.h>
 #include <process/io_service_signal.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/bind.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/asio/signal_set.hpp>
+
+using namespace isc::asiolink;
 
 namespace isc {
 namespace process {
 
-IOSignal::IOSignal (asiolink::IOService& io_service, int signum,
-                    IOSignalHandler handler)
-    : sequence_id_(nextSequenceId()), signum_(signum),
-      timer_(new asiolink::IntervalTimer(io_service)) {
-    // Valid handler is essential.
-    if (!handler) {
-        isc_throw(IOSignalError,
-                  "IOSignal - handler cannot be null");
-    }
-
-    // Set up the timer as a one-shot which expires in 1 ms (intervals of 0
-    // are invalid). This means that once control returns to IOService::run
-    // the timer will have expired and its handler will be invoked.
-    timer_->setup(TimerCallback(sequence_id_, handler), 1,
-                  asiolink::IntervalTimer::ONE_SHOT);
+/// Implementation class of IOSignalSet.
+class IOSignalSetImpl :
+        public boost::enable_shared_from_this<IOSignalSetImpl>
+{
+private:
+    // Prohibit copy.
+    IOSignalSetImpl(const IOSignalSetImpl& source);
+    IOSignalSetImpl& operator=(const IOSignalSetImpl& source);
+public:
+    IOSignalSetImpl(IOServicePtr io_service, IOSignalHandler handler);
+    ~IOSignalSetImpl(){}
+    void install();
+    void add(int signum);
+ private:
+    boost::asio::signal_set signal_set_;
+    IOSignalHandler handler_;
+    void callback(const boost::system::error_code& ec, int signum);
+};
+
+IOSignalSetImpl::IOSignalSetImpl(IOServicePtr io_service,
+                                 IOSignalHandler handler)
+    : signal_set_(io_service->get_io_service()), handler_(handler) {
 }
 
-IOSignal::~IOSignal() {
-    if (timer_) {
-        // In the unlikely event that the timer hasn't expired cancel it.
-        timer_->cancel();
+void
+IOSignalSetImpl::callback(const boost::system::error_code& ec, int signum) {
+    if (ec && ec.value() == boost::asio::error::operation_aborted) {
+        return;
     }
-}
-
-IOSignal::
-TimerCallback::TimerCallback(IOSignalId sequence_id, IOSignalHandler handler)
-    : sequence_id_(sequence_id), handler_(handler) {
-    if (!handler) {
-        isc_throw(IOSignalError,
-                  "IOSignal::TimerCallback - handler cannot be null");
+    install();
+    if (!ec && (signum > 0)) {
+        try {
+            handler_(signum);
+        } catch (const std::exception& ex) {
+            // We log it and swallow it so we don't undermine IOService::run.
+            LOG_ERROR(dctl_logger, DCTL_SIGNAL_ERROR)
+                .arg(signum)
+                .arg(ex.what());
+        }
     }
 }
 
 void
-IOSignal::TimerCallback::operator()() {
-    try {
-        handler_(sequence_id_);
-    } catch (const std::exception& ex) {
-        // We log it and swallow it so we don't undermine IOService::run.
-        LOG_ERROR(dctl_logger, DCTL_SIGNAL_ERROR)
-                  .arg(sequence_id_).arg(ex.what());
-    }
-
-    return;
+IOSignalSetImpl::install() {
+    signal_set_.async_wait(boost::bind(&IOSignalSetImpl::callback,
+                                       shared_from_this(), _1, _2));
 }
 
-IOSignalQueue::IOSignalQueue(asiolink::IOServicePtr& io_service)
-    : io_service_(io_service), signals_() {
-    if (!io_service_) {
-        isc_throw(IOSignalError, "IOSignalQueue - io_service cannot be NULL");
+void
+IOSignalSetImpl::add(int signum) {
+    try {
+        signal_set_.add(signum);
+    } catch (const boost::system::system_error& ex) {
+        isc_throw(isc::Unexpected, "Failed to add signal " << signum
+                  << ": " << ex.what());
     }
 }
 
-IOSignalQueue::~IOSignalQueue() {
-    clear();
-}
-
-IOSignalId
-IOSignalQueue::pushSignal(int signum, IOSignalHandler handler) {
-    // Create the new signal.
-    IOSignalPtr signal(new IOSignal(*io_service_, signum, handler));
-
-    // Make sure the sequence_id isn't already in the queue.
-    IOSignalId sequence_id = signal->getSequenceId();
-    IOSignalMap::iterator it = signals_.find(sequence_id);
-    if (it != signals_.end()) {
-        // This really shouldn't happen unless we are in the weeds.
-        isc_throw (IOSignalError, "pushSignal - "
-                   "signal already exists for sequence_id: " << sequence_id);
-    }
-
-    //  Add the signal to the queue.
-    signals_[sequence_id] = signal;
-    return (sequence_id);
+IOSignalSet::IOSignalSet(IOServicePtr io_service, IOSignalHandler handler) :
+    impl_(new IOSignalSetImpl(io_service, handler))
+{
+    // It can throw but the error is fatal...
+    impl_->install();
 }
 
-IOSignalPtr
-IOSignalQueue::popSignal(IOSignalId sequence_id) {
-    // Look for the signal in the queue.
-    IOSignalMap::iterator it = signals_.find(sequence_id);
-    if (it == signals_.end()) {
-        // This really shouldn't happen unless we are in the weeds.
-        isc_throw (IOSignalError, "popSignal - "
-                   "signal not found for sequence_id: " << sequence_id);
-    }
-
-    // Save the signal so we can return it.
-    IOSignalPtr signal = ((*it).second);
-
-    // Delete it from the queue.
-    signals_.erase(it);
-
-    // Return the signal.
-    return (signal);
+IOSignalSet::~IOSignalSet() {
+    impl_.reset();
 }
 
 void
-IOSignalQueue::clear() {
-    signals_.clear();
+IOSignalSet::add(int signum) {
+    impl_->add(signum);
 }
 
 }; // end of isc::process namespace
index a393c164a5f43d5caf035bae6ab3fe0f72c8d3c4..22fe53b5d3067389ab0eac2b80763b7f63dad075 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2020 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 #define IO_SERVICE_SIGNAL_H
 
 #include <asiolink/io_service.h>
-#include <asiolink/interval_timer.h>
-#include <exceptions/exceptions.h>
 
-#include <map>
-#include <stdint.h>
+#include <boost/shared_ptr.hpp>
 
 namespace isc {
 namespace process {
 
-/// @brief Exception thrown if IOSignal encounters an error.
-class IOSignalError : public isc::Exception {
-public:
-    IOSignalError(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) { };
-};
-
-/// @brief Defines a unique identifier type for IOSignal.
-typedef uint64_t IOSignalId;
-
 /// @brief Defines a handler function for an IOSignal.
-/// IOSignalHandlers should contain the application level logic that would
-/// ordinarily be an OS signal handler.
-typedef boost::function<void(IOSignalId sequence_id)> IOSignalHandler;
+typedef boost::function<void(int signum)> IOSignalHandler;
+
+class IOSignalSetImpl;
 
 /// @brief Implements an asynchronous "signal" for IOService driven processing
 ///
 /// This class allows a OS signal such as SIGHUP to propagated to an IOService
-/// as a ready event with a callback. While boost::asio provides a signal class,
-/// it requires linking in additional boost libraries that as of yet we do not
-/// need. Therefore, this class was implemented to allow IOService-based
-/// processes to handle signals as IOService events.
-///
-/// The mechanics of IOSignal are straight forward. Upon construction it is
-/// given the target IOService, the value of the signal to send (i.e. SIGINT,
-/// SIGHUP...), and an IOSignalHandler.  The IOSignalHandler should contain
-/// the logic the caller would normally execute in its OS signal handler. Each
-/// IOSignal instance has a unique identifier called its sequence_id.
-///
-/// Internally, IOSignal creates a 1 ms, one-shot timer, on the given
-/// IOService.  When the timer expires its event handler invokes the caller's
-/// IOSignalHandler passing it the sequence_id of the IOSignal.
-///
-/// Sending IOSignals is done through an IOSignalQueue.  This class is used to
-/// create the signals, house them until they are delivered, and dequeue them
-/// so they can be been handled.  To generate an IOSignal when an OS signal
-/// arrives, the process's OS signal handler simply calls @ref
-/// isc::process::IOSignalQueue::pushSignal() with the appropriate values.
-///
-/// @note that an IOSignalQueue requires a non-null IOServicePtr to construct.
-/// This ensures that the IOService cannot be destroyed before any pending
-/// signals can be canceled.  It also means that a queue can only be used to
-/// send signals to that IOService.  If you need to send signals to more than
-/// one service, each service must have its own queue.
-///
-/// To dequeue the IOSignal inside the caller's IOSignalHandler, one simply
-/// invokes @ref isc::process::IOSignalQueue::popSignal() passing it the
-/// sequence_id parameter passed to the handler.  This method returns a
-/// pointer to instigating IOSignal from which the value of OS signal (i.e.
-/// SIGINT, SIGUSR1...) can be obtained.  Note that calling popSignal()
-/// removes the IOSignalPtr from the queue, which should reduce its
-/// reference count to zero upon exiting the handler (unless a deliberate
-/// copy of it is made).
-///
-/// A typical IOSignalHandler might be structured as follows:
-/// @code
-///
-///    void processSignal(IOSignalId sequence_id) {
-///        // Pop the signal instance off the queue.
-///        IOSignalPtr signal = io_signal_queue_->popSignal(sequence_id);
-///
-///        int os_signal_value = signal->getSignum();
-///        :
-///        // logic based on the signal value
-///        :
-///     }
-///
-/// @endcode
-///
-/// IOSignal handler invocation code will catch, log ,and then swallow any
-/// exceptions thrown by a IOSignalHandler invocation.  This is done to protect
-/// the integrity IOService context.
-///
-class IOSignal {
-public:
-    /// @brief Constructor
-    ///
-    /// @param io_service IOService to which to send the signal
-    /// @param signum value of the signal to send
-    /// @param handler the handler to run when IOService "receives" the
-    /// signal
-    ///
-    /// @throw IOSignalError if handler is null
-    IOSignal(asiolink::IOService& io_service, int signum,
-              IOSignalHandler handler);
-
-    /// @brief Destructor
-    ~IOSignal();
-
-    /// @brief Static method for generating IOSignal sequence_ids.
-    ///
-    /// Generates and returns the next IOSignalId. This method is intentionally
-    /// static in the event a process is using generating signals to more than
-    /// IOService.  It assures that each IOSignal is unique with the process
-    /// space.
-    ///
-    /// @return The next sequential value as an IOSignalId.
-    static IOSignalId nextSequenceId() {
-        static IOSignalId next_id_ = 0;
-        return (++next_id_);
-    }
-
-    /// @brief Gets the IOSignal's sequence_id
-    ///
-    /// @return The sequence_id of the signal.
-    IOSignalId getSequenceId() const {
-        return (sequence_id_);
-    }
-
-    /// @brief Gets the OS signal value this IOSignal represents.
-    ///
-    /// @return The OS signal value (i.e. SIGINT, SIGUSR1...)
-    int getSignum() const {
-        return (signum_);
-    }
-
-    /// @brief Defines the callback used by IOSignal's internal timer.
-    ///
-    /// This class stores the sequence_id of the IOSignal being sent and the
-    /// IOSignalHandler to invoke when delivering the signal.  The () operator
-    /// is called by IOService when the timer expires.  This method invokes
-    /// the IOSignalHandler passing it the sequence_id.
-    class TimerCallback : public std::unary_function<void, void> {
-    public:
-        /// @brief Constructor
-        ///
-        /// @param sequence_id sequence_id of the IOSignal to handle
-        /// @param handler pointer to the function to handle the IOSignal
-        ///
-        /// @throw IOSignalError if handler is null.
-        TimerCallback(IOSignalId sequence_id, IOSignalHandler handler);
-
-        /// @brief () Operator which serves as the timer's callback
-        ///
-        /// It is invoked when the timer expires and calls the handler
-        /// passing in the signal.
-        void operator()();
-
-    private:
-        /// @brief Id of the IOSignal to which the callback pertains.
-        IOSignalId sequence_id_;
-
-        /// @brief Pointer to the function to handle the signal
-        IOSignalHandler handler_;
-    };
-
-private:
-    /// @brief Value which uniquely identifies each IOSignal instance.
-    IOSignalId sequence_id_;
-
-    /// @brief Numeric value of the signal to send (e.g. SIGINT, SIGUSR1...)
-    int signum_;
-
-    /// @brief Timer instance created to propagate the signal.
-    asiolink::IntervalTimerPtr timer_;
-};
-
-/// @brief Defines a pointer to an IOSignal
-typedef boost::shared_ptr<IOSignal> IOSignalPtr;
-
-/// @brief Defines a map of IOSignalPtr keyed by id
-typedef std::map<IOSignalId, IOSignalPtr> IOSignalMap;
-
-/// @brief Creates and manages IOSignals
-///
-/// This class is used to create IOSignals, house them until they are delivered,
-/// and dequeue them so they can be been handled.  IOSignals are designed to
-/// used once and then destroyed.  They need to be created from within OS
-/// signal handlers and persist until they have been delivered and processed.
-///
-/// This class is designed specifically to make managing them painless.
-/// It maintains an internal map of IOSignals keyed by sequence_id. When a
-/// signal is created via the pushSignal() method it is added to the map. When
-/// a signal is retrieved via the popSignal() method it is removed from the map.
-class IOSignalQueue {
+/// as a ready event with a callback using boost ASIO.
+class IOSignalSet {
 public:
-    /// @brief Constructor
+    /// @brief Constructor.
     ///
-    /// @param io_service the IOService to which to send signals.
-    /// @throw IOSignalError if io_service is NULL.
-    IOSignalQueue (asiolink::IOServicePtr& io_service);
+    /// @param io_service IOService to which to send the signal.
+    /// @param handler Handler to call when a signal is received.
+    IOSignalSet(asiolink::IOServicePtr io_service, IOSignalHandler handler);
 
     /// @brief Destructor.
-    ~IOSignalQueue();
+    ~IOSignalSet();
 
-    /// @brief Creates an IOSignal
-    ///
-    /// Given a signal number and a handler, it will instantiate an IOSignal
-    /// and add it to the instance map.  (Remember that IOSignals are really
-    /// just timers programmed during construction, so once instantiated
-    /// there's nothing more required to "send" the signal other than return
-    /// control to IOService::run()).
-    ///
-    /// @param signum OS signal value of the signal to propagate
-    /// @param handler IOSignalHandler to invoke when the signal is delivered.
-    ///
-    /// @return The sequence_id of the newly created signal.
+    /// @brief Add a signal to the list of signals to handle.
     ///
-    /// @throw IOSignalError if the sequence_id already exists in the map. This
-    /// is virtually impossible unless things have gone very wrong.
-    IOSignalId pushSignal(int signum, IOSignalHandler handler);
-
-    /// @brief Removes an IOSignal from the map and returns it.
-    ///
-    /// Given a sequence_id this method will extract the IOSignal from the
-    /// internal map and return.  At that point, the caller will hold the
-    /// only copy of the IOSignal.
-    ///
-    /// @param sequence_id  sequence_id of the IOSignal to retrieve.
-    ///
-    /// @return A smart pointer to the IOSignal.
-    ///
-    /// @throw IOSignalError if there is no matching IOSignal in the map for
-    /// the given sequence_id.  Other than by doubling popping, this should be
-    /// very unlikely.
-    IOSignalPtr popSignal(IOSignalId sequence_id);
-
-    /// @brief Erases the contents of the queue.
-    ///
-    /// Any instances still in the map will be destroyed. This will cause their
-    /// timers to be cancelled without any callbacks invoked. (Not sure when
-    /// this might be desirable).
-    void clear();
+    /// @param signum Signal number.
+    /// @throw Unexpected on error.
+    void add(int signum);
 
 private:
-    /// @brief Pointer to the IOService which will receive the signals.
-    asiolink::IOServicePtr io_service_;
-
-    /// @brief A map of the IOSignals pushed through this queue.
-    IOSignalMap signals_;
+    /// @brief Pointer to the implementation.
+    boost::shared_ptr<IOSignalSetImpl> impl_;
 };
 
-/// @brief Defines a pointer to an IOSignalQueue.
-typedef boost::shared_ptr<IOSignalQueue> IOSignalQueuePtr;
-
+/// @brief Defines a pointer to an IOSignalSet.
+typedef boost::shared_ptr<IOSignalSet> IOSignalSetPtr;
 
 }; // end of isc::process namespace
 }; // end of isc namespace
index 875cea29934ca1a360a32d588e0f5bbb86f2df5e..accc6dc6b122c8a315ca3c51784349426af290bf 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
index c4e5a6f84b7a5a5cdb60e99179d46859b2a75a2a..4e04a2fc4dbe8e842034be4db2eaddfa44a4e6f5 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2020 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -6,7 +6,6 @@
 
 #include <config.h>
 
-#include <asiolink/io_service.h>
 #include <process/io_service_signal.h>
 #include <process/testutils/d_test_stubs.h>
 
 namespace isc {
 namespace process {
 
-/// @brief Test fixture for testing the use of IOSignals.
+/// @brief Test fixture for testing the use of IO service signals.
 ///
-/// This fixture is exercises IOSignaling as it is intended to be used in
-/// an application in conjunction with util::SignalSet.
+/// This fixture is exercises io service signaling it is intended to be used in
+/// an application in place of util::SignalSet.
 class IOSignalTest : public ::testing::Test {
 public:
     /// @brief IOService instance to process IO.
     asiolink::IOServicePtr io_service_;
+
     /// @brief Failsafe timer to ensure test(s) do not hang.
     isc::asiolink::IntervalTimer test_timer_;
+
     /// @brief Maximum time should be allowed to run.
     int test_time_ms_;
 
-    /// @brief SignalSet object so we can catch real signals.
-    util::SignalSetPtr signal_set_;
-
-    /// @brief IOSignalQueue so we can generate IOSignals.
-    IOSignalQueuePtr io_signal_queue_;
+    /// @brief IOSignalSet object.
+    IOSignalSetPtr io_signal_set_;
 
     /// @brief Vector to record the signal values received.
     std::vector<int> processed_signals_;
+
     /// @brief The number of signals that must be received to stop the test.
     int stop_at_count_;
+
     /// @brief Boolean which causes IOSignalHandler to throw if true.
     bool handler_throw_error_;
 
-    /// @brief Constructor
+    /// @brief Constructor.
     IOSignalTest() :
         io_service_(new asiolink::IOService()), test_timer_(*io_service_),
-        test_time_ms_(0), signal_set_(),
-        io_signal_queue_(new IOSignalQueue(io_service_)),
+        test_time_ms_(0), io_signal_set_(),
         processed_signals_(), stop_at_count_(0), handler_throw_error_(false) {
-    }
-
-    /// @brief Destructor
-    ~IOSignalTest() {
-        if (signal_set_) {
-            signal_set_->clear();
-        }
 
-        // clear the on-receipt handler
-        util::SignalSet::clearOnReceiptHandler();
+        io_signal_set_.reset(new IOSignalSet(
+                                     io_service_,
+                                     boost::bind(&IOSignalTest::processSignal,
+                                                 this, _1)));
     }
 
-    /// @brief On-receipt signal handler used by unit tests.
-    ///
-    /// This function is registered with SignalSet as the "on-receipt" handler.
-    /// When an OS signal is caught it schedules an IOSignal.
-    ///
-    /// @param signum Signal being handled.
-    bool onReceiptHandler(int signum) {
-        // Queue up a signal binging processSignal instance method as the
-        // IOSignalHandler.
-        io_signal_queue_->pushSignal(signum,
-                                  boost::bind(&IOSignalTest::processSignal,
-                                              this, _1));
-
-        // Return true so SignalSet knows the signal has been consumed.
-        return (true);
-    }
+    /// @brief Destructor.
+    ~IOSignalTest() {}
 
-    /// @brief Method used as the IOSignalHandler.
+    /// @brief Method used as the IOSignalSet handler.
     ///
     /// Records the value of the given signal and checks if the desired
     /// number of signals have been received.  If so, the IOService is
     /// stopped which will cause IOService::run() to exit, returning control
     /// to the test.
     ///
-    /// @param sequence_id id of the IOSignal received
-    void processSignal(IOSignalId sequence_id) {
-        // Pop the signal instance off the queue.  This should make us
-        // the only one holding it, so when we leave it should be freed.
-        IOSignalPtr signal = io_signal_queue_->popSignal(sequence_id);
-
+    /// @param signum signal number.
+    void processSignal(int signum) {
         // Remember the signal we got.
-        processed_signals_.push_back(signal->getSignum());
+        processed_signals_.push_back(signum);
 
         // If the flag is on, force a throw to test error handling.
         if (handler_throw_error_) {
@@ -128,99 +104,7 @@ public:
 };
 
 // Used for constructor tests.
-void dummyHandler(IOSignalId) {
-}
-
-// Tests IOSignal constructor.
-TEST(IOSignal, construction) {
-    asiolink::IOServicePtr io_service(new asiolink::IOService());
-    IOSignalPtr signal;
-    IOSignalPtr signal2;
-
-    // Verify that handler cannot be empty.
-    ASSERT_THROW(signal.reset(new IOSignal(*io_service, SIGINT,
-                                           IOSignalHandler())),
-                 IOSignalError);
-
-    // Verify constructor with valid arguments works.
-    ASSERT_NO_THROW(signal.reset(new IOSignal(*io_service, SIGINT,
-                                              dummyHandler)));
-    // Verify sequence_id is set.
-    EXPECT_EQ(IOSignal::nextSequenceId()-1, signal->getSequenceId());
-
-    // Verify SIGINT is correct.
-    EXPECT_EQ(SIGINT, signal->getSignum());
-
-    // Make a second signal.
-    ASSERT_NO_THROW(signal2.reset(new IOSignal(*io_service, SIGUSR1,
-                                               dummyHandler)));
-
-    // Verify sequence_id is not the same as the previous one.
-    EXPECT_NE(signal2->getSequenceId(), signal->getSequenceId());
-
-    // Verify that the signal value is correct.
-    EXPECT_EQ(SIGUSR1, signal2->getSignum());
-}
-
-// Tests IOSignalQueue constructors and exercises queuing methods.
-TEST(IOSignalQueue, constructionAndQueuing) {
-    IOSignalQueuePtr queue;
-    asiolink::IOServicePtr io_service;
-
-    // Verify constructing with an empty IOService will throw.
-    ASSERT_THROW(queue.reset(new IOSignalQueue(io_service)), IOSignalError);
-
-    // Verify valid construction works.
-    io_service.reset(new asiolink::IOService());
-    ASSERT_NO_THROW(queue.reset(new IOSignalQueue(io_service)));
-
-    // Verify an empty handler is not allowed.
-    ASSERT_THROW(queue->pushSignal(SIGINT, IOSignalHandler()), IOSignalError);
-
-    // Verify that we can queue valid entries.
-    std::vector<IOSignalId> ids;
-    ASSERT_NO_THROW(ids.push_back(queue->pushSignal(SIGINT, dummyHandler)));
-    ASSERT_NO_THROW(ids.push_back(queue->pushSignal(SIGUSR1, dummyHandler)));
-    ASSERT_NO_THROW(ids.push_back(queue->pushSignal(SIGUSR2, dummyHandler)));
-
-    // Now verify that we can pop each one and what we pop is correct.
-    // Verify popping it again, throws.  We'll do it in a non-sequential order.
-
-    // Check the middle one.
-    IOSignalPtr signal;
-    ASSERT_NO_THROW(signal = queue->popSignal(ids[1]));
-    ASSERT_TRUE(signal);
-    EXPECT_EQ(ids[1], signal->getSequenceId());
-    EXPECT_EQ(SIGUSR1, signal->getSignum());
-    ASSERT_THROW(queue->popSignal(ids[1]), IOSignalError);
-
-    // Check the first one.
-    ASSERT_NO_THROW(signal = queue->popSignal(ids[0]));
-    ASSERT_TRUE(signal);
-    EXPECT_EQ(ids[0], signal->getSequenceId());
-    EXPECT_EQ(SIGINT, signal->getSignum());
-    ASSERT_THROW(queue->popSignal(ids[0]), IOSignalError);
-
-    // Check the last one.
-    ASSERT_NO_THROW(signal = queue->popSignal(ids[2]));
-    ASSERT_TRUE(signal);
-    EXPECT_EQ(ids[2], signal->getSequenceId());
-    EXPECT_EQ(SIGUSR2, signal->getSignum());
-    ASSERT_THROW(queue->popSignal(ids[2]), IOSignalError);
-
-    // Now we will test clearing the queue.  Queue three signals.
-    ids.clear();
-    for (int i = 0; i < 3; ++i) {
-        ASSERT_NO_THROW(ids.push_back(queue->pushSignal(SIGINT, dummyHandler)));
-    }
-
-    // Now clear the queue.
-    ASSERT_NO_THROW(queue->clear());
-
-    // We should not be able to dequeue any of them.
-    for (int i = 0; i < 3; ++i) {
-        ASSERT_THROW(queue->popSignal(ids[i]), IOSignalError);
-    }
+void dummyHandler(int) {
 }
 
 // Test the basic mechanics of IOSignal by handling one signal occurrence.
@@ -228,16 +112,8 @@ TEST_F(IOSignalTest, singleSignalTest) {
     // Set test fail safe.
     setTestTime(1000);
 
-    // Register the onreceipt-handler with SignalSet.
-    // We set this up to catch the actual signal.  The onreceipt handler
-    // creates an IOSignal which should propagate the signal as a
-    // IOService event.
-    util::SignalSet::
-    setOnReceiptHandler(boost::bind(&IOSignalTest::onReceiptHandler,
-                                    this, _1));
-
     // Register to receive SIGINT.
-    ASSERT_NO_THROW(signal_set_.reset(new util::SignalSet(SIGINT)));
+    ASSERT_NO_THROW(io_signal_set_->add(SIGINT));
 
     // Use TimedSignal to generate SIGINT 100 ms after we start IOService::run.
     TimedSignal sig_int(*io_service_, SIGINT, 100);
@@ -249,6 +125,9 @@ TEST_F(IOSignalTest, singleSignalTest) {
     // The next handler executed is IOSignal's handler.
     io_service_->run_one();
 
+    // Polling once to be sure.
+    io_service_->poll();
+
     // Verify that we processed the signal.
     ASSERT_EQ(1, processed_signals_.size());
 
@@ -256,19 +135,14 @@ TEST_F(IOSignalTest, singleSignalTest) {
     EXPECT_EQ(SIGINT, processed_signals_[0]);
 }
 
-
 // Test verifies that signals can be delivered rapid-fire without falling over.
 TEST_F(IOSignalTest, hammer) {
     // Set test fail safe.  We want to allow at least 100 ms per signal,
     // plus a bit more so 6 seconds ought to be enough.
     setTestTime(6000);
 
-    // Register the onreceipt-handler with SignalSet, and register to receive
-    // SIGINT.
-    util::SignalSet::
-    setOnReceiptHandler(boost::bind(&IOSignalTest::onReceiptHandler,
-                                    this, _1));
-    ASSERT_NO_THROW(signal_set_.reset(new util::SignalSet(SIGINT)));
+    // Register to receive SIGINT.
+    ASSERT_NO_THROW(io_signal_set_->add(SIGINT));
 
     // Stop the test after 50 signals. This allows 100ms+ per signal
     // so even sluggish VMs should handle it.
@@ -298,12 +172,8 @@ TEST_F(IOSignalTest, handlerThrow) {
     // Set test fail safe.
     setTestTime(1000);
 
-    // Register the onreceipt-handler with SignalSet, and register to
-    // receive SIGINT.
-    util::SignalSet::
-    setOnReceiptHandler(boost::bind(&IOSignalTest::onReceiptHandler,
-                                    this, _1));
-    ASSERT_NO_THROW(signal_set_.reset(new util::SignalSet(SIGINT)));
+    // Register to receive SIGINT.
+    ASSERT_NO_THROW(io_signal_set_->add(SIGINT));
 
     // Set the stop after we've done at least 1 all the way through.
     stop_at_count_ = 1;
@@ -330,13 +200,10 @@ TEST_F(IOSignalTest, mixedSignals) {
     // Set test fail safe.
     setTestTime(1000);
 
-    // Register the onreceipt-handler with SignalSet, and register to
-    // receive SIGINT, SIGUSR1, and SIGUSR2.
-    util::SignalSet::
-    setOnReceiptHandler(boost::bind(&IOSignalTest::onReceiptHandler,
-                                    this, _1));
-    ASSERT_NO_THROW(signal_set_.reset(new util::SignalSet(SIGINT, SIGUSR1,
-                                      SIGUSR2)));
+    // Register to receive SIGINT, SIGUSR1, and SIGUSR2.
+    ASSERT_NO_THROW(io_signal_set_->add(SIGINT));
+    ASSERT_NO_THROW(io_signal_set_->add(SIGUSR1));
+    ASSERT_NO_THROW(io_signal_set_->add(SIGUSR2));
 
     // Stop the test after 21 signals.  Needs to be a multiple of 3.
     stop_at_count_ = 21;
index 64bbd4f4fbab37787cdc3b77b9d6c73272d3f9db..ac6ff13ff8ddef781640b566fda7cd19972ac587 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef D_TEST_STUBS_H
 #define D_TEST_STUBS_H
 
-#include <asiolink/io_service.h>
+#include <asiolink/interval_timer.h>
 
 #include <cc/data.h>
 #include <cc/command_interpreter.h>