summaryrefslogtreecommitdiffstats
path: root/src/lib/asiolink/io_service_signal.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/asiolink/io_service_signal.cc')
-rw-r--r--src/lib/asiolink/io_service_signal.cc129
1 files changed, 129 insertions, 0 deletions
diff --git a/src/lib/asiolink/io_service_signal.cc b/src/lib/asiolink/io_service_signal.cc
new file mode 100644
index 0000000..ea10103
--- /dev/null
+++ b/src/lib/asiolink/io_service_signal.cc
@@ -0,0 +1,129 @@
+// Copyright (C) 2020-2021 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/io_service_signal.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/asio/signal_set.hpp>
+#include <functional>
+
+namespace ph = std::placeholders;
+
+namespace isc {
+namespace asiolink {
+
+/// @brief Implementation class of IOSignalSet.
+class IOSignalSetImpl : public boost::enable_shared_from_this<IOSignalSetImpl>,
+ public boost::noncopyable {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param io_service the process IO service.
+ /// @param handler the signal handler.
+ IOSignalSetImpl(IOServicePtr io_service, IOSignalHandler handler);
+
+ /// @brief Destructor.
+ ~IOSignalSetImpl() = default;
+
+ /// @brief Install the callback on the IO service queue.
+ void install();
+
+ /// @brief Add a signal to the ASIO signal set.
+ ///
+ /// @param signum the signal number.
+ void add(int signum);
+
+ /// @brief Remove a signal from the ASIO signal set.
+ ///
+ /// @param signum the signal number.
+ void remove(int signum);
+
+private:
+ /// @brief Extends the lifetime of IOService to avoid heap-use-after-free.
+ IOServicePtr io_service_;
+
+ /// @brief the ASIO signal set.
+ boost::asio::signal_set signal_set_;
+
+ /// @brief the signal handler.
+ IOSignalHandler handler_;
+
+ /// @brief the callback (called on cancel or received signal).
+ ///
+ /// The callback is installed on the IO service queue and calls
+ /// the handler if the operation was not aborted.
+ void callback(const boost::system::error_code& ec, int signum);
+};
+
+IOSignalSetImpl::IOSignalSetImpl(IOServicePtr io_service,
+ IOSignalHandler handler)
+ : io_service_(io_service),
+ signal_set_(io_service_->get_io_service()),
+ handler_(handler) {
+}
+
+void
+IOSignalSetImpl::callback(const boost::system::error_code& ec, int signum) {
+ if (ec && ec.value() == boost::asio::error::operation_aborted) {
+ return;
+ }
+ install();
+ if (!ec && (signum > 0)) {
+ try {
+ handler_(signum);
+ } catch (const std::exception& ex) {
+ }
+ }
+}
+
+void
+IOSignalSetImpl::install() {
+ signal_set_.async_wait(std::bind(&IOSignalSetImpl::callback,
+ shared_from_this(), ph::_1, ph::_2));
+}
+
+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());
+ }
+}
+
+void
+IOSignalSetImpl::remove(int signum) {
+ try {
+ signal_set_.remove(signum);
+ } catch (const boost::system::system_error& ex) {
+ isc_throw(isc::Unexpected,
+ "Failed to remove signal " << signum << ": " << ex.what());
+ }
+}
+
+IOSignalSet::IOSignalSet(IOServicePtr io_service, IOSignalHandler handler) :
+ impl_(new IOSignalSetImpl(io_service, handler)) {
+ // It can throw but the error is fatal...
+ impl_->install();
+}
+
+void
+IOSignalSet::add(int signum) {
+ impl_->add(signum);
+}
+
+void
+IOSignalSet::remove(int signum) {
+ impl_->remove(signum);
+}
+
+} // namespace asiolink
+} // namespace isc