Commit 47602142 authored by Thomas Markwalder's avatar Thomas Markwalder

[#278,!162] Minor simplification of PacketQueue<> interface

src/lib/dhcp/packet_queue.h
    PacketQueue<>
        enqueuePacket()
        dequeuePacket() - are now pure virtual,
        and no longer accept a QueueEnd parameter

        shouldDropPacket()
        eatPackets()
        pushPacket()
        popPacket()
        peek() - removed (they are now only in
        PackeQueueRing<> and its derivations

src/lib/dhcp/packet_queue_ring.h
    New fiel which houses PacketQueueRing<> and its derivations
parent 067c78a8
......@@ -47,6 +47,7 @@ libkea_dhcp___la_SOURCES += packet_queue.h
libkea_dhcp___la_SOURCES += packet_queue_mgr.h
libkea_dhcp___la_SOURCES += packet_queue_mgr4.cc packet_queue_mgr4.h
libkea_dhcp___la_SOURCES += packet_queue_mgr6.cc packet_queue_mgr6.h
libkea_dhcp___la_SOURCES += packet_queue_ring.h
libkea_dhcp___la_SOURCES += pkt.cc pkt.h
libkea_dhcp___la_SOURCES += pkt4.cc pkt4.h
libkea_dhcp___la_SOURCES += pkt4o6.cc pkt4o6.h
......@@ -129,6 +130,7 @@ libkea_dhcp___include_HEADERS = \
packet_queue_mgr.h \
packet_queue_mgr4.h \
packet_queue_mgr6.h \
packet_queue_ring.h \
pkt.h \
pkt4.h \
pkt4o6.h \
......
......@@ -42,7 +42,6 @@ enum class QueueEnd {
/// implementations which may be used by @c IfaceMgr to store
/// inbound packets until they are a dequeued for processing.
/// @note Derivations of this class MUST BE thread-safe.
/// @endnote
///
template<typename PacketTypePtr>
class PacketQueue {
......@@ -62,114 +61,25 @@ public:
/// @brief Adds a packet to the queue
///
/// Calls @c shouldDropPacket to determine if the packet should be queued
/// or dropped. If it should be queued it is added to the end of the
/// queue specified by the "to" parameter.
/// Adds the packet to the queue. Derivations determine
/// which packets to queue and how to queue them.
///
/// @param packet packet to enqueue
/// @param source socket the packet came from
/// @param to end of the queue from which to remove packet(s).
/// Defaults to BACK.
///
void enqueuePacket(PacketTypePtr packet, const SocketInfo& source,
const QueueEnd& to=QueueEnd::BACK) {
if (!shouldDropPacket(packet, source)) {
pushPacket(packet, to);
}
}
virtual void enqueuePacket(PacketTypePtr packet, const SocketInfo& source) = 0;
/// @brief Dequeues the next packet from the queue
///
/// Calls @eatPackets to discard packets as needed, and then
/// dequeues the next packet (if any) and returns it. Packets
/// are dequeued from the end of the queue specified by the "from"
/// parameter.
///
/// @param from end of the queue from which to remove packet(s).
/// Defaults to FRONT.
///
/// @return A pointer to dequeued packet, or an empty pointer
/// if the queue is empty.
PacketTypePtr dequeuePacket(const QueueEnd& from=QueueEnd::FRONT) {
eatPackets(from);
return(popPacket(from));
}
/// @brief Determines if a packet should be discarded.
///
/// This function is called at the beginning of @c enqueuePacket and
/// provides an opportunity to examine the packet and its source
/// and decide whether it should be dropped or added to the queue.
/// Derivations are expected to provide implementations based on
/// their own requirements. Bear in mind that the packet has NOT
/// been unpacked at this point. The default implementation simply
/// returns false.
///
/// @param packet the packet under consideration
/// @param source the socket the packet came from
///
/// @return true if the packet should be dropped, false if it should be
/// kept.
virtual bool shouldDropPacket(PacketTypePtr /* packet */,
const SocketInfo& /* source */) {
return (false);
}
/// @brief Discards packets from one end of the queue.
///
/// This function is called at the beginning of @c dequeuePacket and
/// provides an opportunity to examine and discard packets from
/// the queue prior to dequeuing the next packet to be
/// processed. Derivations are expected to provide implementations
/// based on their own requirements. The default implemenation is to
/// to simply return without skipping any packets.
///
/// @param from end of the queue from which packets should discarded
/// This is passed in from @c dequeuePackets.
///
/// @param from specifies the end of the queue from which packets
/// should be discarded.
///
/// @return The number of packets discarded.
virtual int eatPackets(const QueueEnd& /* from */) {
return (0);
}
/// @brief Pushes a packet onto the queue
///
/// Adds a packet onto the end of queue specified.
///
/// @param packet packet to add to the queue
/// @param to specifies the end of the queue to which the packet
/// should be added.
virtual void pushPacket(PacketTypePtr& packet, const QueueEnd& to=QueueEnd::BACK) = 0;
/// @brief Pops a packet from the queue
///
/// Removes a packet from the end of the queue specified and returns it.
///
/// @param from specifies the end of the queue from which the packet
/// should be taken.
/// Dequeues the next packet (if any) and returns it. Derivations determine
/// how packets are dequeued.
///
/// @return A pointer to dequeued packet, or an empty pointer
/// if the queue is empty.
virtual PacketTypePtr popPacket(const QueueEnd& from=QueueEnd::FRONT) = 0;
/// @brief Gets the packet currently at one end of the queue
///
/// Returns a pointer the packet at the specified end of the
/// queue without dequeuing it.
///
/// @param from specifies which end of the queue to examine.
///
/// @return A pointer to packet, or an empty pointer if the
/// queue is empty.
virtual const PacketTypePtr peek(const QueueEnd& from=QueueEnd::FRONT) const = 0;
virtual PacketTypePtr dequeuePacket() = 0;
/// @brief return True if the queue is empty.
virtual bool empty() const = 0;
/// @todo size may not apply either... what if there are two internal buffers?
/// @brief Returns the current number of packets in the buffer.
virtual size_t getSize() const = 0;
......@@ -225,187 +135,6 @@ typedef boost::shared_ptr<PacketQueue<Pkt4Ptr>> PacketQueue4Ptr;
/// DHCPv6 packet queue factories.
typedef boost::shared_ptr<PacketQueue<Pkt6Ptr>> PacketQueue6Ptr;
/// @brief Provides an abstract ring-buffer implementation of the PacketQueue interface.
template<typename PacketTypePtr>
class PacketQueueRing : public PacketQueue<PacketTypePtr> {
public:
/// @brief Minimum queue capacity permitted. Below five is pretty much
/// nonsensical.
static const size_t MIN_RING_CAPACITY = 5;
/// @brief Constructor
///
/// @param queue_type logical name of the queue implementation
/// @param capacity maximum number of packets the queue can hold
PacketQueueRing(const std::string& queue_type, size_t capacity)
: PacketQueue<PacketTypePtr>(queue_type) {
queue_.set_capacity(capacity);
}
/// @brief virtual Destructor
virtual ~PacketQueueRing(){};
/// @brief Pushes a packet onto the queue
///
/// Adds a packet onto the end of queue specified. Note that this
/// function is protected by a Mutex.
///
/// @param packet packet to add to the queue
/// @param to specifies the end of the queue to which the packet
/// should be added.
virtual void pushPacket(PacketTypePtr& packet, const QueueEnd& to=QueueEnd::BACK) {
isc::util::thread::Mutex::Locker lock(mutex_);
if (to == QueueEnd::BACK) {
queue_.push_back(packet);
} else {
queue_.push_front(packet);
}
}
/// @brief Pops a packet from the queue
///
/// Removes a packet from the end of the queue specified and returns it. Note
/// that this function is protected by a Mutex.
///
/// @param from specifies the end of the queue from which the packet
/// should be taken.
///
/// @return A pointer to dequeued packet, or an empty pointer
/// if the queue is empty.
virtual PacketTypePtr popPacket(const QueueEnd& from = QueueEnd::FRONT) {
isc::util::thread::Mutex::Locker lock(mutex_);
PacketTypePtr packet;
if (queue_.empty()) {
return (packet);
}
if (from == QueueEnd::FRONT) {
packet = queue_.front();
queue_.pop_front();
} else {
packet = queue_.back();
queue_.pop_back();
}
return (packet);
}
/// @brief Gets the packet currently at one end of the queue
///
/// Returns a pointer the packet at the specified end of the
/// queue without dequeuing it.
///
/// @param from specifies which end of the queue to examine.
///
/// @return A pointer to packet, or an empty pointer if the
/// queue is empty.
virtual const PacketTypePtr peek(const QueueEnd& from=QueueEnd::FRONT) const {
PacketTypePtr packet;
if (!queue_.empty()) {
packet = (from == QueueEnd::FRONT ? queue_.front() : queue_.back());
}
return (packet);
}
/// @brief Returns True if the queue is empty.
virtual bool empty() const {
return(queue_.empty());
}
/// @brief Returns the maximum number of packets allowed in the buffer.
virtual size_t getCapacity() const {
return (queue_.capacity());
}
/// @brief Sets the maximum number of packets allowed in the buffer.
///
/// @todo - do we want to change size on the fly? This might need
/// to be private, called only by constructor
///
/// @throw BadValue if capacity is too low.
virtual void setCapacity(size_t capacity) {
if (capacity < MIN_RING_CAPACITY) {
isc_throw(BadValue, "Queue capacity of " << capacity
<< " is invalid. It must be at least "
<< MIN_RING_CAPACITY);
}
/// @todo should probably throw if it's zero
queue_.set_capacity(capacity);
}
/// @brief Returns the current number of packets in the buffer.
virtual size_t getSize() const {
return (queue_.size());
}
/// @brief Discards all packets currently in the buffer.
virtual void clear() {
queue_.clear();
}
/// @brief Fetches pertinent information
virtual data::ElementPtr getInfo() const {
data::ElementPtr info = PacketQueue<PacketTypePtr>::getInfo();
info->set("capacity", data::Element::create(static_cast<int64_t>(getCapacity())));
info->set("size", data::Element::create(static_cast<int64_t>(getSize())));
return(info);
}
private:
/// @brief Packet queue
boost::circular_buffer<PacketTypePtr> queue_;
/// @brief Mutex for protecting queue accesses.
isc::util::thread::Mutex mutex_;
};
/// @brief DHCPv4 packet queue buffer implementation
///
/// This implementation does not (currently) add any drop
/// or packet skip logic, it operates as a verbatim ring
/// queue for DHCPv4 packets.
///
class PacketQueueRing4 : public PacketQueueRing<Pkt4Ptr> {
public:
/// @brief Constructor
///
/// @param queue_type logical name of the queue implementation
/// @param capacity maximum number of packets the queue can hold
PacketQueueRing4(const std::string& queue_type, size_t capacity)
: PacketQueueRing(queue_type, capacity) {
};
/// @brief virtual Destructor
virtual ~PacketQueueRing4(){}
};
/// @brief DHCPv6 packet queue buffer implementation
///
/// This implementation does not (currently) add any drop
/// or packet skip logic, it operates as a verbatim ring
/// queue for DHCPv6 packets.
///
class PacketQueueRing6 : public PacketQueueRing<Pkt6Ptr> {
public:
/// @brief Constructor
///
/// @param queue_type logical name of the queue implementation
/// @param capacity maximum number of packets the queue can hold
PacketQueueRing6(const std::string& queue_type, size_t capacity)
: PacketQueueRing(queue_type, capacity) {
};
/// @brief virtual Destructor
virtual ~PacketQueueRing6(){}
};
}; // namespace isc::dhcp
}; // namespace isc
......
......@@ -5,6 +5,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <dhcp/packet_queue_ring.h>
#include <dhcp/packet_queue_mgr4.h>
#include <boost/scoped_ptr.hpp>
......
......@@ -5,6 +5,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <dhcp/packet_queue_ring.h>
#include <dhcp/packet_queue_mgr6.h>
#include <boost/scoped_ptr.hpp>
......
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef PACKET_QUEUE_RING_H
#define PACKET_QUEUE_RING_H
#include <dhcp/packet_queue.h>
#include <util/threads/sync.h>
#include <boost/function.hpp>
#include <boost/circular_buffer.hpp>
#include <sstream>
namespace isc {
namespace dhcp {
/// @brief Provides an abstract ring-buffer implementation of the PacketQueue interface.
template<typename PacketTypePtr>
class PacketQueueRing : public PacketQueue<PacketTypePtr> {
public:
/// @brief Minimum queue capacity permitted. Below five is pretty much
/// nonsensical.
static const size_t MIN_RING_CAPACITY = 5;
/// @brief Constructor
///
/// @param queue_type logical name of the queue implementation
/// @param capacity maximum number of packets the queue can hold
PacketQueueRing(const std::string& queue_type, size_t capacity)
: PacketQueue<PacketTypePtr>(queue_type) {
queue_.set_capacity(capacity);
}
/// @brief virtual Destructor
virtual ~PacketQueueRing(){};
/// @brief Adds a packet to the queue
///
/// Calls @c shouldDropPacket to determine if the packet should be queued
/// or dropped. If it should be queued it is added to the end of the
/// queue specified by the "to" parameter.
///
/// @param packet packet to enqueue
/// @param source socket the packet came from
virtual void enqueuePacket(PacketTypePtr packet, const SocketInfo& source) {
if (!shouldDropPacket(packet, source)) {
pushPacket(packet);
}
}
/// @brief Dequeues the next packet from the queue
///
/// Dequeues the next packet (if any) and returns it.
///
/// @return A pointer to dequeued packet, or an empty pointer
/// if the queue is empty.
virtual PacketTypePtr dequeuePacket() {
eatPackets(QueueEnd::FRONT);
return(popPacket());
}
/// @brief Determines if a packet should be discarded.
///
/// This function is called in @c enqueuePackets for each packet
/// in its packet list. It provides an opportunity to examine the
/// packet and its source and decide whether it should be dropped
/// or added to the queue. Derivations are expected to provide
/// implementations based on their own requirements. Bear in mind
/// that the packet has NOT been unpacked at this point. The default
/// implementation simply returns false (i.e. keep the packet).
///
/// @param packet the packet under consideration
/// @param source the socket the packet came from
///
/// @return true if the packet should be dropped, false if it should be
/// kept.
virtual bool shouldDropPacket(PacketTypePtr /* packet */,
const SocketInfo& /* source */) {
return (false);
}
/// @brief Discards packets from one end of the queue.
///
/// This function is called at the beginning of @c dequeuePacket and
/// provides an opportunity to examine and discard packets from
/// the queue prior to dequeuing the next packet to be
/// processed. Derivations are expected to provide implementations
/// based on their own requirements. The default implemenation is to
/// to simply return without skipping any packets.
///
/// @param from end of the queue from which packets should discarded
/// This is passed in from @c dequeuePackets.
///
/// @param from specifies the end of the queue from which packets
/// should be discarded.
///
/// @return The number of packets discarded.
virtual int eatPackets(const QueueEnd& /* from */) {
return (0);
}
/// @brief Pushes a packet onto the queue
///
/// Adds a packet onto the end of queue specified.
///
/// @param packet packet to add to the queue
/// @param to specifies the end of the queue to which the packet
/// should be added.
virtual void pushPacket(PacketTypePtr& packet, const QueueEnd& to=QueueEnd::BACK) {
if (to == QueueEnd::BACK) {
queue_.push_back(packet);
} else {
queue_.push_front(packet);
}
}
/// @brief Pops a packet from the queue
///
/// Removes a packet from the end of the queue specified and returns it.
///
/// @param from specifies the end of the queue from which the packet
/// should be taken. It locks the queue's Mutex upon entry.
///
/// @return A pointer to dequeued packet, or an empty pointer
/// if the queue is empty.
virtual PacketTypePtr popPacket(const QueueEnd& from = QueueEnd::FRONT) {
isc::util::thread::Mutex::Locker lock(mutex_);
PacketTypePtr packet;
if (queue_.empty()) {
return (packet);
}
if (from == QueueEnd::FRONT) {
packet = queue_.front();
queue_.pop_front();
} else {
packet = queue_.back();
queue_.pop_back();
}
return (packet);
}
/// @brief Gets the packet currently at one end of the queue
///
/// Returns a pointer the packet at the specified end of the
/// queue without dequeuing it.
///
/// @param from specifies which end of the queue to examine.
///
/// @return A pointer to packet, or an empty pointer if the
/// queue is empty.
virtual const PacketTypePtr peek(const QueueEnd& from=QueueEnd::FRONT) const {
PacketTypePtr packet;
if (!queue_.empty()) {
packet = (from == QueueEnd::FRONT ? queue_.front() : queue_.back());
}
return (packet);
}
/// @brief Returns True if the queue is empty.
virtual bool empty() const {
return(queue_.empty());
}
/// @brief Returns the maximum number of packets allowed in the buffer.
virtual size_t getCapacity() const {
return (queue_.capacity());
}
/// @brief Sets the maximum number of packets allowed in the buffer.
///
/// @todo - do we want to change size on the fly? This might need
/// to be private, called only by constructor
///
/// @throw BadValue if capacity is too low.
virtual void setCapacity(size_t capacity) {
if (capacity < MIN_RING_CAPACITY) {
isc_throw(BadValue, "Queue capacity of " << capacity
<< " is invalid. It must be at least "
<< MIN_RING_CAPACITY);
}
/// @todo should probably throw if it's zero
queue_.set_capacity(capacity);
}
/// @brief Returns the current number of packets in the buffer.
virtual size_t getSize() const {
return (queue_.size());
}
/// @brief Discards all packets currently in the buffer.
virtual void clear() {
queue_.clear();
}
/// @brief Fetches pertinent information
virtual data::ElementPtr getInfo() const {
data::ElementPtr info = PacketQueue<PacketTypePtr>::getInfo();
info->set("capacity", data::Element::create(static_cast<int64_t>(getCapacity())));
info->set("size", data::Element::create(static_cast<int64_t>(getSize())));
return(info);
}
private:
/// @brief Packet queue
boost::circular_buffer<PacketTypePtr> queue_;
/// @brief Mutex for protecting queue accesses.
isc::util::thread::Mutex mutex_;
};
/// @brief DHCPv4 packet queue buffer implementation
///
/// This implementation does not (currently) add any drop
/// or packet skip logic, it operates as a verbatim ring
/// queue for DHCPv4 packets.
///
class PacketQueueRing4 : public PacketQueueRing<Pkt4Ptr> {
public:
/// @brief Constructor
///
/// @param queue_type logical name of the queue implementation
/// @param capacity maximum number of packets the queue can hold
PacketQueueRing4(const std::string& queue_type, size_t capacity)
: PacketQueueRing(queue_type, capacity) {
};
/// @brief virtual Destructor
virtual ~PacketQueueRing4(){}
};
/// @brief DHCPv6 packet queue buffer implementation
///
/// This implementation does not (currently) add any drop
/// or packet skip logic, it operates as a verbatim ring
/// queue for DHCPv6 packets.
///
class PacketQueueRing6 : public PacketQueueRing<Pkt6Ptr> {
public:
/// @brief Constructor
///
/// @param queue_type logical name of the queue implementation
/// @param capacity maximum number of packets the queue can hold
PacketQueueRing6(const std::string& queue_type, size_t capacity)
: PacketQueueRing(queue_type, capacity) {
};
/// @brief virtual Destructor
virtual ~PacketQueueRing6(){}
};
}; // namespace isc::dhcp
}; // namespace isc
#endif // PACKET_QUEUE_RING_H
......@@ -6,7 +6,7 @@
#include <config.h>
#include <dhcp/packet_queue.h>
#include <dhcp/packet_queue_ring.h>
#include <dhcp/tests/packet_queue_testutils.h>
#include <boost/shared_ptr.hpp>
......@@ -83,135 +83,124 @@ public:
// Verifies use of the generic PacketQueue interface to
// construct a queue implementation.
TEST(TestQueue4, interfaceBasics) {
TEST(PacketQueueRing4, interfaceBasics) {
// Verify we can create a queue
PacketQueue4Ptr q4(new TestQueue4(100));
ASSERT_TRUE(q4);
PacketQueue4Ptr q(new PacketQueueRing4("kea-ring4",100));
ASSERT_TRUE(q);
// It should be empty.
EXPECT_TRUE(q4->empty());
EXPECT_TRUE(q->empty());
// Type should match.
EXPECT_EQ("kea-ring4", q4->getQueueType());
EXPECT_EQ("kea-ring4", q->getQueueType());
// Fetch the queue info and verify it has all the expected values.
checkInfo(q4, "{ \"capacity\": 100, \"queue-type\": \"kea-ring4\", \"size\": 0 }");
checkInfo(q, "{ \"capacity\": 100, \"queue-type\": \"kea-ring4\", \"size\": 0 }");
}
// Verifies the basic mechanics of the adding and
// removing packets to and from the ring buffer.
TEST(TestQueue4, ringTest) {
PacketQueue4Ptr q4(new TestQueue4(3));
// Verifies the higher level functions of queueing and dequeueing
// from the ring buffer.
TEST(PacketQueueRing4, enqueueDequeueTest) {
PacketQueue4Ptr q(new PacketQueueRing4("kea-ring4", 3));
// Fetch the queue info and verify it has all the expected values.
checkInfo(q4, "{ \"capacity\": 3, \"queue-type\": \"kea-ring4\", \"size\": 0 }");
checkInfo(q, "{ \"capacity\": 3, \"queue-type\": \"kea-ring4\", \"size\": 0 }");
// Enqueue five packets. The first two should be pushed off.
SocketInfo sock1(isc::asiolink::IOAddress("127.0.0.1"), 777, 10);
for (int i = 1; i < 6; ++i) {
Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 1000+i));
ASSERT_NO_THROW(q4->enqueuePacket(pkt, sock1));
checkIntStat(q4, "size", (i > 3 ? 3 : i));
ASSERT_NO_THROW(q->enqueuePacket(pkt, sock1));
}
// Fetch the queue info and verify it has all the expected values.
checkInfo(q4, "{ \"capacity\": 3, \"queue-type\": \"kea-ring4\", \"size\": 3 }");
checkInfo(q, "{ \"capacity\": 3, \"queue-type\": \"kea-ring4\", \"size\": 3 }");
// We should have transids 1003,1004,1005
Pkt4Ptr pkt;
for (int i = 3; i < 6; ++i) {
ASSERT_NO_THROW(pkt = q->dequeuePacket());
ASSERT_TRUE(pkt);
EXPECT_EQ(1000 + i, pkt->getTransid());
}
// Queue should be empty.
ASSERT_TRUE(q->empty());
// Dequeuing should fail safely, with an empty return.