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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
|
// Copyright (C) 2014-2016 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/.
#ifndef IO_SERVICE_SIGNAL_H
#define IO_SERVICE_SIGNAL_H
#include <asiolink/io_service.h>
#include <asiolink/interval_timer.h>
#include <exceptions/exceptions.h>
#include <map>
#include <stdint.h>
namespace isc {
namespace process {
/// @brief Exception thrown if IOSignal encounters an error.
class IOSignalError : public isc::Exception {
public:
IOSignalError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Defines a unique identifier type for IOSignal.
typedef uint64_t IOSignalId;
/// @brief Defines a handler function for an IOSignal.
/// IOSignalHandlers should contain the application level logic that would
/// ordinarily be an OS signal handler.
typedef boost::function<void(IOSignalId sequence_id)> IOSignalHandler;
/// @brief Implements an asynchronous "signal" for IOService driven processing
///
/// This class allows a OS signal such as SIGHUP to propagated to an IOService
/// as a ready event with a callback. While boost::asio provides a signal class,
/// it requires linking in additional boost libraries that as of yet we do not
/// need. Therefore, this class was implemented to allow IOService-based
/// processes to handle signals as IOService events.
///
/// The mechanics of IOSignal are straight forward. Upon construction it is
/// given the target IOService, the value of the signal to send (i.e. SIGINT,
/// SIGHUP...), and an IOSignalHandler. The IOSignalHandler should contain
/// the logic the caller would normally execute in its OS signal handler. Each
/// IOSignal instance has a unique identifier called its sequence_id.
///
/// Internally, IOSignal creates a 1 ms, one-shot timer, on the given
/// IOService. When the timer expires its event handler invokes the caller's
/// IOSignalHandler passing it the sequence_id of the IOSignal.
///
/// Sending IOSignals is done through an IOSignalQueue. This class is used to
/// create the signals, house them until they are delivered, and dequeue them
/// so they can be been handled. To generate an IOSignal when an OS signal
/// arrives, the process's OS signal handler simply calls @ref
/// isc::process::IOSignalQueue::pushSignal() with the appropriate values.
///
/// @note that an IOSignalQueue requires a non-null IOServicePtr to construct.
/// This ensures that the IOService cannot be destroyed before any pending
/// signals can be canceled. It also means that a queue can only be used to
/// send signals to that IOService. If you need to send signals to more than
/// one service, each service must have its own queue.
///
/// To dequeue the IOSignal inside the caller's IOSignalHandler, one simply
/// invokes @ref isc::process::IOSignalQueue::popSignal() passing it the
/// sequence_id parameter passed to the handler. This method returns a
/// pointer to instigating IOSignal from which the value of OS signal (i.e.
/// SIGINT, SIGUSR1...) can be obtained. Note that calling popSignal()
/// removes the IOSignalPtr from the queue, which should reduce its
/// reference count to zero upon exiting the handler (unless a delibrate
/// copy of it is made).
///
/// A typical IOSignalHandler might be structured as follows:
/// @code
///
/// void processSignal(IOSignalId sequence_id) {
/// // Pop the signal instance off the queue.
/// IOSignalPtr signal = io_signal_queue_->popSignal(sequence_id);
///
/// int os_signal_value = signal->getSignum();
/// :
/// // logic based on the signal value
/// :
/// }
///
/// @endcode
///
/// IOSignal handler invocation code will catch, log ,and then swallow any
/// exceptions thrown by a IOSignalHandler invocation. This is done to protect
/// the integrity IOService context.
///
class IOSignal {
public:
/// @brief Constructor
///
/// @param io_service IOService to which to send the signal
/// @param signum value of the signal to send
/// @param handler the handler to run when IOService "receives" the
/// signal
///
/// @throw IOSignalError if handler is null
IOSignal(asiolink::IOService& io_service, int signum,
IOSignalHandler handler);
/// @brief Destructor
~IOSignal();
/// @brief Static method for generating IOSignal sequence_ids.
///
/// Generates and returns the next IOSignalId. This method is intentionally
/// static in the event a process is using generating signals to more than
/// IOService. It assures that each IOSignal is unique with the process
/// space.
///
/// @return The next sequential value as an IOSignalId.
static IOSignalId nextSequenceId() {
static IOSignalId next_id_ = 0;
return (++next_id_);
}
/// @brief Gets the IOSignal's sequence_id
///
/// @return The sequence_id of the signal.
IOSignalId getSequenceId() const {
return (sequence_id_);
}
/// @brief Gets the OS signal value this IOSignal represents.
///
/// @return The OS signal value (i.e. SIGINT, SIGUSR1...)
int getSignum() const {
return (signum_);
}
/// @brief Defines the callback used by IOSignal's internal timer.
///
/// This class stores the sequence_id of the IOSignal being sent and the
/// IOSignalHandler to invoke when delivering the signal. The () operator
/// is called by IOService when the timer expires. This method invokes
/// the IOSignalHandler passing it the sequence_id.
class TimerCallback : public std::unary_function<void, void> {
public:
/// @brief Constructor
///
/// @param sequence_id sequence_id of the IOSignal to handle
/// @param handler pointer to the function to handle the IOSignal
///
/// @throw IOSignalError if handler is null.
TimerCallback(IOSignalId sequence_id, IOSignalHandler handler);
/// @brief () Operator which serves as the timer's callback
///
/// It is invoked when the timer expires and calls the handler
/// passing in the signal.
void operator()();
private:
/// @brief Id of the IOSignal to which the callback pertains.
IOSignalId sequence_id_;
/// @brief Pointer to the function to handle the signal
IOSignalHandler handler_;
};
private:
/// @brief Value which uniquely identifies each IOSignal instance.
IOSignalId sequence_id_;
/// @brief Numeric value of the signal to send (e.g. SIGINT, SIGUSR1...)
int signum_;
/// @brief Timer instance created to propagate the signal.
asiolink::IntervalTimerPtr timer_;
};
/// @brief Defines a pointer to an IOSignal
typedef boost::shared_ptr<IOSignal> IOSignalPtr;
/// @brief Defines a map of IOSignalPtr keyed by id
typedef std::map<IOSignalId, IOSignalPtr> IOSignalMap;
/// @brief Creates and manages IOSignals
///
/// This class is used to create IOSignals, house them until they are delivered,
/// and dequeue them so they can be been handled. IOSignals are designed to
/// used once and then destroyed. They need to be created from within OS
/// signal handlers and persist until they have been delivered and processed.
///
/// This class is designed specifically to make managing them painless.
/// It maintains an internal map of IOSignals keyed by sequence_id. When a
/// signal is created via the pushSignal() method it is added to the map. When
/// a signal is retrevied via the popSignal() method it is removed from the map.
class IOSignalQueue {
public:
/// @brief Constructor
///
/// @param io_service the IOService to which to send signals.
/// @throw IOSignalError if io_service is NULL.
IOSignalQueue (asiolink::IOServicePtr& io_service);
/// @brief Destructor.
~IOSignalQueue();
/// @brief Creates an IOSignal
///
/// Given a signal number and a handler, it will instantiate an IOSignal
/// and add it to the instance map. (Remember that IOSignals are really
/// just timers programmed during construction, so once instantiated
/// there's nothing more required to "send" the signal other than return
/// control to IOService::run()).
///
/// @param signum OS signal value of the signal to propagate
/// @param handler IOSignalHandler to invoke when the signal is delivererd.
///
/// @return The sequence_id of the newly created signal.
///
/// @throw IOSignalError if the sequence_id already exists in the map. This
/// is virtually impossible unless things have gone very wrong.
IOSignalId pushSignal(int signum, IOSignalHandler handler);
/// @brief Removes an IOSignal from the map and returns it.
///
/// Given a sequence_id this method will extract the IOSignal from the
/// internal map and return. At that point, the caller will hold the
/// only copy of the IOSignal.
///
/// @param sequence_id sequence_id of the IOSignal to retrieve.
///
/// @return A smart pointer to the IOSignal.
///
/// @throw IOSignalError if there is no matching IOSignal in the map for
/// the given sequence_id. Other than by doubling popping, this should be
/// very unlikley.
IOSignalPtr popSignal(IOSignalId sequence_id);
/// @brief Erases the contents of the queue.
///
/// Any instances still in the map will be destroyed. This will cause their
/// timers to be cancelled without any callbacks invoked. (Not sure when
/// this might be desirable).
void clear();
private:
/// @brief Pointer to the IOService which will receive the signals.
asiolink::IOServicePtr io_service_;
/// @brief A map of the IOSignals pushed through this queue.
IOSignalMap signals_;
};
/// @brief Defines a pointer to an IOSignalQueue.
typedef boost::shared_ptr<IOSignalQueue> IOSignalQueuePtr;
}; // end of isc::process namespace
}; // end of isc namespace
#endif // IO_SERVICE_SIGNAL_H
|