From: Marcin Siodelski Date: Thu, 29 May 2014 17:30:39 +0000 (+0200) Subject: [3405] Implemented Signal set class to monitor signals. X-Git-Tag: trac3434_base~11^2~17 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d4dc6b5a8e0210346a26e5e88101b281f786e2c5;p=thirdparty%2Fkea.git [3405] Implemented Signal set class to monitor signals. --- diff --git a/configure.ac b/configure.ac index 8e6680bb0b..71a2e007dc 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/src/lib/util/io/Makefile.am b/src/lib/util/io/Makefile.am index 4011bde483..c0fae76b1f 100644 --- a/src/lib/util/io/Makefile.am +++ b/src/lib/util/io/Makefile.am @@ -1,3 +1,4 @@ +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 diff --git a/src/lib/util/io/signal_set.cc b/src/lib/util/io/signal_set.cc new file mode 100644 index 0000000000..07eddb0a93 --- /dev/null +++ b/src/lib/util/io/signal_set.cc @@ -0,0 +1,144 @@ +// 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 + +#include + +using namespace isc; +using namespace isc::util::io; + +namespace { + +std::list* getSignalStates() { + static std::list states; + return (&states); +} + +void internalHandler(int sig) { + std::list* states = getSignalStates(); + for (std::list::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 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* 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* 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 diff --git a/src/lib/util/io/signal_set.h b/src/lib/util/io/signal_set.h new file mode 100644 index 0000000000..b1c31a0c29 --- /dev/null +++ b/src/lib/util/io/signal_set.h @@ -0,0 +1,84 @@ +// 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 +#include +#include +#include +#include + +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 SignalHandler; + +class SignalSet : public boost::noncopyable { +private: + typedef std::set 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 + diff --git a/src/lib/util/io/tests/Makefile.am b/src/lib/util/io/tests/Makefile.am new file mode 100644 index 0000000000..d71c28b8c3 --- /dev/null +++ b/src/lib/util/io/tests/Makefile.am @@ -0,0 +1,31 @@ +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) diff --git a/src/lib/util/io/tests/run_unittests.cc b/src/lib/util/io/tests/run_unittests.cc new file mode 100644 index 0000000000..304c652382 --- /dev/null +++ b/src/lib/util/io/tests/run_unittests.cc @@ -0,0 +1,23 @@ +// 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 +#include + +int +main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + int result = RUN_ALL_TESTS(); + return (result); +} diff --git a/src/lib/util/io/tests/signal_set_unittest.cc b/src/lib/util/io/tests/signal_set_unittest.cc new file mode 100644 index 0000000000..a8ac2da2f0 --- /dev/null +++ b/src/lib/util/io/tests/signal_set_unittest.cc @@ -0,0 +1,93 @@ +// 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 +#include +#include +#include +#include + +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 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