: 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
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
/// @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
/// 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.
/// @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_;
-// 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
-// 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
-// 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
-// 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
#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_) {
};
// 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.
// 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);
// 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());
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.
// 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;
// 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;
#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>