d2_queue_mgr.cc 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. // Copyright (C) 2013 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. #include <d2/d2_log.h>
  15. #include <d2/d2_queue_mgr.h>
  16. #include <dhcp_ddns/ncr_udp.h>
  17. namespace isc {
  18. namespace d2 {
  19. // Makes constant visible to Google test macros.
  20. const size_t D2QueueMgr::MAX_QUEUE_DEFAULT;
  21. D2QueueMgr::D2QueueMgr(IOServicePtr& io_service, const size_t max_queue_size)
  22. : io_service_(io_service), max_queue_size_(max_queue_size),
  23. mgr_state_(NOT_INITTED), target_stop_state_(NOT_INITTED) {
  24. if (!io_service_) {
  25. isc_throw(D2QueueMgrError, "IOServicePtr cannot be null");
  26. }
  27. // Use setter to do validation.
  28. setMaxQueueSize(max_queue_size);
  29. }
  30. D2QueueMgr::~D2QueueMgr() {
  31. }
  32. void
  33. D2QueueMgr::operator()(const dhcp_ddns::NameChangeListener::Result result,
  34. dhcp_ddns::NameChangeRequestPtr& ncr) {
  35. try {
  36. // Note that error conditions must be handled here without throwing
  37. // exceptions. Remember this is the application level "link" in the
  38. // callback chain. Throwing an exception here will "break" the
  39. // io_service "run" we are operating under. With that in mind,
  40. // if we hit a problem, we will stop the listener transition to
  41. // the appropriate stopped state. Upper layer(s) must monitor our
  42. // state as well as our queue size.
  43. switch (result) {
  44. case dhcp_ddns::NameChangeListener::SUCCESS:
  45. // Receive was successful, attempt to queue the request.
  46. if (getQueueSize() < getMaxQueueSize()) {
  47. // There's room on the queue, add to the end
  48. enqueue(ncr);
  49. return;
  50. }
  51. // Queue is full, stop the listener.
  52. // Note that we can move straight to a STOPPED state as there
  53. // is no receive in progress.
  54. LOG_ERROR(dctl_logger, DHCP_DDNS_QUEUE_MGR_QUEUE_FULL)
  55. .arg(max_queue_size_);
  56. stopListening(STOPPED_QUEUE_FULL);
  57. break;
  58. case dhcp_ddns::NameChangeListener::STOPPED:
  59. if (mgr_state_ == STOPPING) {
  60. // This is confirmation that the listener has stopped and its
  61. // callback will not be called again, unless its restarted.
  62. updateStopState();
  63. } else {
  64. // We should not get an receive complete status of stopped
  65. // unless we canceled the read as part of stopping. Therefore
  66. // this is unexpected so we will treat it as a receive error.
  67. // This is most likely an unforeseen programmatic issue.
  68. LOG_ERROR(dctl_logger, DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP)
  69. .arg(mgr_state_);
  70. stopListening(STOPPED_RECV_ERROR);
  71. }
  72. break;
  73. default:
  74. // Receive failed, stop the listener.
  75. // Note that we can move straight to a STOPPED state as there
  76. // is no receive in progress.
  77. LOG_ERROR(dctl_logger, DHCP_DDNS_QUEUE_MGR_RECV_ERROR);
  78. stopListening(STOPPED_RECV_ERROR);
  79. break;
  80. }
  81. } catch (const std::exception& ex) {
  82. // On the outside chance a throw occurs, let's log it and swallow it.
  83. LOG_ERROR(dctl_logger, DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR)
  84. .arg(ex.what());
  85. }
  86. }
  87. void
  88. D2QueueMgr::initUDPListener(const isc::asiolink::IOAddress& ip_address,
  89. const uint32_t port,
  90. const dhcp_ddns::NameChangeFormat format,
  91. const bool reuse_address) {
  92. if (listener_) {
  93. isc_throw(D2QueueMgrError,
  94. "D2QueueMgr listener is already initialized");
  95. }
  96. // Instantiate a UDP listener and set state to INITTED.
  97. // Note UDP listener constructor does not throw.
  98. listener_.reset(new dhcp_ddns::
  99. NameChangeUDPListener(ip_address, port, format, *this,
  100. reuse_address));
  101. mgr_state_ = INITTED;
  102. }
  103. void
  104. D2QueueMgr::startListening() {
  105. // We can't listen if we haven't initialized the listener yet.
  106. if (!listener_) {
  107. isc_throw(D2QueueMgrError, "D2QueueMgr "
  108. "listener is not initialized, cannot start listening");
  109. }
  110. // If we are already listening, we do not want to "reopen" the listener
  111. // and really we shouldn't be trying.
  112. if (mgr_state_ == RUNNING) {
  113. isc_throw(D2QueueMgrError, "D2QueueMgr "
  114. "cannot call startListening from the RUNNING state");
  115. }
  116. // Instruct the listener to start listening and set state accordingly.
  117. try {
  118. listener_->startListening(*io_service_);
  119. mgr_state_ = RUNNING;
  120. } catch (const isc::Exception& ex) {
  121. isc_throw(D2QueueMgrError, "D2QueueMgr listener start failed: "
  122. << ex.what());
  123. }
  124. LOG_INFO (dctl_logger, DHCP_DDNS_QUEUE_MGR_STARTED);
  125. }
  126. void
  127. D2QueueMgr::stopListening(const State target_stop_state) {
  128. if (listener_) {
  129. // Enforce only valid "stop" states.
  130. // This is purely a programmatic error and should never happen.
  131. if (target_stop_state != STOPPED &&
  132. target_stop_state != STOPPED_QUEUE_FULL &&
  133. target_stop_state != STOPPED_RECV_ERROR) {
  134. isc_throw(D2QueueMgrError,
  135. "D2QueueMgr invalid value for stop state: "
  136. << target_stop_state);
  137. }
  138. // Remember the state we want to acheive.
  139. target_stop_state_ = target_stop_state;
  140. // Instruct the listener to stop. If the listener reports that it
  141. // has IO pending, then we transition to STOPPING to wait for the
  142. // cancellation event. Otherwise, we can move directly to the targeted
  143. // state.
  144. listener_->stopListening();
  145. if (listener_->isIoPending()) {
  146. mgr_state_ = STOPPING;
  147. } else {
  148. updateStopState();
  149. }
  150. }
  151. }
  152. void
  153. D2QueueMgr::updateStopState() {
  154. mgr_state_ = target_stop_state_;
  155. LOG_INFO (dctl_logger, DHCP_DDNS_QUEUE_MGR_STOPPED);
  156. }
  157. void
  158. D2QueueMgr::removeListener() {
  159. // Force our managing layer(s) to stop us properly first.
  160. if (mgr_state_ == RUNNING) {
  161. isc_throw(D2QueueMgrError,
  162. "D2QueueMgr cannot delete listener while state is RUNNING");
  163. }
  164. listener_.reset();
  165. mgr_state_ = NOT_INITTED;
  166. }
  167. const dhcp_ddns::NameChangeRequestPtr&
  168. D2QueueMgr::peek() const {
  169. if (getQueueSize() == 0) {
  170. isc_throw(D2QueueMgrQueueEmpty,
  171. "D2QueueMgr peek attempted on an empty queue");
  172. }
  173. return (ncr_queue_.front());
  174. }
  175. const dhcp_ddns::NameChangeRequestPtr&
  176. D2QueueMgr::peekAt(const size_t index) const {
  177. if (index >= getQueueSize()) {
  178. isc_throw(D2QueueMgrInvalidIndex,
  179. "D2QueueMgr peek beyond end of queue attempted"
  180. << " index: " << index << " queue size: " << getQueueSize());
  181. }
  182. return (ncr_queue_.at(index));
  183. }
  184. void
  185. D2QueueMgr::dequeueAt(const size_t index) {
  186. if (index >= getQueueSize()) {
  187. isc_throw(D2QueueMgrInvalidIndex,
  188. "D2QueueMgr dequeue beyond end of queue attempted"
  189. << " index: " << index << " queue size: " << getQueueSize());
  190. }
  191. RequestQueue::iterator pos = ncr_queue_.begin() + index;
  192. ncr_queue_.erase(pos);
  193. }
  194. void
  195. D2QueueMgr::dequeue() {
  196. if (getQueueSize() == 0) {
  197. isc_throw(D2QueueMgrQueueEmpty,
  198. "D2QueueMgr dequeue attempted on an empty queue");
  199. }
  200. ncr_queue_.pop_front();
  201. }
  202. void
  203. D2QueueMgr::enqueue(dhcp_ddns::NameChangeRequestPtr& ncr) {
  204. ncr_queue_.push_back(ncr);
  205. }
  206. void
  207. D2QueueMgr::clearQueue() {
  208. ncr_queue_.clear();
  209. }
  210. void
  211. D2QueueMgr::setMaxQueueSize(const size_t new_queue_max) {
  212. if (new_queue_max < 1) {
  213. isc_throw(D2QueueMgrError,
  214. "D2QueueMgr maximum queue size must be greater than zero");
  215. }
  216. if (new_queue_max < getQueueSize()) {
  217. isc_throw(D2QueueMgrError, "D2QueueMgr maximum queue size value cannot"
  218. " be less than the current queue size :" << getQueueSize());
  219. }
  220. max_queue_size_ = new_queue_max;
  221. }
  222. } // namespace isc::d2
  223. } // namespace isc