io_service_signal.h 10 KB

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