mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 05:55:28 +00:00
[#1147] Checkpoint: updated the v4 client
This commit is contained in:
@@ -10,8 +10,10 @@
|
||||
#include <dhcp4/dhcp4_log.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <stats/stats_mgr.h>
|
||||
#include <util/multi_threading_mgr.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc::util;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
@@ -27,15 +29,34 @@ ClientHandler::ClientHandler()
|
||||
}
|
||||
|
||||
ClientHandler::~ClientHandler() {
|
||||
bool unlocked = false;
|
||||
lock_guard<mutex> lock_(mutex_);
|
||||
if (locked_client_id_) {
|
||||
unlocked = true;
|
||||
unLockById();
|
||||
}
|
||||
locked_client_id_.reset();
|
||||
if (locked_hwaddr_) {
|
||||
unlocked = true;
|
||||
unLockByHWAddr();
|
||||
}
|
||||
locked_hwaddr_.reset();
|
||||
if (!client_) {
|
||||
return;
|
||||
}
|
||||
if (!unlocked || !client_->cont_) {
|
||||
client_.reset();
|
||||
return;
|
||||
}
|
||||
// Try to process next query. As the caller holds the mutex of
|
||||
// the handler class the continuation will be resumed after.
|
||||
MultiThreadingMgr& mt_mgr = MultiThreadingMgr::instance();
|
||||
if (mt_mgr.getMode()) {
|
||||
if (!mt_mgr.getThreadPool().addFront(client_->cont_)) {
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_PACKET_QUEUE_FULL);
|
||||
}
|
||||
}
|
||||
client_->cont_.reset();
|
||||
client_.reset();
|
||||
}
|
||||
|
||||
@@ -128,7 +149,7 @@ ClientHandler::unLockByHWAddr() {
|
||||
}
|
||||
|
||||
bool
|
||||
ClientHandler::tryLock(Pkt4Ptr query) {
|
||||
ClientHandler::tryLock(Pkt4Ptr query, ContinuationPtr cont) {
|
||||
// Sanity checks.
|
||||
if (!query) {
|
||||
isc_throw(InvalidParameter, "null query in ClientHandler::tryLock");
|
||||
@@ -159,6 +180,7 @@ ClientHandler::tryLock(Pkt4Ptr query) {
|
||||
|
||||
ClientPtr holder_id;
|
||||
ClientPtr holder_hw;
|
||||
client_.reset(new Client(query, duid, hwaddr));
|
||||
|
||||
// Try first duid.
|
||||
if (duid) {
|
||||
@@ -168,7 +190,6 @@ ClientHandler::tryLock(Pkt4Ptr query) {
|
||||
holder_id = lookup(duid);
|
||||
if (!holder_id) {
|
||||
locked_client_id_ = duid;
|
||||
client_.reset(new Client(query, duid, hwaddr));
|
||||
lockById();
|
||||
}
|
||||
}
|
||||
@@ -182,15 +203,30 @@ ClientHandler::tryLock(Pkt4Ptr query) {
|
||||
holder_hw = lookup(hwaddr);
|
||||
if (!holder_hw) {
|
||||
locked_hwaddr_ = hwaddr;
|
||||
client_.reset(new Client(query, duid, hwaddr));
|
||||
lockByHWAddr();
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
if (holder_id) {
|
||||
// This query is a by-client-id-option duplicate:
|
||||
// currently it is simply dropped.
|
||||
// This query is a by-id duplicate so put the continuation.
|
||||
if (cont) {
|
||||
Pkt4Ptr next_query = holder_id->next_query_;
|
||||
holder_id->next_query_ = query;
|
||||
holder_id->cont_ = cont;
|
||||
if (next_query) {
|
||||
// Logging a warning as it is supposed to be a rare event
|
||||
// with well behaving clients...
|
||||
LOG_WARN(bad_packet4_logger, DHCP4_PACKET_DROP_0011)
|
||||
.arg(next_query->toText())
|
||||
.arg(this_thread::get_id())
|
||||
.arg(query->toText())
|
||||
.arg(this_thread::get_id());
|
||||
stats::StatsMgr::instance().addValue("pkt4-receive-drop",
|
||||
static_cast<int64_t>(1));
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
// Logging a warning as it is supposed to be a rare event
|
||||
// with well behaving clients...
|
||||
LOG_WARN(bad_packet4_logger, DHCP4_PACKET_DROP_0011)
|
||||
@@ -198,9 +234,27 @@ ClientHandler::tryLock(Pkt4Ptr query) {
|
||||
.arg(this_thread::get_id())
|
||||
.arg(holder_id->query_->toText())
|
||||
.arg(holder_id->thread_);
|
||||
stats::StatsMgr::instance().addValue("pkt4-receive-drop",
|
||||
static_cast<int64_t>(1));
|
||||
} else {
|
||||
// This query is a by-hardware address duplicate:
|
||||
// currently it is simply dropped.
|
||||
// This query is a by-hw duplicate so put the continuation.
|
||||
if (cont) {
|
||||
Pkt4Ptr next_query = holder_hw->next_query_;
|
||||
holder_hw->next_query_ = query;
|
||||
holder_hw->cont_ = cont;
|
||||
if (next_query) {
|
||||
// Logging a warning as it is supposed to be a rare event
|
||||
// with well behaving clients...
|
||||
LOG_WARN(bad_packet4_logger, DHCP4_PACKET_DROP_0012)
|
||||
.arg(next_query->toText())
|
||||
.arg(this_thread::get_id())
|
||||
.arg(query->toText())
|
||||
.arg(this_thread::get_id());
|
||||
stats::StatsMgr::instance().addValue("pkt4-receive-drop",
|
||||
static_cast<int64_t>(1));
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
// Logging a warning as it is supposed to be a rare event
|
||||
// with well behaving clients...
|
||||
LOG_WARN(bad_packet4_logger, DHCP4_PACKET_DROP_0012)
|
||||
@@ -208,9 +262,9 @@ ClientHandler::tryLock(Pkt4Ptr query) {
|
||||
.arg(this_thread::get_id())
|
||||
.arg(holder_hw->query_->toText())
|
||||
.arg(holder_hw->thread_);
|
||||
stats::StatsMgr::instance().addValue("pkt4-receive-drop",
|
||||
static_cast<int64_t>(1));
|
||||
}
|
||||
stats::StatsMgr::instance().addValue("pkt4-receive-drop",
|
||||
static_cast<int64_t>(1));
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
@@ -24,20 +25,39 @@ namespace dhcp {
|
||||
class ClientHandler : public boost::noncopyable {
|
||||
public:
|
||||
|
||||
/// @brief Define the type of packet processing continuation.
|
||||
typedef std::function<void()> Continuation;
|
||||
|
||||
/// @brief Define the type of shared pointers to continuations.
|
||||
typedef boost::shared_ptr<Continuation> ContinuationPtr;
|
||||
|
||||
/// @brief Continuation factory.
|
||||
///
|
||||
/// @param cont Continuation rvalue.
|
||||
static ContinuationPtr makeContinuation(Continuation&& cont) {
|
||||
return (boost::make_shared<Continuation>(cont));
|
||||
}
|
||||
|
||||
/// @brief Constructor.
|
||||
ClientHandler();
|
||||
|
||||
/// @brief Destructor.
|
||||
///
|
||||
/// Releases the client if it was acquired.
|
||||
/// Releases the client if it was acquired, if it has also a continuation,
|
||||
/// push it at front of the thread packet queue.
|
||||
virtual ~ClientHandler();
|
||||
|
||||
/// @brief Tries to acquires a client.
|
||||
///
|
||||
/// Lookup the client:
|
||||
/// - if not found insert the client in the clients map and return true
|
||||
/// - if found put the continuation in the holder and return false
|
||||
///
|
||||
/// @param query The query from the client.
|
||||
/// @param cont The continuation in the case the client was held.
|
||||
/// @return true if the client was acquired, false if there is already
|
||||
/// a query from the same client.
|
||||
bool tryLock(Pkt4Ptr query);
|
||||
bool tryLock(Pkt4Ptr query, ContinuationPtr cont = ContinuationPtr());
|
||||
|
||||
private:
|
||||
|
||||
@@ -66,6 +86,12 @@ private:
|
||||
|
||||
/// @brief The ID of the thread processing the query.
|
||||
std::thread::id thread_;
|
||||
|
||||
/// @brief The next query.
|
||||
Pkt4Ptr next_query_;
|
||||
|
||||
/// @brief The continuation to process next query for the client.
|
||||
ContinuationPtr cont_;
|
||||
};
|
||||
|
||||
/// @brief The type of shared pointers to clients by ID.
|
||||
|
@@ -9,6 +9,8 @@
|
||||
#include <dhcp4/client_handler.h>
|
||||
#include <dhcp4/tests/dhcp4_test_utils.h>
|
||||
#include <stats/stats_mgr.h>
|
||||
#include <util/multi_threading_mgr.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace isc;
|
||||
using namespace isc::dhcp;
|
||||
@@ -25,7 +27,8 @@ public:
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// Creates the pkt4-receive-drop statistic.
|
||||
ClientHandleTest() {
|
||||
ClientHandleTest() : called1_(false), called2_(false), called3_(false) {
|
||||
MultiThreadingMgr::instance().apply(false, 0, 0);
|
||||
StatsMgr::instance().setValue("pkt4-receive-drop", static_cast<int64_t>(0));
|
||||
}
|
||||
|
||||
@@ -33,6 +36,7 @@ public:
|
||||
///
|
||||
/// Removes statistics.
|
||||
~ClientHandleTest() {
|
||||
MultiThreadingMgr::instance().apply(false, 0, 0);
|
||||
StatsMgr::instance().removeAll();
|
||||
}
|
||||
|
||||
@@ -78,6 +82,37 @@ public:
|
||||
EXPECT_EQ(0, obs->getInteger().first);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Waits for pending continuations.
|
||||
void waitForThreads() {
|
||||
while (MultiThreadingMgr::instance().getThreadPool().count() > 0) {
|
||||
usleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Set called1_ to true.
|
||||
void setCalled1() {
|
||||
called1_ = true;
|
||||
}
|
||||
|
||||
/// @brief Set called2_ to true.
|
||||
void setCalled2() {
|
||||
called2_ = true;
|
||||
}
|
||||
|
||||
/// @brief Set called3_ to true.
|
||||
void setCalled3() {
|
||||
called3_ = true;
|
||||
}
|
||||
|
||||
/// @brief The called flag number 1.
|
||||
bool called1_;
|
||||
|
||||
/// @brief The called flag number 2.
|
||||
bool called2_;
|
||||
|
||||
/// @brief The called flag number 3.
|
||||
bool called3_;
|
||||
};
|
||||
|
||||
// Verifies behavior with empty block.
|
||||
@@ -88,14 +123,15 @@ TEST_F(ClientHandleTest, empty) {
|
||||
} catch (const std::exception& ex) {
|
||||
ADD_FAILURE() << "unexpected exception: " << ex.what();
|
||||
}
|
||||
checkStat(false);
|
||||
}
|
||||
|
||||
// Verifies behavior with one query.
|
||||
TEST_F(ClientHandleTest, oneQuery) {
|
||||
// Get a query.
|
||||
Pkt4Ptr sol(new Pkt4(DHCPDISCOVER, 1234));
|
||||
sol->addOption(generateClientId());
|
||||
sol->setHWAddr(generateHWAddr());
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
dis->addOption(generateClientId());
|
||||
dis->setHWAddr(generateHWAddr());
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
@@ -103,7 +139,7 @@ TEST_F(ClientHandleTest, oneQuery) {
|
||||
|
||||
// Try to lock it.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
@@ -116,22 +152,22 @@ TEST_F(ClientHandleTest, oneQuery) {
|
||||
// Verifies behavior with two queries for the same client (id).
|
||||
TEST_F(ClientHandleTest, sharedQueriesById) {
|
||||
// Get two queries.
|
||||
Pkt4Ptr sol(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
|
||||
OptionPtr client_id = generateClientId();
|
||||
// Same client ID: same client.
|
||||
sol->addOption(client_id);
|
||||
dis->addOption(client_id);
|
||||
req->addOption(client_id);
|
||||
sol->setHWAddr(generateHWAddr());
|
||||
dis->setHWAddr(generateHWAddr());
|
||||
req->setHWAddr(generateHWAddr(55));
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
ClientHandler client_handler;
|
||||
|
||||
// Try to lock it with the solicit.
|
||||
// Try to lock it with the discover.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
@@ -153,22 +189,22 @@ TEST_F(ClientHandleTest, sharedQueriesById) {
|
||||
// Verifies behavior with two queries for the same client (hw).
|
||||
TEST_F(ClientHandleTest, sharedQueriesByHWAddr) {
|
||||
// Get two queries.
|
||||
Pkt4Ptr sol(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
|
||||
sol->addOption(generateClientId());
|
||||
dis->addOption(generateClientId());
|
||||
req->addOption(generateClientId(111));
|
||||
HWAddrPtr hwaddr = generateHWAddr();
|
||||
// Same hardware address: same client.
|
||||
sol->setHWAddr(hwaddr);
|
||||
dis->setHWAddr(hwaddr);
|
||||
req->setHWAddr(hwaddr);
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
ClientHandler client_handler;
|
||||
|
||||
// Try to lock it with the solicit.
|
||||
// Try to lock it with the discover.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
@@ -190,21 +226,21 @@ TEST_F(ClientHandleTest, sharedQueriesByHWAddr) {
|
||||
// Verifies behavior with two queries for the same client (hw only).
|
||||
TEST_F(ClientHandleTest, sharedQueriesByHWAddrOnly) {
|
||||
// Get two queries.
|
||||
Pkt4Ptr sol(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
|
||||
// No client ID.
|
||||
HWAddrPtr hwaddr = generateHWAddr();
|
||||
// Same hardware address: same client.
|
||||
sol->setHWAddr(hwaddr);
|
||||
dis->setHWAddr(hwaddr);
|
||||
req->setHWAddr(hwaddr);
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
ClientHandler client_handler;
|
||||
|
||||
// Try to lock it with the solicit.
|
||||
// Try to lock it with the discover.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
@@ -226,24 +262,24 @@ TEST_F(ClientHandleTest, sharedQueriesByHWAddrOnly) {
|
||||
// Verifies behavior with a sequence of two queries.
|
||||
TEST_F(ClientHandleTest, sequence) {
|
||||
// Get two queries.
|
||||
Pkt4Ptr sol(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
|
||||
OptionPtr client_id = generateClientId();
|
||||
// Same client ID: same client.
|
||||
sol->addOption(client_id);
|
||||
dis->addOption(client_id);
|
||||
req->addOption(client_id);
|
||||
HWAddrPtr hwaddr = generateHWAddr();
|
||||
// Same hardware address: same client.
|
||||
sol->setHWAddr(hwaddr);
|
||||
dis->setHWAddr(hwaddr);
|
||||
req->setHWAddr(hwaddr);
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
ClientHandler client_handler;
|
||||
|
||||
// Try to lock it with the solicit.
|
||||
// Try to lock it with the discover.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
@@ -272,25 +308,25 @@ TEST_F(ClientHandleTest, sequence) {
|
||||
// Verifies behavior with different clients.
|
||||
TEST_F(ClientHandleTest, notSharedQueries) {
|
||||
// Get two queries.
|
||||
Pkt4Ptr sol(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
|
||||
OptionPtr client_id = generateClientId();
|
||||
OptionPtr client_id2 = generateClientId(111);
|
||||
HWAddrPtr hwaddr = generateHWAddr();
|
||||
HWAddrPtr hwaddr1 = generateHWAddr(55);
|
||||
// Different client ID and hardware address: different client.
|
||||
sol->addOption(client_id);
|
||||
dis->addOption(client_id);
|
||||
req->addOption(client_id2);
|
||||
sol->setHWAddr(hwaddr);
|
||||
dis->setHWAddr(hwaddr);
|
||||
req->setHWAddr(hwaddr1);
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
ClientHandler client_handler;
|
||||
|
||||
// Try to lock it with the solicit.
|
||||
// Try to lock it with the discover.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
@@ -312,7 +348,7 @@ TEST_F(ClientHandleTest, notSharedQueries) {
|
||||
// Verifies behavior without client ID nor hardware address.
|
||||
TEST_F(ClientHandleTest, noClientIdHWAddr) {
|
||||
// Get two queries.
|
||||
Pkt4Ptr sol(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
|
||||
// No client id nor hardware address: nothing to recognize the client.
|
||||
|
||||
@@ -320,9 +356,9 @@ TEST_F(ClientHandleTest, noClientIdHWAddr) {
|
||||
// Get a client handler.
|
||||
ClientHandler client_handler;
|
||||
|
||||
// Try to lock it with the solicit.
|
||||
// Try to lock it with the discover.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
@@ -355,11 +391,11 @@ TEST_F(ClientHandleTest, noQuery) {
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies that double tryLock call fails.
|
||||
TEST_F(ClientHandleTest, doubleTryLock) {
|
||||
// Verifies that double tryLock call fails (id only).
|
||||
TEST_F(ClientHandleTest, doubleTryLockById) {
|
||||
// Get a query.
|
||||
Pkt4Ptr sol(new Pkt4(DHCPDISCOVER, 1234));
|
||||
sol->addOption(generateClientId());
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
dis->addOption(generateClientId());
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
@@ -367,13 +403,13 @@ TEST_F(ClientHandleTest, doubleTryLock) {
|
||||
|
||||
// Try to lock it.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
|
||||
// Try to lock a second time.
|
||||
EXPECT_THROW(client_handler.tryLock(sol), Unexpected);
|
||||
EXPECT_THROW(client_handler.tryLock(dis), Unexpected);
|
||||
} catch (const std::exception& ex) {
|
||||
ADD_FAILURE() << "unexpected exception: " << ex.what();
|
||||
}
|
||||
@@ -382,8 +418,8 @@ TEST_F(ClientHandleTest, doubleTryLock) {
|
||||
// Verifies that double tryLock call fails (hw only).
|
||||
TEST_F(ClientHandleTest, doubleTryLockByHWAddr) {
|
||||
// Get a query without a client ID.
|
||||
Pkt4Ptr sol(new Pkt4(DHCPDISCOVER, 1234));
|
||||
sol->setHWAddr(generateHWAddr());
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
dis->setHWAddr(generateHWAddr());
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
@@ -391,13 +427,13 @@ TEST_F(ClientHandleTest, doubleTryLockByHWAddr) {
|
||||
|
||||
// Try to lock it.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
|
||||
// Try to lock a second time.
|
||||
EXPECT_THROW(client_handler.tryLock(sol), Unexpected);
|
||||
EXPECT_THROW(client_handler.tryLock(dis), Unexpected);
|
||||
} catch (const std::exception& ex) {
|
||||
ADD_FAILURE() << "unexpected exception: " << ex.what();
|
||||
}
|
||||
@@ -406,4 +442,454 @@ TEST_F(ClientHandleTest, doubleTryLockByHWAddr) {
|
||||
// Cannot verifies that empty client ID fails because getClientId() handles
|
||||
// this condition and replaces it by no client ID.
|
||||
|
||||
// Verifies behavior with two queries for the same client and
|
||||
// multi-threading, by client id option version.
|
||||
TEST_F(ClientHandleTest, serializeTwoQueriesById) {
|
||||
// Get two queries.
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
|
||||
OptionPtr client_id = generateClientId();
|
||||
// Same client ID: same client.
|
||||
dis->addOption(client_id);
|
||||
req->addOption(client_id);
|
||||
dis->setHWAddr(generateHWAddr());
|
||||
req->setHWAddr(generateHWAddr(55));
|
||||
|
||||
// Start multi-threading.
|
||||
EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
ClientHandler client_handler;
|
||||
|
||||
// Create a continuation.
|
||||
ClientHandler::ContinuationPtr cont1 =
|
||||
ClientHandler::makeContinuation(std::bind(&ClientHandleTest::setCalled1, this));
|
||||
|
||||
// Try to lock it with the discover.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis, cont1));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
|
||||
// Get a second client handler.
|
||||
ClientHandler client_handler2;
|
||||
|
||||
// Create a continuation.
|
||||
ClientHandler::ContinuationPtr cont2 =
|
||||
ClientHandler::makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
|
||||
|
||||
// Try to lock it with a request.
|
||||
EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req, cont2));
|
||||
|
||||
// Should return false (multi-threading enforces serialization).
|
||||
EXPECT_FALSE(duplicate);
|
||||
} catch (const std::exception& ex) {
|
||||
ADD_FAILURE() << "unexpected exception: " << ex.what();
|
||||
}
|
||||
|
||||
// Give the second continuation a chance.
|
||||
waitForThreads();
|
||||
|
||||
// Force multi-threading to stop;
|
||||
MultiThreadingCriticalSection cs;
|
||||
|
||||
checkStat(false);
|
||||
EXPECT_FALSE(called1_);
|
||||
EXPECT_TRUE(called2_);
|
||||
}
|
||||
|
||||
// Verifies behavior with two queries for the same client and
|
||||
// multi-threading, by hardware address version.
|
||||
TEST_F(ClientHandleTest, serializeTwoQueriesByHWAddr) {
|
||||
// Get two queries.
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
|
||||
dis->addOption(generateClientId());
|
||||
req->addOption(generateClientId(111));
|
||||
HWAddrPtr hwaddr = generateHWAddr();
|
||||
// Same hardware address: same client.
|
||||
dis->setHWAddr(hwaddr);
|
||||
req->setHWAddr(hwaddr);
|
||||
|
||||
// Start multi-threading.
|
||||
EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
ClientHandler client_handler;
|
||||
|
||||
// Create a continuation.
|
||||
ClientHandler::ContinuationPtr cont1 =
|
||||
ClientHandler::makeContinuation(std::bind(&ClientHandleTest::setCalled1, this));
|
||||
|
||||
// Try to lock it with the discover.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis, cont1));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
|
||||
// Get a second client handler.
|
||||
ClientHandler client_handler2;
|
||||
|
||||
// Create a continuation.
|
||||
ClientHandler::ContinuationPtr cont2 =
|
||||
ClientHandler::makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
|
||||
|
||||
// Try to lock it with a request.
|
||||
EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req, cont2));
|
||||
|
||||
// Should return false (multi-threading enforces serialization).
|
||||
EXPECT_FALSE(duplicate);
|
||||
} catch (const std::exception& ex) {
|
||||
ADD_FAILURE() << "unexpected exception: " << ex.what();
|
||||
}
|
||||
|
||||
// Give the second continuation a chance.
|
||||
waitForThreads();
|
||||
|
||||
// Force multi-threading to stop;
|
||||
MultiThreadingCriticalSection cs;
|
||||
|
||||
checkStat(false);
|
||||
EXPECT_FALSE(called1_);
|
||||
EXPECT_TRUE(called2_);
|
||||
}
|
||||
|
||||
// Verifies behavior with two queries for the same client and multi-threading.
|
||||
// Continuations are required for serialization. By client id option version.
|
||||
TEST_F(ClientHandleTest, serializeNoContById) {
|
||||
// Get two queries.
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
|
||||
OptionPtr client_id = generateClientId();
|
||||
// Same client ID: same client.
|
||||
dis->addOption(client_id);
|
||||
req->addOption(client_id);
|
||||
dis->setHWAddr(generateHWAddr());
|
||||
req->setHWAddr(generateHWAddr(55));
|
||||
|
||||
// Start multi-threading.
|
||||
EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
ClientHandler client_handler;
|
||||
|
||||
// Try to lock it with the discover.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
|
||||
// Get a second client handler.
|
||||
ClientHandler client_handler2;
|
||||
|
||||
// Try to lock it with a request.
|
||||
EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
|
||||
|
||||
// Should return true (duplicate without continuation).
|
||||
EXPECT_TRUE(duplicate);
|
||||
} catch (const std::exception& ex) {
|
||||
ADD_FAILURE() << "unexpected exception: " << ex.what();
|
||||
}
|
||||
|
||||
// Give the second continuation a chance even there is none...
|
||||
waitForThreads();
|
||||
|
||||
// Force multi-threading to stop;
|
||||
MultiThreadingCriticalSection cs;
|
||||
|
||||
checkStat(true);
|
||||
}
|
||||
|
||||
// Verifies behavior with two queries for the same client and multi-threading.
|
||||
// Continuations are required for serialization. By hardware address version.
|
||||
TEST_F(ClientHandleTest, serializeNoContByHWAddr) {
|
||||
// Get two queries.
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
|
||||
dis->addOption(generateClientId());
|
||||
req->addOption(generateClientId(111));
|
||||
HWAddrPtr hwaddr = generateHWAddr();
|
||||
// Same hardware address: same client.
|
||||
dis->setHWAddr(hwaddr);
|
||||
req->setHWAddr(hwaddr);
|
||||
|
||||
// Start multi-threading.
|
||||
EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
ClientHandler client_handler;
|
||||
|
||||
// Try to lock it with the discover.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
|
||||
// Get a second client handler.
|
||||
ClientHandler client_handler2;
|
||||
|
||||
// Try to lock it with a request.
|
||||
EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
|
||||
|
||||
// Should return true (duplicate without continuation).
|
||||
EXPECT_TRUE(duplicate);
|
||||
} catch (const std::exception& ex) {
|
||||
ADD_FAILURE() << "unexpected exception: " << ex.what();
|
||||
}
|
||||
|
||||
// Give the second continuation a chance even there is none...
|
||||
waitForThreads();
|
||||
|
||||
// Force multi-threading to stop;
|
||||
MultiThreadingCriticalSection cs;
|
||||
|
||||
checkStat(true);
|
||||
}
|
||||
|
||||
// Verifies behavior with three queries for the same client and
|
||||
// multi-threading: currently we accept only two queries,
|
||||
// a third one replaces second so we get the first (oldest) query and
|
||||
// the last (newest) query when the client is busy.
|
||||
// By client id option version.
|
||||
TEST_F(ClientHandleTest, serializeThreeQueriesById) {
|
||||
// Get two queries.
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
|
||||
Pkt4Ptr rel(new Pkt4(DHCPRELEASE, 3456));
|
||||
OptionPtr client_id = generateClientId();
|
||||
// Same client ID: same client.
|
||||
dis->addOption(client_id);
|
||||
req->addOption(client_id);
|
||||
rel->addOption(client_id);
|
||||
dis->setHWAddr(generateHWAddr());
|
||||
req->setHWAddr(generateHWAddr(55));
|
||||
rel->setHWAddr(generateHWAddr(66));
|
||||
|
||||
// Start multi-threading.
|
||||
EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
ClientHandler client_handler;
|
||||
|
||||
// Create a continuation.
|
||||
ClientHandler::ContinuationPtr cont1 =
|
||||
ClientHandler::makeContinuation(std::bind(&ClientHandleTest::setCalled1, this));
|
||||
|
||||
// Try to lock it with the discover.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis, cont1));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
|
||||
// Get a second client handler.
|
||||
ClientHandler client_handler2;
|
||||
|
||||
// Create a continuation.
|
||||
ClientHandler::ContinuationPtr cont2 =
|
||||
ClientHandler::makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
|
||||
|
||||
// Try to lock it with a request.
|
||||
EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req, cont2));
|
||||
|
||||
// Should return false (multi-threading enforces serialization).
|
||||
EXPECT_FALSE(duplicate);
|
||||
|
||||
// Get a third client handler.
|
||||
ClientHandler client_handler3;
|
||||
|
||||
// Create a continuation.
|
||||
ClientHandler::ContinuationPtr cont3 =
|
||||
ClientHandler::makeContinuation(std::bind(&ClientHandleTest::setCalled3, this));
|
||||
|
||||
// Try to lock it with a release.
|
||||
EXPECT_NO_THROW(duplicate = client_handler3.tryLock(rel, cont3));
|
||||
|
||||
// Should return false (multi-threading enforces serialization).
|
||||
EXPECT_FALSE(duplicate);
|
||||
} catch (const std::exception& ex) {
|
||||
ADD_FAILURE() << "unexpected exception: " << ex.what();
|
||||
}
|
||||
|
||||
// Give the second continuation a chance.
|
||||
waitForThreads();
|
||||
|
||||
// Force multi-threading to stop;
|
||||
MultiThreadingCriticalSection cs;
|
||||
|
||||
checkStat(true);
|
||||
EXPECT_FALSE(called1_);
|
||||
EXPECT_FALSE(called2_);
|
||||
EXPECT_TRUE(called3_);
|
||||
}
|
||||
|
||||
// Verifies behavior with three queries for the same client and
|
||||
// multi-threading: currently we accept only two queries,
|
||||
// a third one replaces second so we get the first (oldest) query and
|
||||
// the last (newest) query when the client is busy.
|
||||
// By hardware address version.
|
||||
TEST_F(ClientHandleTest, serializeThreeQueriesHWAddr) {
|
||||
// Get two queries.
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
|
||||
Pkt4Ptr rel(new Pkt4(DHCPRELEASE, 3456));
|
||||
dis->addOption(generateClientId());
|
||||
req->addOption(generateClientId(111));
|
||||
rel->addOption(generateClientId(99));
|
||||
HWAddrPtr hwaddr = generateHWAddr();
|
||||
// Same hardware address: same client.
|
||||
dis->setHWAddr(hwaddr);
|
||||
req->setHWAddr(hwaddr);
|
||||
rel->setHWAddr(hwaddr);
|
||||
|
||||
// Start multi-threading.
|
||||
EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
ClientHandler client_handler;
|
||||
|
||||
// Create a continuation.
|
||||
ClientHandler::ContinuationPtr cont1 =
|
||||
ClientHandler::makeContinuation(std::bind(&ClientHandleTest::setCalled1, this));
|
||||
|
||||
// Try to lock it with the discover.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis, cont1));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
|
||||
// Get a second client handler.
|
||||
ClientHandler client_handler2;
|
||||
|
||||
// Create a continuation.
|
||||
ClientHandler::ContinuationPtr cont2 =
|
||||
ClientHandler::makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
|
||||
|
||||
// Try to lock it with a request.
|
||||
EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req, cont2));
|
||||
|
||||
// Should return false (multi-threading enforces serialization).
|
||||
EXPECT_FALSE(duplicate);
|
||||
|
||||
// Get a third client handler.
|
||||
ClientHandler client_handler3;
|
||||
|
||||
// Create a continuation.
|
||||
ClientHandler::ContinuationPtr cont3 =
|
||||
ClientHandler::makeContinuation(std::bind(&ClientHandleTest::setCalled3, this));
|
||||
|
||||
// Try to lock it with a release.
|
||||
EXPECT_NO_THROW(duplicate = client_handler3.tryLock(rel, cont3));
|
||||
|
||||
// Should return false (multi-threading enforces serialization).
|
||||
EXPECT_FALSE(duplicate);
|
||||
} catch (const std::exception& ex) {
|
||||
ADD_FAILURE() << "unexpected exception: " << ex.what();
|
||||
}
|
||||
|
||||
// Give the second continuation a chance.
|
||||
waitForThreads();
|
||||
|
||||
// Force multi-threading to stop;
|
||||
MultiThreadingCriticalSection cs;
|
||||
|
||||
checkStat(true);
|
||||
EXPECT_FALSE(called1_);
|
||||
EXPECT_FALSE(called2_);
|
||||
EXPECT_TRUE(called3_);
|
||||
}
|
||||
|
||||
// Verifies behavior with three queries for the same client and
|
||||
// multi-threading: currently we accept only two queries,
|
||||
// a third one replaces second so we get the first (oldest) query and
|
||||
// the last (newest) query when the client is busy.
|
||||
// Mixed version (hardware address then client id option duplicates).
|
||||
// Note the system is transitive because further races are detected
|
||||
// when serialized packet processing is performed.
|
||||
TEST_F(ClientHandleTest, serializeThreeQueriesMixed) {
|
||||
// Get two queries.
|
||||
Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
|
||||
Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
|
||||
Pkt4Ptr rel(new Pkt4(DHCPRELEASE, 3456));
|
||||
HWAddrPtr hwaddr = generateHWAddr();
|
||||
// Same hardware address: same client for discover and request.
|
||||
dis->setHWAddr(hwaddr);
|
||||
req->setHWAddr(hwaddr);
|
||||
rel->setHWAddr(generateHWAddr(55));
|
||||
OptionPtr client_id = generateClientId();
|
||||
// Same client ID: same client for discover and release.
|
||||
dis->addOption(client_id);
|
||||
req->addOption(generateClientId(111));
|
||||
rel->addOption(client_id);
|
||||
|
||||
// Start multi-threading.
|
||||
EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
|
||||
|
||||
try {
|
||||
// Get a client handler.
|
||||
ClientHandler client_handler;
|
||||
|
||||
// Create a continuation.
|
||||
ClientHandler::ContinuationPtr cont1 =
|
||||
ClientHandler::makeContinuation(std::bind(&ClientHandleTest::setCalled1, this));
|
||||
|
||||
// Try to lock it with the discover.
|
||||
bool duplicate = false;
|
||||
EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis, cont1));
|
||||
|
||||
// Should return false (no duplicate).
|
||||
EXPECT_FALSE(duplicate);
|
||||
|
||||
// Get a second client handler.
|
||||
ClientHandler client_handler2;
|
||||
|
||||
// Create a continuation.
|
||||
ClientHandler::ContinuationPtr cont2 =
|
||||
ClientHandler::makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
|
||||
|
||||
// Try to lock it with a request.
|
||||
EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req, cont2));
|
||||
|
||||
// Should return false (multi-threading enforces serialization).
|
||||
EXPECT_FALSE(duplicate);
|
||||
|
||||
// Get a third client handler.
|
||||
ClientHandler client_handler3;
|
||||
|
||||
// Create a continuation.
|
||||
ClientHandler::ContinuationPtr cont3 =
|
||||
ClientHandler::makeContinuation(std::bind(&ClientHandleTest::setCalled3, this));
|
||||
|
||||
// Try to lock it with a release.
|
||||
EXPECT_NO_THROW(duplicate = client_handler3.tryLock(rel, cont3));
|
||||
|
||||
// Should return false (multi-threading enforces serialization).
|
||||
EXPECT_FALSE(duplicate);
|
||||
} catch (const std::exception& ex) {
|
||||
ADD_FAILURE() << "unexpected exception: " << ex.what();
|
||||
}
|
||||
|
||||
// Give the second continuation a chance.
|
||||
waitForThreads();
|
||||
|
||||
// Force multi-threading to stop;
|
||||
MultiThreadingCriticalSection cs;
|
||||
|
||||
checkStat(true);
|
||||
EXPECT_FALSE(called1_);
|
||||
EXPECT_FALSE(called2_);
|
||||
EXPECT_TRUE(called3_);
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
@@ -98,7 +98,6 @@ public:
|
||||
|
||||
/// @brief The called flag number 3.
|
||||
bool called3_;
|
||||
|
||||
};
|
||||
|
||||
// Verifies behavior with empty block.
|
||||
|
Reference in New Issue
Block a user