d2_queue_mgr.cc 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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(isc::asiolink::IOService& io_service,
  22. const size_t max_queue_size)
  23. : io_service_(io_service), max_queue_size_(max_queue_size),
  24. mgr_state_(NOT_INITTED) {
  25. // Use setter to do validation.
  26. setMaxQueueSize(max_queue_size);
  27. }
  28. D2QueueMgr::~D2QueueMgr() {
  29. // clean up
  30. try {
  31. stopListening();
  32. } catch (...) {
  33. // This catch is strictly for safety's sake, in case a future
  34. // implementation isn't tidy or careful.
  35. }
  36. }
  37. void
  38. D2QueueMgr::operator()(const dhcp_ddns::NameChangeListener::Result result,
  39. dhcp_ddns::NameChangeRequestPtr& ncr) {
  40. // Note that error conditions must be handled here without throwing
  41. // exceptions. Remember this is the application level "link" in the
  42. // callback chain. Throwing an exception here will "break" the
  43. // io_service "run" we are operating under. With that in mind,
  44. // if we hit a problem, we will stop the listener transition to
  45. // the appropriate stopped state. Upper layer(s) must monitor our
  46. // state as well as our queue size.
  47. // If the receive was successful, attempt to queue the request.
  48. if (result == dhcp_ddns::NameChangeListener::SUCCESS) {
  49. if (getQueueSize() < getMaxQueueSize()) {
  50. // There's room on the queue, add to the end
  51. enqueue(ncr);
  52. return;
  53. }
  54. // Queue is full, stop the listener.
  55. stopListening(STOPPED_QUEUE_FULL);
  56. LOG_ERROR(dctl_logger, DHCP_DDNS_QUEUE_MGR_QUEUE_FULL)
  57. .arg(max_queue_size_);
  58. } else {
  59. // Receive failed, stop the listener.
  60. stopListening(STOPPED_RECV_ERROR);
  61. LOG_ERROR(dctl_logger, DHCP_DDNS_QUEUE_MGR_RECV_ERROR);
  62. }
  63. }
  64. void
  65. D2QueueMgr::initUDPListener(const isc::asiolink::IOAddress& ip_address,
  66. const uint32_t port,
  67. const dhcp_ddns::NameChangeFormat format,
  68. const bool reuse_address) {
  69. if (listener_) {
  70. isc_throw(D2QueueMgrError,
  71. "D2QueueMgr listener is already initialized");
  72. }
  73. // Instantiate a UDP listener and set state to INITTED.
  74. // Note UDP listener constructor does not throw.
  75. listener_.reset(new dhcp_ddns::
  76. NameChangeUDPListener(ip_address, port, format, *this,
  77. reuse_address));
  78. mgr_state_ = INITTED;
  79. }
  80. void
  81. D2QueueMgr::startListening() {
  82. // We can't listen if we haven't initialized the listener yet.
  83. if (!listener_) {
  84. isc_throw(D2QueueMgrError, "D2QueueMgr "
  85. "listener is not initialized, cannot start listening");
  86. }
  87. // If we are already listening, we do not want to "reopen" the listener
  88. // and really we shouldn't be trying.
  89. if (mgr_state_ == RUNNING) {
  90. isc_throw(D2QueueMgrError, "D2QueueMgr "
  91. "cannot call startListening from the RUNNING state");
  92. }
  93. // Instruct the listener to start listening and set state accordingly.
  94. try {
  95. listener_->startListening(io_service_);
  96. mgr_state_ = RUNNING;
  97. } catch (const isc::Exception& ex) {
  98. isc_throw(D2QueueMgrError, "D2QueueMgr listener start failed: "
  99. << ex.what());
  100. }
  101. }
  102. void
  103. D2QueueMgr::stopListening(const State stop_state) {
  104. // Note, stopListening is guaranteed not to throw.
  105. if (listener_) {
  106. listener_->stopListening();
  107. }
  108. // Enforce only valid "stop" states.
  109. if (stop_state != STOPPED && stop_state != STOPPED_QUEUE_FULL &&
  110. stop_state != STOPPED_RECV_ERROR) {
  111. // This is purely a programmatic error and should never happen.
  112. isc_throw(D2QueueMgrError, "D2QueueMgr invalid value for stop state: "
  113. << stop_state);
  114. }
  115. mgr_state_ = stop_state;
  116. }
  117. void
  118. D2QueueMgr::removeListener() {
  119. // Force our managing layer(s) to stop us properly first.
  120. if (mgr_state_ == RUNNING) {
  121. isc_throw(D2QueueMgrError,
  122. "D2QueueMgr cannot delete listener while state is RUNNING");
  123. }
  124. listener_.reset();
  125. mgr_state_ = NOT_INITTED;
  126. }
  127. const dhcp_ddns::NameChangeRequestPtr&
  128. D2QueueMgr::peek() const {
  129. if (getQueueSize() == 0) {
  130. isc_throw(D2QueueMgrQueueEmpty,
  131. "D2QueueMgr peek attempted on an empty queue");
  132. }
  133. return (ncr_queue_.front());
  134. }
  135. const dhcp_ddns::NameChangeRequestPtr&
  136. D2QueueMgr::peekAt(const size_t index) const {
  137. if (index >= getQueueSize()) {
  138. isc_throw(D2QueueMgrInvalidIndex,
  139. "D2QueueMgr peek beyond end of queue attempted"
  140. << " index: " << index << " queue size: " << getQueueSize());
  141. }
  142. return (ncr_queue_.at(index));
  143. }
  144. void
  145. D2QueueMgr::dequeueAt(const size_t index) {
  146. if (index >= getQueueSize()) {
  147. isc_throw(D2QueueMgrInvalidIndex,
  148. "D2QueueMgr dequeue beyond end of queue attempted"
  149. << " index: " << index << " queue size: " << getQueueSize());
  150. }
  151. RequestQueue::iterator pos = ncr_queue_.begin() + index;
  152. ncr_queue_.erase(pos);
  153. }
  154. void
  155. D2QueueMgr::dequeue() {
  156. if (getQueueSize() == 0) {
  157. isc_throw(D2QueueMgrQueueEmpty,
  158. "D2QueueMgr dequeue attempted on an empty queue");
  159. }
  160. ncr_queue_.pop_front();
  161. }
  162. void
  163. D2QueueMgr::enqueue(dhcp_ddns::NameChangeRequestPtr& ncr) {
  164. ncr_queue_.push_back(ncr);
  165. }
  166. void
  167. D2QueueMgr::clearQueue() {
  168. ncr_queue_.clear();
  169. }
  170. void
  171. D2QueueMgr::setMaxQueueSize(const size_t new_queue_max) {
  172. if (new_queue_max < 1) {
  173. isc_throw(D2QueueMgrError,
  174. "D2QueueMgr maximum queue size must be greater than zero");
  175. }
  176. if (new_queue_max < getQueueSize()) {
  177. isc_throw(D2QueueMgrError, "D2QueueMgr maximum queue size value cannot"
  178. " be less than the current queue size :" << getQueueSize());
  179. }
  180. max_queue_size_ = new_queue_max;
  181. }
  182. } // namespace isc::d2
  183. } // namespace isc