2
0
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:
Francis Dupont
2020-05-09 22:18:31 +02:00
parent fd50371a22
commit 3959850796
4 changed files with 619 additions and 54 deletions

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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

View File

@@ -98,7 +98,6 @@ public:
/// @brief The called flag number 3.
bool called3_;
};
// Verifies behavior with empty block.