summaryrefslogtreecommitdiffstats
path: root/src/lib/asiolink/io_service_signal.cc
blob: ea1010359df832f07b066842fca3ea8b64283c4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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