Commit 75d29c25 authored by Thomas Markwalder's avatar Thomas Markwalder
Browse files

[3407] Addressed review comments

Added missing parameter and method commentary.
Extended unit tests as suggested.
Moved onreceipt_handler_ from SignalSet to
anonymous namespace.
parent ade8283d
...@@ -405,6 +405,9 @@ protected: ...@@ -405,6 +405,9 @@ protected:
/// @return returns an Element that contains the results of shutdown /// @return returns an Element that contains the results of shutdown
/// command composed of an integer status value (0 means successful, /// command composed of an integer status value (0 means successful,
/// non-zero means failure), and a string explanation of the outcome. /// non-zero means failure), and a string explanation of the outcome.
///
/// @param args is a set of derivation-specific arguments (if any)
/// for the shutdown command.
isc::data::ConstElementPtr shutdownProcess(isc::data::ConstElementPtr args); isc::data::ConstElementPtr shutdownProcess(isc::data::ConstElementPtr args);
/// @brief Initializes signal handling /// @brief Initializes signal handling
...@@ -437,6 +440,8 @@ protected: ...@@ -437,6 +440,8 @@ protected:
/// obtained from the IOSignal. This allows derivations to supply a /// obtained from the IOSignal. This allows derivations to supply a
/// custom signal processing method, while ensuring IOSignalQueue /// custom signal processing method, while ensuring IOSignalQueue
/// integrity. /// integrity.
///
/// @param sequence_id id of the IOSignal instance "received"
void ioSignalHandler(IOSignalId sequence_id); void ioSignalHandler(IOSignalId sequence_id);
/// @brief Fetches the current process /// @brief Fetches the current process
......
...@@ -108,6 +108,7 @@ public: ...@@ -108,6 +108,7 @@ public:
IOSignal(asiolink::IOService& io_service, int signum, IOSignal(asiolink::IOService& io_service, int signum,
IOSignalHandler handler); IOSignalHandler handler);
/// @brief Destructor
~IOSignal(); ~IOSignal();
/// @brief Static method for generating IOSignal sequence_ids. /// @brief Static method for generating IOSignal sequence_ids.
...@@ -203,7 +204,7 @@ public: ...@@ -203,7 +204,7 @@ public:
/// @throw IOSignalError if io_service is NULL. /// @throw IOSignalError if io_service is NULL.
IOSignalQueue (IOServicePtr& io_service); IOSignalQueue (IOServicePtr& io_service);
/// Destructor. /// @brief Destructor.
~IOSignalQueue(); ~IOSignalQueue();
/// @brief Creates an IOSignal /// @brief Creates an IOSignal
......
...@@ -43,7 +43,7 @@ public: ...@@ -43,7 +43,7 @@ public:
virtual ~DStubControllerTest() { virtual ~DStubControllerTest() {
} }
DStubControllerPtr controller_; DStubControllerPtr controller_;
}; };
...@@ -355,7 +355,7 @@ TEST_F(DStubControllerTest, ioSignals) { ...@@ -355,7 +355,7 @@ TEST_F(DStubControllerTest, ioSignals) {
// base class signal handler. // base class signal handler.
controller_->recordSignalOnly(true); controller_->recordSignalOnly(true);
// Setup to raise SIGHUP in 10 ms. // Setup to raise SIGHUP in 10 ms.
TimedSignal sighup(*getIOService(), SIGHUP, 10); TimedSignal sighup(*getIOService(), SIGHUP, 10);
TimedSignal sigint(*getIOService(), SIGINT, 10); TimedSignal sigint(*getIOService(), SIGINT, 10);
TimedSignal sigterm(*getIOService(), SIGTERM, 10); TimedSignal sigterm(*getIOService(), SIGTERM, 10);
...@@ -380,17 +380,17 @@ TEST_F(DStubControllerTest, invalidConfigReload) { ...@@ -380,17 +380,17 @@ TEST_F(DStubControllerTest, invalidConfigReload) {
// new content is invalid JSON which will cause the config parse to fail. // new content is invalid JSON which will cause the config parse to fail.
scheduleTimedWrite("{ \"string_test\": BOGUS JSON }", 100); scheduleTimedWrite("{ \"string_test\": BOGUS JSON }", 100);
// Setup to raise SIGHUP in 200 ms. // Setup to raise SIGHUP in 200 ms.
TimedSignal sighup(*getIOService(), SIGHUP, 200); TimedSignal sighup(*getIOService(), SIGHUP, 200);
// Write the config and then run launch() for 500 ms // Write the config and then run launch() for 500 ms
// After startup, which will load the initial configuration this enters // After startup, which will load the initial configuration this enters
// the process's runIO() loop. We will first rewrite the config file. // the process's runIO() loop. We will first rewrite the config file.
// Next we process the SIGHUP signal which should cause us to reconfigure. // Next we process the SIGHUP signal which should cause us to reconfigure.
time_duration elapsed_time; time_duration elapsed_time;
runWithConfig("{ \"string_test\": \"first value\" }", 500, elapsed_time); runWithConfig("{ \"string_test\": \"first value\" }", 500, elapsed_time);
// Context is still available post launch. Check to see that our // Context is still available post launch. Check to see that our
// configuration value is still the original value. // configuration value is still the original value.
std::string actual_value = ""; std::string actual_value = "";
ASSERT_NO_THROW(getContext()->getParam("string_test", actual_value)); ASSERT_NO_THROW(getContext()->getParam("string_test", actual_value));
...@@ -409,28 +409,30 @@ TEST_F(DStubControllerTest, validConfigReload) { ...@@ -409,28 +409,30 @@ TEST_F(DStubControllerTest, validConfigReload) {
// file is updated after we have done the initial configuration. // file is updated after we have done the initial configuration.
scheduleTimedWrite("{ \"string_test\": \"second value\" }", 100); scheduleTimedWrite("{ \"string_test\": \"second value\" }", 100);
// Setup to raise SIGHUP in 200 ms. // Setup to raise SIGHUP in 200 ms and another at 400 ms.
TimedSignal sighup(*getIOService(), SIGHUP, 200); TimedSignal sighup(*getIOService(), SIGHUP, 200);
TimedSignal sighup2(*getIOService(), SIGHUP, 400);
// Write the config and then run launch() for 500 ms // Write the config and then run launch() for 800 ms
time_duration elapsed_time; time_duration elapsed_time;
runWithConfig("{ \"string_test\": \"first value\" }", 500, elapsed_time); runWithConfig("{ \"string_test\": \"first value\" }", 800, elapsed_time);
// Context is still available post launch. // Context is still available post launch.
// Check to see that our configuration value is what we expect. // Check to see that our configuration value is what we expect.
std::string actual_value = ""; std::string actual_value = "";
ASSERT_NO_THROW(getContext()->getParam("string_test", actual_value)); ASSERT_NO_THROW(getContext()->getParam("string_test", actual_value));
EXPECT_EQ("second value", actual_value); EXPECT_EQ("second value", actual_value);
// Verify that we saw the signal. // Verify that we saw two occurrences of the signal.
std::vector<int>& signals = controller_->getProcessedSignals(); std::vector<int>& signals = controller_->getProcessedSignals();
ASSERT_EQ(1, signals.size()); ASSERT_EQ(2, signals.size());
EXPECT_EQ(SIGHUP, signals[0]); EXPECT_EQ(SIGHUP, signals[0]);
EXPECT_EQ(SIGHUP, signals[1]);
} }
// Tests that the SIGINT triggers a normal shutdown. // Tests that the SIGINT triggers a normal shutdown.
TEST_F(DStubControllerTest, sigintShutdown) { TEST_F(DStubControllerTest, sigintShutdown) {
// Setup to raise SIGHUP in 1 ms. // Setup to raise SIGHUP in 1 ms.
TimedSignal sighup(*getIOService(), SIGINT, 1); TimedSignal sighup(*getIOService(), SIGINT, 1);
// Write the config and then run launch() for 1000 ms // Write the config and then run launch() for 1000 ms
...@@ -448,7 +450,7 @@ TEST_F(DStubControllerTest, sigintShutdown) { ...@@ -448,7 +450,7 @@ TEST_F(DStubControllerTest, sigintShutdown) {
// Tests that the SIGTERM triggers a normal shutdown. // Tests that the SIGTERM triggers a normal shutdown.
TEST_F(DStubControllerTest, sigtermShutdown) { TEST_F(DStubControllerTest, sigtermShutdown) {
// Setup to raise SIGHUP in 1 ms. // Setup to raise SIGHUP in 1 ms.
TimedSignal sighup(*getIOService(), SIGTERM, 1); TimedSignal sighup(*getIOService(), SIGTERM, 1);
// Write the config and then run launch() for 1000 ms // Write the config and then run launch() for 1000 ms
......
...@@ -230,6 +230,8 @@ public: ...@@ -230,6 +230,8 @@ public:
/// DControllerBase::processSignals will also be invoked. This switch is /// DControllerBase::processSignals will also be invoked. This switch is
/// useful for ensuring that IOSignals are delivered as expected without /// useful for ensuring that IOSignals are delivered as expected without
/// incurring the full impact such as reconfiguring or shutting down. /// incurring the full impact such as reconfiguring or shutting down.
///
/// @param value boolean which if true enables record-only behavior
void recordSignalOnly(bool value) { void recordSignalOnly(bool value) {
record_signal_only_ = value; record_signal_only_ = value;
} }
...@@ -280,6 +282,14 @@ protected: ...@@ -280,6 +282,14 @@ protected:
/// @return returns a string containing the option letters. /// @return returns a string containing the option letters.
virtual const std::string getCustomOpts() const; virtual const std::string getCustomOpts() const;
/// @brief Application-level "signal handler"
///
/// Overrides the base class implementation such that this method
/// is invoked each time an IOSignal is processed. It records the
/// signal received and unless we are in record-only behavior, it
/// in invokes the base class implementation.
///
/// @param signum OS signal value received
virtual void processSignal(int signum); virtual void processSignal(int signum);
private: private:
......
...@@ -139,6 +139,7 @@ void dummyHandler(IOSignalId) { ...@@ -139,6 +139,7 @@ void dummyHandler(IOSignalId) {
TEST(IOSignal, construction) { TEST(IOSignal, construction) {
IOServicePtr io_service(new asiolink::IOService()); IOServicePtr io_service(new asiolink::IOService());
IOSignalPtr signal; IOSignalPtr signal;
IOSignalPtr signal2;
// Verify that handler cannot be empty. // Verify that handler cannot be empty.
ASSERT_THROW(signal.reset(new IOSignal(*io_service, SIGINT, ASSERT_THROW(signal.reset(new IOSignal(*io_service, SIGINT,
...@@ -153,6 +154,16 @@ TEST(IOSignal, construction) { ...@@ -153,6 +154,16 @@ TEST(IOSignal, construction) {
// Verify SIGINT is correct. // Verify SIGINT is correct.
EXPECT_EQ(SIGINT, signal->getSignum()); 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. // Tests IOSignalQueue constructors and exercises queuing methods.
...@@ -168,22 +179,52 @@ TEST(IOSignalQueue, constructionAndQueuing) { ...@@ -168,22 +179,52 @@ TEST(IOSignalQueue, constructionAndQueuing) {
ASSERT_NO_THROW(queue.reset(new IOSignalQueue(io_service))); ASSERT_NO_THROW(queue.reset(new IOSignalQueue(io_service)));
// Verify an empty handler is not allowed. // Verify an empty handler is not allowed.
ASSERT_THROW(queue->pushSignal(SIGINT, IOSignalHandler()), ASSERT_THROW(queue->pushSignal(SIGINT, IOSignalHandler()), IOSignalError);
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)));
// Verify we can queue up a valid entry. // Now verify that we can pop each one and what we pop is correct.
IOSignalId sequence_id = queue->pushSignal(SIGINT, dummyHandler); // Verify popping it again, throws. We'll do it in a non-sequential order.
// Verify we can pop the entry. // Check the middle one.
IOSignalPtr signal = queue->popSignal(sequence_id); IOSignalPtr signal;
ASSERT_NO_THROW(signal = queue->popSignal(ids[1]));
ASSERT_TRUE(signal); ASSERT_TRUE(signal);
EXPECT_EQ(ids[1], signal->getSequenceId());
EXPECT_EQ(SIGUSR1, signal->getSignum());
ASSERT_THROW(queue->popSignal(ids[1]), IOSignalError);
// Verify the one we popped is right. // Check the first one.
EXPECT_EQ(sequence_id, signal->getSequenceId()); ASSERT_NO_THROW(signal = queue->popSignal(ids[0]));
ASSERT_TRUE(signal);
EXPECT_EQ(ids[0], signal->getSequenceId());
EXPECT_EQ(SIGINT, signal->getSignum()); 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());
// Verify popping it again, throws. // We should not be able to dequeue any of them.
ASSERT_THROW(queue->popSignal(sequence_id), IOSignalError); for (int i = 0; i < 3; ++i) {
ASSERT_THROW(queue->popSignal(ids[i]), IOSignalError);
}
} }
// Test the basic mechanics of IOSignal by handling one signal occurrence. // Test the basic mechanics of IOSignal by handling one signal occurrence.
......
...@@ -78,17 +78,17 @@ void internalHandler(int sig) { ...@@ -78,17 +78,17 @@ void internalHandler(int sig) {
states->push_back(sig); states->push_back(sig);
} }
/// @brief Optional handler to execute at the time of signal receipt
BoolSignalHandler onreceipt_handler_ = BoolSignalHandler();
}; // end anon namespace }; // end anon namespace
namespace isc { namespace isc {
namespace util { namespace util {
const BoolSignalHandler SignalSet::EMPTY_BOOL_HANDLER = BoolSignalHandler();
BoolSignalHandler SignalSet::onreceipt_handler_ = EMPTY_BOOL_HANDLER;
bool bool
SignalSet::invokeOnReceiptHandler(int sig) { SignalSet::invokeOnReceiptHandler(int sig) {
if (!SignalSet::onreceipt_handler_) { if (!onreceipt_handler_) {
return (false); return (false);
} }
...@@ -109,7 +109,7 @@ SignalSet::invokeOnReceiptHandler(int sig) { ...@@ -109,7 +109,7 @@ SignalSet::invokeOnReceiptHandler(int sig) {
// Call the registered handler. // Call the registered handler.
bool signal_processed = false; bool signal_processed = false;
try { try {
signal_processed = SignalSet::onreceipt_handler_(sig); signal_processed = onreceipt_handler_(sig);
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
// Restore the handler. We might fail to restore it, but we likely // Restore the handler. We might fail to restore it, but we likely
// have bigger issues anyway. // have bigger issues anyway.
...@@ -291,7 +291,7 @@ SignalSet::setOnReceiptHandler(BoolSignalHandler handler) { ...@@ -291,7 +291,7 @@ SignalSet::setOnReceiptHandler(BoolSignalHandler handler) {
void void
SignalSet::clearOnReceiptHandler() { SignalSet::clearOnReceiptHandler() {
onreceipt_handler_ = EMPTY_BOOL_HANDLER; onreceipt_handler_ = BoolSignalHandler();
} }
} // end of isc::util } // end of isc::util
......
...@@ -79,10 +79,6 @@ typedef boost::function<bool(int signum)> BoolSignalHandler; ...@@ -79,10 +79,6 @@ typedef boost::function<bool(int signum)> BoolSignalHandler;
/// signals' queue. /// signals' queue.
class SignalSet : public boost::noncopyable { class SignalSet : public boost::noncopyable {
public: public:
/// @brief Optional handler to execute at the time of signal receipt
static BoolSignalHandler onreceipt_handler_;
static const BoolSignalHandler EMPTY_BOOL_HANDLER;
/// @brief Constructor installing one signal. /// @brief Constructor installing one signal.
/// ///
/// @param sig0 First signal. /// @param sig0 First signal.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment