io_service_signal.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. // Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #ifndef IO_SERVICE_SIGNAL_H
  15. #define IO_SERVICE_SIGNAL_H
  16. #include <asiolink/io_service.h>
  17. #include <asiolink/interval_timer.h>
  18. #include <exceptions/exceptions.h>
  19. #include <map>
  20. namespace isc {
  21. namespace d2 {
  22. /// @brief Exception thrown if IOSignal encounters an error.
  23. class IOSignalError : public isc::Exception {
  24. public:
  25. IOSignalError(const char* file, size_t line, const char* what) :
  26. isc::Exception(file, line, what) { };
  27. };
  28. /// @brief Defines a unique identifier type for IOSignal.
  29. typedef uint64_t IOSignalId;
  30. /// @brief Defines a handler function for an IOSignal.
  31. /// IOSignalHandlers should contain the application level logic that would
  32. /// ordinarily be an OS signal handler.
  33. typedef boost::function<void(IOSignalId sequence_id)> IOSignalHandler;
  34. /// @brief Implements an asynchronous "signal" for IOService driven processing
  35. ///
  36. /// This class allows a OS signal such as SIGHUP to propagated to an IOService
  37. /// as a ready event with a callback. While boost::asio provides a signal class,
  38. /// it requires linking in additional boost libraries that as of yet we do not
  39. /// need. Therefore, this class was implemented to allow IOService-based
  40. /// processes to handle signals as IOService events.
  41. ///
  42. /// The mechanics of IOSignal are straight forward. Upon construction it is
  43. /// given the target IOService, the value of the signal to send (i.e. SIGINT,
  44. /// SIGHUP...), and an IOSignalHandler. The IOSignalHandler should contain
  45. /// the logic the caller would normally execute in its OS signal handler. Each
  46. /// IOSignal instance has a unique identifier called its sequence_id.
  47. ///
  48. /// Internally, IOSignal creates a 1 ms, one-shot timer, on the given
  49. /// IOService. When the timer expires its event handler invokes the caller's
  50. /// IOSignalHandler passing it the sequence_id of the IOSignal.
  51. ///
  52. /// Sending IOSignals is done through an IOSignalQueue. This class is used to
  53. /// create the signals, house them until they are delivered, and dequeue them
  54. /// so they can be been handled. To generate an IOSignal when an OS signal
  55. /// arrives, the process's OS signal handler simply calls @ref
  56. /// isc::d2::IOSignalQueue::pushSignal() with the appropriate values.
  57. ///
  58. /// @note that an IOSignalQueue requires a non-null IOServicePtr to construct.
  59. /// This ensures that the IOService cannot be destroyed before any pending
  60. /// signals can be canceled. It also means that a queue can only be used to
  61. /// send signals to that IOService. If you need to send signals to more than
  62. /// one service, each service must have its own queue.
  63. ///
  64. /// To dequeue the IOSignal inside the caller's IOSignalHandler, one simply
  65. /// invokes @ref isc::d2::IOSignalQueue::popSignal() passing it the sequence_id
  66. /// parameter passed to the handler. This method returns a pointer to
  67. /// instigating IOSignal from which the value of OS signal (i.e. SIGINT,
  68. /// SIGUSR1...) can be obtained. Note that calling popSignal() removes the
  69. /// IOSignalPtr from the queue, which should reduce its reference count to
  70. /// zero upon exiting the handler (unless a delibrate copy of it is made).
  71. ///
  72. /// A typical IOSignalHandler might be structured as follows:
  73. /// @code
  74. ///
  75. /// void processSignal(IOSignalId sequence_id) {
  76. /// // Pop the signal instance off the queue.
  77. /// IOSignalPtr signal = io_signal_queue_->popSignal(sequence_id);
  78. ///
  79. /// int os_signal_value = signal->getSignum();
  80. /// :
  81. /// // logic based on the signal value
  82. /// :
  83. /// }
  84. ///
  85. /// @endcode
  86. ///
  87. /// IOSignal handler invocation code will catch, log ,and then swallow any
  88. /// exceptions thrown by a IOSignalHandler invocation. This is done to protect
  89. /// the integrity IOService context.
  90. ///
  91. class IOSignal {
  92. public:
  93. /// @brief Constructor
  94. ///
  95. /// @param io_service IOService to which to send the signal
  96. /// @param signum value of the signal to send
  97. /// @param handler the handler to run when IOService "receives" the
  98. /// signal
  99. ///
  100. /// @throw IOSignalError if handler is null
  101. IOSignal(asiolink::IOService& io_service, int signum,
  102. IOSignalHandler handler);
  103. /// @brief Destructor
  104. ~IOSignal();
  105. /// @brief Static method for generating IOSignal sequence_ids.
  106. ///
  107. /// Generates and returns the next IOSignalId. This method is intentionally
  108. /// static in the event a process is using generating signals to more than
  109. /// IOService. It assures that each IOSignal is unique with the process
  110. /// space.
  111. ///
  112. /// @return The next sequential value as an IOSignalId.
  113. static IOSignalId nextSequenceId() {
  114. static IOSignalId next_id_ = 0;
  115. return (++next_id_);
  116. }
  117. /// @brief Gets the IOSignal's sequence_id
  118. ///
  119. /// @return The sequence_id of the signal.
  120. IOSignalId getSequenceId() const {
  121. return (sequence_id_);
  122. }
  123. /// @brief Gets the OS signal value this IOSignal represents.
  124. ///
  125. /// @return The OS signal value (i.e. SIGINT, SIGUSR1...)
  126. int getSignum() const {
  127. return (signum_);
  128. }
  129. /// @brief Defines the callback used by IOSignal's internal timer.
  130. ///
  131. /// This class stores the sequence_id of the IOSignal being sent and the
  132. /// IOSignalHandler to invoke when delivering the signal. The () operator
  133. /// is called by IOService when the timer expires. This method invokes
  134. /// the IOSignalHandler passing it the sequence_id.
  135. class TimerCallback : public std::unary_function<void, void> {
  136. public:
  137. /// @brief Constructor
  138. ///
  139. /// @param sequence_id sequence_id of the IOSignal to handle
  140. /// @param handler pointer to the function to handle the IOSignal
  141. ///
  142. /// @throw IOSignalError if handler is null.
  143. TimerCallback(IOSignalId sequence_id, IOSignalHandler handler);
  144. /// @brief () Operator which serves as the timer's callback
  145. ///
  146. /// It is invoked when the timer expires and calls the handler
  147. /// passing in the signal.
  148. void operator()();
  149. private:
  150. /// @brief Id of the IOSignal to which the callback pertains.
  151. IOSignalId sequence_id_;
  152. /// @brief Pointer to the function to handle the signal
  153. IOSignalHandler handler_;
  154. };
  155. private:
  156. /// @brief Value which uniquely identifies each IOSignal instance.
  157. IOSignalId sequence_id_;
  158. /// @brief Numeric value of the signal to send (e.g. SIGINT, SIGUSR1...)
  159. int signum_;
  160. /// @brief Timer instance created to propagate the signal.
  161. asiolink::IntervalTimerPtr timer_;
  162. };
  163. /// @brief Defines a pointer to an IOSignal
  164. typedef boost::shared_ptr<IOSignal> IOSignalPtr;
  165. /// @brief Defines a map of IOSignalPtr keyed by id
  166. typedef std::map<IOSignalId, IOSignalPtr> IOSignalMap;
  167. /// @brief Creates and manages IOSignals
  168. ///
  169. /// This class is used to create IOSignals, house them until they are delivered,
  170. /// and dequeue them so they can be been handled. IOSignals are designed to
  171. /// used once and then destroyed. They need to be created from within OS
  172. /// signal handlers and persist until they have been delivered and processed.
  173. ///
  174. /// This class is designed specifically to make managing them painless.
  175. /// It maintains an internal map of IOSignals keyed by sequence_id. When a
  176. /// signal is created via the pushSignal() method it is added to the map. When
  177. /// a signal is retrevied via the popSignal() method it is removed from the map.
  178. class IOSignalQueue {
  179. public:
  180. /// @brief Constructor
  181. ///
  182. /// @param io_service the IOService to which to send signals.
  183. /// @throw IOSignalError if io_service is NULL.
  184. IOSignalQueue (asiolink::IOServicePtr& io_service);
  185. /// @brief Destructor.
  186. ~IOSignalQueue();
  187. /// @brief Creates an IOSignal
  188. ///
  189. /// Given a signal number and a handler, it will instantiate an IOSignal
  190. /// and add it to the instance map. (Remember that IOSignals are really
  191. /// just timers programmed during construction, so once instantiated
  192. /// there's nothing more required to "send" the signal other than return
  193. /// control to IOService::run()).
  194. ///
  195. /// @param signum OS signal value of the signal to propagate
  196. /// @param handler IOSignalHandler to invoke when the signal is delivererd.
  197. ///
  198. /// @return The sequence_id of the newly created signal.
  199. ///
  200. /// @throw IOSignalError if the sequence_id already exists in the map. This
  201. /// is virtually impossible unless things have gone very wrong.
  202. IOSignalId pushSignal(int signum, IOSignalHandler handler);
  203. /// @brief Removes an IOSignal from the map and returns it.
  204. ///
  205. /// Given a sequence_id this method will extract the IOSignal from the
  206. /// internal map and return. At that point, the caller will hold the
  207. /// only copy of the IOSignal.
  208. ///
  209. /// @param sequence_id sequence_id of the IOSignal to retrieve.
  210. ///
  211. /// @return A smart pointer to the IOSignal.
  212. ///
  213. /// @throw IOSignalError if there is no matching IOSignal in the map for
  214. /// the given sequence_id. Other than by doubling popping, this should be
  215. /// very unlikley.
  216. IOSignalPtr popSignal(IOSignalId sequence_id);
  217. /// @brief Erases the contents of the queue.
  218. ///
  219. /// Any instances still in the map will be destroyed. This will cause their
  220. /// timers to be cancelled without any callbacks invoked. (Not sure when
  221. /// this might be desirable).
  222. void clear();
  223. private:
  224. /// @brief Pointer to the IOService which will receive the signals.
  225. asiolink::IOServicePtr io_service_;
  226. /// @brief A map of the IOSignals pushed through this queue.
  227. IOSignalMap signals_;
  228. };
  229. /// @brief Defines a pointer to an IOSignalQueue.
  230. typedef boost::shared_ptr<IOSignalQueue> IOSignalQueuePtr;
  231. }; // end of isc::d2 namespace
  232. }; // end of isc namespace
  233. #endif // IO_SERVICE_SIGNAL_H