Commit d4dc6b5a authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[3405] Implemented Signal set class to monitor signals.

parent 83816e00
......@@ -1562,6 +1562,7 @@ AC_CONFIG_FILES([compatcheck/Makefile
src/lib/testutils/Makefile
src/lib/testutils/testdata/Makefile
src/lib/util/io/Makefile
src/lib/util/io/tests/Makefile
src/lib/util/Makefile
src/lib/util/python/doxygen2pydoc.py
src/lib/util/python/gen_wiredata.py
......
SUBDIRS = . tests
AM_CXXFLAGS = $(B10_CXXFLAGS)
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
......@@ -6,6 +7,7 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
lib_LTLIBRARIES = libkea-util-io.la
libkea_util_io_la_SOURCES = fd.h fd.cc fd_share.h fd_share.cc
libkea_util_io_la_SOURCES += socketsession.h socketsession.cc sockaddr_util.h
libkea_util_io_la_SOURCES += signal_set.cc signal_set.h
libkea_util_io_la_SOURCES += pktinfo_utilities.h
libkea_util_io_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
......
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <util/io/signal_set.h>
#include <list>
using namespace isc;
using namespace isc::util::io;
namespace {
std::list<int>* getSignalStates() {
static std::list<int> states;
return (&states);
}
void internalHandler(int sig) {
std::list<int>* states = getSignalStates();
for (std::list<int>::const_iterator it = states->begin();
it != states->end(); ++it) {
if (sig == *it) {
return;
}
}
states->push_back(sig);
}
}
namespace isc {
namespace util {
namespace io {
SignalSet::SignalSet(const int sig0) {
add(sig0);
}
SignalSet::SignalSet(const int sig0, const int sig1) {
add(sig0);
add(sig1);
}
SignalSet::SignalSet(const int sig0, const int sig1, const int sig2) {
add(sig0);
add(sig1);
add(sig2);
}
void
SignalSet::add(const int sig) {
std::pair<Pool::iterator, bool> ret = registered_signals_.insert(sig);
if (!ret.second) {
isc_throw(SignalSetError, "attempt to register a duplicate signal "
<< sig);
}
struct sigaction sa;
sa.sa_handler = internalHandler;
if (sigaction(sig, &sa, 0) < 0) {
registered_signals_.erase(sig);
isc_throw(SignalSetError, "failed to register a signal handler for"
" signal " << sig << ": " << strerror(errno));
}
}
void
SignalSet::clear() {
Pool all_signals = registered_signals_;
for (Pool::const_iterator it = all_signals.begin();
it != all_signals.end(); ++it) {
remove(*it);
}
}
int
SignalSet::getNext() const {
std::list<int>* states = getSignalStates();
if (states->empty()) {
return (-1);
}
return (*states->begin());
}
void
SignalSet::handleNext(SignalHandler signal_handler) {
block();
int signum = getNext();
if (signum >= 0) {
popNext();
try {
signal_handler(signum);
} catch (...) {
unblock();
throw;
}
}
unblock();
}
void
SignalSet::maskSignals(const int mask) const {
sigset_t new_set;
for (Pool::const_iterator it = registered_signals_.begin();
it != registered_signals_.end(); ++it) {
sigaddset(&new_set, *it);
}
sigprocmask(mask, &new_set, 0);
}
void
SignalSet::popNext() const {
std::list<int>* states = getSignalStates();
if (!states->empty()) {
states->pop_front();
}
}
void
SignalSet::remove(const int sig) {
if (registered_signals_.find(sig) != registered_signals_.end()) {
struct sigaction sa;
sa.sa_handler = SIG_DFL;
if (sigaction(sig, &sa, 0) < 0) {
isc_throw(SignalSetError, "unable to restore original signal"
" handler for signal: " << sig);
}
registered_signals_.erase(sig);
}
}
} // end of isc::util::io
} // end of isc::util
} // end of isc
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef SIGNAL_SET_H
#define SIGNAL_SET_H
#include <exceptions/exceptions.h>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <set>
#include <signal.h>
namespace isc {
namespace util {
namespace io {
class SignalSetError : public Exception {
public:
SignalSetError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
typedef boost::function<void(int signum)> SignalHandler;
class SignalSet : public boost::noncopyable {
private:
typedef std::set<int> Pool;
public:
SignalSet(const int sig0);
SignalSet(const int sig0, const int sig1);
SignalSet(const int sig0, const int sig1, const int sig2);
/// @throw SignalSetError if signal being added duplicates an existing
/// signal.
void add(const int sig);
void clear();
int getNext() const;
void handleNext(SignalHandler signal_handler);
void remove(const int sig);
private:
void block() const {
maskSignals(SIG_BLOCK);
}
Pool::iterator erase(const int signal);
void maskSignals(const int mask) const;
void popNext() const;
void unblock() const {
maskSignals(SIG_UNBLOCK);
}
Pool registered_signals_;
};
}
}
}
#endif // SIGNAL_SET_H
SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
CLEANFILES = *.gcno *.gcda
TESTS_ENVIRONMENT = \
$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += signal_set_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libkea-util-io.la
run_unittests_LDADD += $(GTEST_LDADD)
endif
noinst_PROGRAMS = $(TESTS)
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <log/logger_support.h>
#include <gtest/gtest.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
int result = RUN_ALL_TESTS();
return (result);
}
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <util/io/signal_set.h>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
#include <signal.h>
namespace {
using namespace isc;
using namespace isc::util::io;
class SignalSetTest : public ::testing::Test {
public:
SignalSetTest()
: handler_calls_(0),
signum_ (-1) {
}
~SignalSetTest() {
if (signal_set_) {
signal_set_->clear();
}
}
void handleNext() {
signal_set_->handleNext(boost::bind(&SignalSetTest::testHandler,
this, _1));
}
void testHandler(int signum) {
signum_ = signum;
++handler_calls_;
}
int handler_calls_;
int signum_;
boost::shared_ptr<SignalSet> signal_set_;
};
TEST_F(SignalSetTest, twoSignals) {
// Register handlers for two signals.
signal_set_.reset(new SignalSet(SIGHUP, SIGINT));
// Send SIGHUP signal to the process.
ASSERT_EQ(0, raise(SIGHUP));
// The SIGHUP should be the next one in the queue to be handled.
EXPECT_EQ(SIGHUP, signal_set_->getNext());
// But, no handlers should have been called yet.
EXPECT_EQ(0, handler_calls_);
// Send a different signal.
ASSERT_EQ(0, raise(SIGINT));
// The SIGHUP hasn't been handled yet so it should still be the first
// one in the queue.
EXPECT_EQ(SIGHUP, signal_set_->getNext());
// No handlers have been called yet.
EXPECT_EQ(0, handler_calls_);
// Raise another SIGHUP before the first one has been handled. The
// second one should be dropped.
ASSERT_EQ(0, raise(SIGHUP));
// Execute the first handler (for SIGHUP).
handleNext();
// The handler should have been called once and the signal
// handled should be SIGHUP.
EXPECT_EQ(1, handler_calls_);
EXPECT_EQ(SIGHUP, signum_);
// Next signal to be handled should be SIGINT.
EXPECT_EQ(SIGINT, signal_set_->getNext());
handleNext();
EXPECT_EQ(2, handler_calls_);
EXPECT_EQ(SIGINT, signum_);
// There should be no more waiting handlers.
EXPECT_EQ(-1, signal_set_->getNext());
// Make sure that signals can be unregistered.
EXPECT_NO_THROW(signal_set_->remove(SIGHUP));
EXPECT_NO_THROW(signal_set_->remove(SIGINT));
}
} // end of anonymous namespace
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