Kea 2.6.2
d2_queue_mgr.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2024 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8#include <d2/d2_queue_mgr.h>
9#include <d2srv/d2_log.h>
10#include <dhcp_ddns/ncr_udp.h>
11
12namespace isc {
13namespace d2 {
14
15// Makes constant visible to Google test macros.
17
18D2QueueMgr::D2QueueMgr(asiolink::IOServicePtr& io_service, const size_t max_queue_size)
19 : io_service_(io_service), max_queue_size_(max_queue_size),
20 mgr_state_(NOT_INITTED), target_stop_state_(NOT_INITTED) {
21 if (!io_service_) {
22 isc_throw(D2QueueMgrError, "IOServicePtr cannot be null");
23 }
24
25 // Use setter to do validation.
26 setMaxQueueSize(max_queue_size);
27}
28
31
32void
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) {
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
50 // Log that we got the request
54 .arg(ncr->getRequestId());
55 return;
56 }
57
58 // Queue is full, stop the listener.
59 // Note that we can move straight to a STOPPED state as there
60 // is no receive in progress.
62 .arg(max_queue_size_);
64 break;
65
67 if (mgr_state_ == STOPPING) {
68 // This is confirmation that the listener has stopped and its
69 // callback will not be called again, unless its restarted.
70 updateStopState();
71 } else {
72 // We should not get a receive complete status of stopped
73 // unless we canceled the read as part of stopping. Therefore
74 // this is unexpected so we will treat it as a receive error.
75 // This is most likely an unforeseen programmatic issue.
77 .arg(mgr_state_);
79 }
80
81 break;
82
83 default:
84 // Receive failed, stop the listener.
85 // Note that we can move straight to a STOPPED state as there
86 // is no receive in progress.
89 break;
90 }
91 } catch (const std::exception& ex) {
92 // On the outside chance a throw occurs, let's log it and swallow it.
94 .arg(ex.what());
95 }
96}
97
98void
100 const uint32_t port,
101 const dhcp_ddns::NameChangeFormat format,
102 const bool reuse_address) {
103
104 if (listener_) {
106 "D2QueueMgr listener is already initialized");
107 }
108
109 // Instantiate a UDP listener and set state to INITTED.
110 // Note UDP listener constructor does not throw.
111 listener_.reset(new dhcp_ddns::NameChangeUDPListener(ip_address, port, format,
112 shared_from_this(), reuse_address));
113 mgr_state_ = INITTED;
114}
115
116void
118 // We can't listen if we haven't initialized the listener yet.
119 if (!listener_) {
120 isc_throw(D2QueueMgrError, "D2QueueMgr "
121 "listener is not initialized, cannot start listening");
122 }
123
124 // If we are already listening, we do not want to "reopen" the listener
125 // and really we shouldn't be trying.
126 if (mgr_state_ == RUNNING) {
127 isc_throw(D2QueueMgrError, "D2QueueMgr "
128 "cannot call startListening from the RUNNING state");
129 }
130
131 // Instruct the listener to start listening and set state accordingly.
132 try {
133 listener_->startListening(io_service_);
134 mgr_state_ = RUNNING;
135 } catch (const isc::Exception& ex) {
136 isc_throw(D2QueueMgrError, "D2QueueMgr listener start failed: "
137 << ex.what());
138 }
139
142}
143
144void
145D2QueueMgr::stopListening(const State target_stop_state) {
146 if (listener_) {
147 // Enforce only valid "stop" states.
148 // This is purely a programmatic error and should never happen.
149 if (target_stop_state != STOPPED &&
150 target_stop_state != STOPPED_QUEUE_FULL &&
151 target_stop_state != STOPPED_RECV_ERROR) {
153 "D2QueueMgr invalid value for stop state: "
154 << target_stop_state);
155 }
156
157 // Remember the state we want to achieve.
158 target_stop_state_ = target_stop_state;
159
160 // Instruct the listener to stop. If the listener reports that it
161 // has IO pending, then we transition to STOPPING to wait for the
162 // cancellation event. Otherwise, we can move directly to the targeted
163 // state.
164 listener_->stopListening();
165 if (listener_->isIoPending()) {
166 mgr_state_ = STOPPING;
167 } else {
168 updateStopState();
169 }
170 }
171}
172
173void
174D2QueueMgr::updateStopState() {
175 mgr_state_ = target_stop_state_;
178}
179
180void
182 // Force our managing layer(s) to stop us properly first.
183 if (mgr_state_ == RUNNING) {
185 "D2QueueMgr cannot delete listener while state is RUNNING");
186 }
187
188 listener_.reset();
189 mgr_state_ = NOT_INITTED;
190}
191
194 if (getQueueSize() == 0) {
196 "D2QueueMgr peek attempted on an empty queue");
197 }
198
199 return (ncr_queue_.front());
200}
201
203D2QueueMgr::peekAt(const size_t index) const {
204 if (index >= getQueueSize()) {
206 "D2QueueMgr peek beyond end of queue attempted"
207 << " index: " << index << " queue size: " << getQueueSize());
208 }
209
210 return (ncr_queue_.at(index));
211}
212
213void
214D2QueueMgr::dequeueAt(const size_t index) {
215 if (index >= getQueueSize()) {
217 "D2QueueMgr dequeue beyond end of queue attempted"
218 << " index: " << index << " queue size: " << getQueueSize());
219 }
220
221 RequestQueue::iterator pos = ncr_queue_.begin() + index;
222 ncr_queue_.erase(pos);
223}
224
225void
227 if (getQueueSize() == 0) {
229 "D2QueueMgr dequeue attempted on an empty queue");
230 }
231
232 ncr_queue_.pop_front();
233}
234
235void
237 ncr_queue_.push_back(ncr);
238}
239
240void
242 ncr_queue_.clear();
243}
244
245void
246D2QueueMgr::setMaxQueueSize(const size_t new_queue_max) {
247 if (new_queue_max < 1) {
249 "D2QueueMgr maximum queue size must be greater than zero");
250 }
251
252 if (new_queue_max < getQueueSize()) {
253 isc_throw(D2QueueMgrError, "D2QueueMgr maximum queue size value cannot"
254 " be less than the current queue size :" << getQueueSize());
255 }
256
257 max_queue_size_ = new_queue_max;
258}
259
260} // namespace isc::d2
261} // namespace isc
when the call the UDPServer carries on at the same position As a result
Definition asiodns.dox:16
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Thrown if the queue manager encounters a general error.
Thrown if a queue index is beyond the end of the queue.
Thrown if the request queue empty and a read is attempted.
const dhcp_ddns::NameChangeRequestPtr & peek() const
Returns the entry at the front of the queue.
D2QueueMgr(asiolink::IOServicePtr &io_service, const size_t max_queue_size=MAX_QUEUE_DEFAULT)
Constructor.
virtual ~D2QueueMgr()
Destructor.
size_t getMaxQueueSize() const
Returns the maximum number of entries allowed in the queue.
static const size_t MAX_QUEUE_DEFAULT
Maximum number of entries allowed in the request queue.
void dequeue()
Removes the entry at the front of the queue.
State
Defines the list of possible states for D2QueueMgr.
const dhcp_ddns::NameChangeRequestPtr & peekAt(const size_t index) const
Returns the entry at a given position in the queue.
void removeListener()
Deletes the current listener.
void enqueue(dhcp_ddns::NameChangeRequestPtr &ncr)
Adds a request to the end of the queue.
void startListening()
Starts actively listening for requests.
void setMaxQueueSize(const size_t max_queue_size)
Sets the maximum number of entries allowed in the queue.
size_t getQueueSize() const
Returns the number of entries in the queue.
virtual void operator()(const dhcp_ddns::NameChangeListener::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)
Function operator implementing the NCR receive callback.
void clearQueue()
Removes all entries from the queue.
void initUDPListener(const isc::asiolink::IOAddress &ip_address, const uint32_t port, const dhcp_ddns::NameChangeFormat format, const bool reuse_address=false)
Initializes the listener as a UDP listener.
void dequeueAt(const size_t index)
Removes the entry at a given position in the queue.
void stopListening(const State target_stop_state=STOPPED)
Stops listening for requests.
Result
Defines the outcome of an asynchronous NCR receive.
Definition ncr_io.h:171
Provides the ability to receive NameChangeRequests via UDP socket.
Definition ncr_udp.h:317
This file defines the class D2QueueMgr.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECV_ERROR
Definition d2_messages.h:60
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE
Definition d2_messages.h:57
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR
Definition d2_messages.h:68
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP
Definition d2_messages.h:69
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STARTED
Definition d2_messages.h:63
isc::log::Logger dhcp_to_d2_logger("dhcp-to-d2")
Definition d2_log.h:19
isc::log::Logger d2_logger("dhcpddns")
Defines the logger used within D2.
Definition d2_log.h:18
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_FULL
Definition d2_messages.h:56
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPED
Definition d2_messages.h:65
NameChangeFormat
Defines the list of data wire formats supported.
Definition ncr_msg.h:59
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition ncr_msg.h:241
const int DBGLVL_TRACE_BASIC
Trace basic operations.
const int DBGLVL_START_SHUT
This is given a value of 0 as that is the level selected if debugging is enabled without giving a lev...
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
Defines the logger used by the top-level component of kea-lfc.
This file provides UDP socket based implementation for sending and receiving NameChangeRequests.