}
if (!duid && !hwaddr) {
// Can't do something useful: cross fingers.
- return (false);
+ return (true);
}
ClientPtr holder_id;
}
if (!holder_id) {
if (!hwaddr) {
- return (false);
+ return (true);
}
// Try to acquire the by-hw-addr lock and return the holder
// when it failed.
if (!holder_hw) {
locked_hwaddr_ = hwaddr;
lockByHWAddr();
- return (false);
+ return (true);
}
}
stats::StatsMgr::instance().addValue("pkt4-receive-drop",
static_cast<int64_t>(1));
}
- return (true);
+ return (false);
}
} // namespace dhcp
/// @brief Tries to acquires a client.
///
/// Lookup the client:
- /// - if not found insert the client in the clients map and return false
- /// - if found and has a continuation put the continuation in the holder
+ /// - if not found insert the client in the clients map and return true
+ /// - if found, if has a continuation put it in the holder,
/// and return false
- /// - if found and has no continuation return true
///
/// @param query The query from the client.
/// @param cont The continuation in the case the client was held.
- /// @return false if the client was acquired, true if there is already
+ /// @return true if the client was acquired, false if there is already
/// a query from the same client.
bool tryLock(Pkt4Ptr query, ContinuationPtr cont = ContinuationPtr());
ContinuationPtr cont =
makeContinuation(std::bind(&Dhcpv4Srv::processDhcp4QueryAndSendResponse,
this, query, rsp, allow_packet_park));
- if (client_handler.tryLock(query, cont)) {
+ if (!client_handler.tryLock(query, cont)) {
return;
}
}
// Try to lock it.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
// Try to lock it with the discover.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
ClientHandler client_handler2;
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
// Should return true (race with the duplicate).
EXPECT_TRUE(duplicate);
// Try to lock it with the discover.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
ClientHandler client_handler2;
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
// Should return true (race with the duplicate).
EXPECT_TRUE(duplicate);
// Try to lock it with the discover.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
ClientHandler client_handler2;
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
// Should return true (race with the duplicate).
EXPECT_TRUE(duplicate);
// Try to lock it with the discover.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
// Try to lock it with a request.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
// Try to lock it with the discover.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
ClientHandler client_handler2;
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
// Try to lock it with the discover.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
ClientHandler client_handler2;
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
// Try to lock it.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
// Try to lock it.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
// Try to lock it with the discover.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis, cont1));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis, cont1));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req, cont2));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req, cont2));
- // Should return false (multi-threading enforces serialization).
- EXPECT_FALSE(duplicate);
+ // Should return true (race with the duplicate).
+ EXPECT_TRUE(duplicate);
} catch (const std::exception& ex) {
ADD_FAILURE() << "unexpected exception: " << ex.what();
}
// Try to lock it with the discover.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis, cont1));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis, cont1));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req, cont2));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req, cont2));
- // Should return false (multi-threading enforces serialization).
- EXPECT_FALSE(duplicate);
+ // Should return true (race with the duplicate).
+ EXPECT_TRUE(duplicate);
} catch (const std::exception& ex) {
ADD_FAILURE() << "unexpected exception: " << ex.what();
}
// Try to lock it with the discover.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
ClientHandler client_handler2;
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
- // Should return true (duplicate without continuation).
+ // Should return true (race with the duplicate).
EXPECT_TRUE(duplicate);
} catch (const std::exception& ex) {
ADD_FAILURE() << "unexpected exception: " << ex.what();
// Try to lock it with the discover.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
ClientHandler client_handler2;
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
- // Should return true (duplicate without continuation).
+ // Should return true (race with the duplicate).
EXPECT_TRUE(duplicate);
} catch (const std::exception& ex) {
ADD_FAILURE() << "unexpected exception: " << ex.what();
// Try to lock it with the discover.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis, cont1));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis, cont1));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req, cont2));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req, cont2));
- // Should return false (multi-threading enforces serialization).
- EXPECT_FALSE(duplicate);
+ // Should return true (race with the duplicate).
+ EXPECT_TRUE(duplicate);
// Get a third client handler.
ClientHandler client_handler3;
makeContinuation(std::bind(&ClientHandleTest::setCalled3, this));
// Try to lock it with a release.
- EXPECT_NO_THROW(duplicate = client_handler3.tryLock(rel, cont3));
+ EXPECT_NO_THROW(duplicate = !client_handler3.tryLock(rel, cont3));
- // Should return false (multi-threading enforces serialization).
- EXPECT_FALSE(duplicate);
+ // Should return true (race with the duplicate).
+ EXPECT_TRUE(duplicate);
} catch (const std::exception& ex) {
ADD_FAILURE() << "unexpected exception: " << ex.what();
}
// Try to lock it with the discover.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis, cont1));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis, cont1));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req, cont2));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req, cont2));
- // Should return false (multi-threading enforces serialization).
- EXPECT_FALSE(duplicate);
+ // Should return true (race with the duplicate).
+ EXPECT_TRUE(duplicate);
// Get a third client handler.
ClientHandler client_handler3;
makeContinuation(std::bind(&ClientHandleTest::setCalled3, this));
// Try to lock it with a release.
- EXPECT_NO_THROW(duplicate = client_handler3.tryLock(rel, cont3));
+ EXPECT_NO_THROW(duplicate = !client_handler3.tryLock(rel, cont3));
- // Should return false (multi-threading enforces serialization).
- EXPECT_FALSE(duplicate);
+ // Should return true (race with the duplicate).
+ EXPECT_TRUE(duplicate);
} catch (const std::exception& ex) {
ADD_FAILURE() << "unexpected exception: " << ex.what();
}
// Try to lock it with the discover.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(dis, cont1));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis, cont1));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req, cont2));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req, cont2));
- // Should return false (multi-threading enforces serialization).
- EXPECT_FALSE(duplicate);
+ // Should return true (race with the duplicate).
+ EXPECT_TRUE(duplicate);
// Get a third client handler.
ClientHandler client_handler3;
makeContinuation(std::bind(&ClientHandleTest::setCalled3, this));
// Try to lock it with a release.
- EXPECT_NO_THROW(duplicate = client_handler3.tryLock(rel, cont3));
+ EXPECT_NO_THROW(duplicate = !client_handler3.tryLock(rel, cont3));
- // Should return false (multi-threading enforces serialization).
- EXPECT_FALSE(duplicate);
+ // Should return true (race with the duplicate).
+ EXPECT_TRUE(duplicate);
} catch (const std::exception& ex) {
ADD_FAILURE() << "unexpected exception: " << ex.what();
}
const DuidPtr& duid = query->getClientId();
if (!duid) {
// Can't do something useful: cross fingers.
- return (false);
+ return (true);
}
if (duid->getDuid().empty()) {
// A lot of code assumes this will never happen...
locked_ = duid;
client_.reset(new Client(query, duid));
lock();
- return (false);
+ return (true);
}
}
// This query can be a duplicate so put the continuation.
.arg(holder->thread_);
stats::StatsMgr::instance().addValue("pkt6-receive-drop",
static_cast<int64_t>(1));
- return (true);
+ return (false);
}
} // namespace dhcp
/// @brief Tries to acquires a client.
///
/// Lookup the client:
- /// - if not found insert the client in the clients map and return false
- /// - if found and has a continuation put the continuation in the holder
+ /// - if not found insert the client in the clients map and return true
+ /// - if found, if has a continuation put it in the holder,
/// and return false
- /// - if found and has no continuation return true
///
/// @param query The query from the client.
/// @param cont The continuation in the case the client was held.
- /// @return false if the client was acquired, true if there is already
+ /// @return true if the client was acquired, false if there is already
/// a query from the same client.
bool tryLock(Pkt6Ptr query, ContinuationPtr cont = ContinuationPtr());
ContinuationPtr cont =
makeContinuation(std::bind(&Dhcpv6Srv::processDhcp6QueryAndSendResponse,
this, query, rsp));
- drop = client_handler.tryLock(query, cont);
+ drop = !client_handler.tryLock(query, cont);
}
// Stop here if ClientHandler tryLock decided the packet is a duplicate.
// Try to lock it.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(sol));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
// Try to lock it with the solicit.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(sol));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
ClientHandler client_handler2;
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
// Should return true (race with the duplicate).
EXPECT_TRUE(duplicate);
// Try to lock it with the solicit.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(sol));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
// Try to lock it with a request.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
// Try to lock it with the solicit.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(sol));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
ClientHandler client_handler2;
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
// Try to lock it with the solicit.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(sol));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
ClientHandler client_handler2;
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
// Try to lock it.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(sol));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
// Try to lock it with the solicit.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol, cont1));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(sol, cont1));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req, cont2));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req, cont2));
- // Should return false (multi-threading enforces serialization).
- EXPECT_FALSE(duplicate);
+ // Should return true (race with the duplicate).
+ EXPECT_TRUE(duplicate);
} catch (const std::exception& ex) {
ADD_FAILURE() << "unexpected exception: " << ex.what();
}
// Try to lock it with the solicit.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(sol));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
ClientHandler client_handler2;
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
- // Should return true (duplicate without continuation).
+ // Should return true (race with the duplicate).
EXPECT_TRUE(duplicate);
} catch (const std::exception& ex) {
ADD_FAILURE() << "unexpected exception: " << ex.what();
// Try to lock it with the solicit.
bool duplicate = false;
- EXPECT_NO_THROW(duplicate = client_handler.tryLock(sol, cont1));
+ EXPECT_NO_THROW(duplicate = !client_handler.tryLock(sol, cont1));
// Should return false (no duplicate).
EXPECT_FALSE(duplicate);
makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
// Try to lock it with a request.
- EXPECT_NO_THROW(duplicate = client_handler2.tryLock(req, cont2));
+ EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req, cont2));
- // Should return false (multi-threading enforces serialization).
- EXPECT_FALSE(duplicate);
+ // Should return true (race with the duplicate).
+ EXPECT_TRUE(duplicate);
// Get a third client handler.
ClientHandler client_handler3;
makeContinuation(std::bind(&ClientHandleTest::setCalled3, this));
// Try to lock it with a renew.
- EXPECT_NO_THROW(duplicate = client_handler3.tryLock(ren, cont3));
+ EXPECT_NO_THROW(duplicate = !client_handler3.tryLock(ren, cont3));
- // Should return false (multi-threading enforces serialization).
- EXPECT_FALSE(duplicate);
+ // Should return true (race with the duplicate).
+ EXPECT_TRUE(duplicate);
} catch (const std::exception& ex) {
ADD_FAILURE() << "unexpected exception: " << ex.what();
}
--- /dev/null
+// Copyright (C) 2020 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/.
+
+#include <config.h>
+
+#include <dhcpsrv/resource_handler.h>
+#include <exceptions/exceptions.h>
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+mutex ResourceHandler::mutex_;
+
+ResourceHandler::ResourceContainer ResourceHandler::resources_;
+
+ResourceHandler::ResourceHandler() : owned_() {
+}
+
+ResourceHandler::~ResourceHandler() {
+ for (auto res : owned_) {
+ lock_guard<mutex> lock_(mutex_);
+ unLockInternal(res->type_, res->addr_);
+ }
+ owned_.clear();
+}
+
+ResourceHandler::ResourcePtr
+ResourceHandler::lookup(Lease::Type type, const asiolink::IOAddress& addr) {
+ auto key = boost::make_tuple(type, addr.toBytes());
+ auto it = resources_.find(key);
+ if (it == resources_.end()) {
+ return (ResourcePtr());
+ }
+ return (*it);
+}
+
+void
+ResourceHandler::lock(Lease::Type type, const asiolink::IOAddress& addr) {
+ ResourcePtr res(new Resource(type, addr));
+ // Assume insert will never fail so not checking its result.
+ resources_.insert(res);
+ owned_.insert(res);
+}
+
+void
+ResourceHandler::unLockInternal(Lease::Type type,
+ const asiolink::IOAddress& addr) {
+ auto key = boost::make_tuple(type, addr.toBytes());
+ auto it = resources_.find(key);
+ if (it == resources_.end()) {
+ return;
+ }
+ resources_.erase(it);
+}
+
+bool
+ResourceHandler::tryLock(Lease::Type type, const asiolink::IOAddress& addr) {
+ ResourcePtr holder;
+ // Try to acquire the lock and return the holder when it failed.
+ lock_guard<mutex> lock_(mutex_);
+ holder = lookup(type, addr);
+ if (holder) {
+ return (false);
+ }
+ lock(type, addr);
+ return (true);
+}
+
+bool
+ResourceHandler::isLocked(Lease::Type type, const asiolink::IOAddress& addr) {
+ auto key = boost::make_tuple(type, addr.toBytes());
+ auto it = owned_.find(key);
+ return (it != owned_.end());
+}
+
+void
+ResourceHandler::unLock(Lease::Type type, const asiolink::IOAddress& addr) {
+ auto key = boost::make_tuple(type, addr.toBytes());
+ auto it = owned_.find(key);
+ if (it == owned_.end()) {
+ isc_throw(InvalidParameter,
+ "does not owne " << Lease::typeToText(type) << " "
+ << addr.toText());
+ }
+ {
+ lock_guard<mutex> lock_(mutex_);
+ unLockInternal(type, addr);
+ }
+ owned_.erase(it);
+}
+
+} // namespace dhcp
+} // namespace isc
--- /dev/null
+// Copyright (C) 2020 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 RESOURCE_HANDLER_H
+#define RESOURCE_HANDLER_H
+
+#include <asiolink/io_address.h>
+#include <dhcpsrv/lease.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/composite_key.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/mem_fun.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <mutex>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Resource race avoidance RAII handler.
+class ResourceHandler : public boost::noncopyable {
+public:
+
+ /// @brief Constructor.
+ ResourceHandler();
+
+ /// @brief Destructor.
+ ///
+ /// Releases owned resources.
+ virtual ~ResourceHandler();
+
+ /// @brief Tries to acquires a resource.
+ ///
+ /// Lookup the resource, if not found insert the resource
+ /// in the resource container and return true, if found return false
+ ///
+ /// @param type Type of the resource, member of @c Lease::Type enum.
+ /// @param addr The address or prefix aka the resource.
+ /// @return true if the resource was acquired, false if the resource is
+ /// busy i.e. owned by a handler.
+ bool tryLock(Lease::Type type, const asiolink::IOAddress& addr);
+
+ /// @brief Checks if a resource is owned by this handler.
+ ///
+ /// @param type Type of the resource, member of @c Lease::Type enum.
+ /// @param addr The address or prefix aka the resource.
+ /// @return true if this handler owns the resource, false otherwise.
+ bool isLocked(Lease::Type type, const asiolink::IOAddress& addr);
+
+ /// @brief Releases a resource.
+ ///
+ /// Remove the resource from the resource container.
+ ///
+ /// @param type Type of the resource, member of @c Lease::Type enum.
+ /// @param addr The address or prefix aka the resource.
+ /// @throw when we do not own the resource.
+ void unLock(Lease::Type type, const asiolink::IOAddress& addr);
+
+private:
+
+ /// Type definitions.
+ //@{
+
+ /// @brief Structure representing a resource.
+ struct Resource {
+
+ /// @brief Constructor.
+ ///
+ /// @param addr The address or prefix aka the resource..
+ Resource(Lease::Type type, const asiolink::IOAddress& addr)
+ : type_(type), addr_(addr) {
+ }
+
+ /// @brief The type.
+ Lease::Type type_;
+
+ /// @brief The resource.
+ asiolink::IOAddress addr_;
+
+ /// @brief The key extractor.
+ std::vector<uint8_t> toBytes() const {
+ return (addr_.toBytes());
+ }
+ };
+
+ /// @brief The type of shared pointers to resources.
+ typedef boost::shared_ptr<Resource> ResourcePtr;
+
+ /// @brief The type of the resource container.
+ typedef boost::multi_index_container<
+
+ // This container stores pointers to resource objects.
+ ResourcePtr,
+
+ // Start specification of indexes here.
+ boost::multi_index::indexed_by<
+
+ // First index is used to search by type and address.
+ boost::multi_index::hashed_unique<
+ boost::multi_index::composite_key<
+ Resource,
+ // Lease type.
+ boost::multi_index::member<
+ Resource, Lease::Type, &Resource::type_
+ >,
+ // Address bytes.
+ boost::multi_index::const_mem_fun<
+ Resource, std::vector<uint8_t>, &Resource::toBytes
+ >
+ >
+ >
+ >
+ > ResourceContainer;
+
+ //@}
+
+ /// Class members.
+ //@{
+
+ /// @brief The resource container.
+ static ResourceContainer resources_;
+
+ /// @brief Mutex to protect the resource container.
+ static std::mutex mutex_;
+
+ /// @brief Lookup a resource.
+ ///
+ /// The mutex must be held by the caller.
+ ///
+ /// @param type Type of the resource, member of @c Lease::Type enum.
+ /// @param addr The address or prefix aka the resource.
+ /// @return The busy resource or null.
+ static ResourcePtr
+ lookup(Lease::Type type, const asiolink::IOAddress& addr);
+
+ //@}
+
+ /// Instance members.
+ //@{
+
+ /// @brief Acquire a resource.
+ ///
+ /// The mutex must be held by the caller.
+ ///
+ /// @param type Type of the resource, member of @c Lease::Type enum.
+ /// @param addr The address or prefix aka the resource.
+ void lock(Lease::Type type, const asiolink::IOAddress& addr);
+
+ /// @brief Release a resource.
+ ///
+ /// The mutex must be held by the caller.
+ ///
+ /// Remove the resource from the resource container.
+ ///
+ /// @param type Type of the resource, member of @c Lease::Type enum.
+ /// @param addr The address or prefix aka the resource.
+ void unLockInternal(Lease::Type type, const asiolink::IOAddress& addr);
+
+ /// @brief List of resources this handler owns.
+ ResourceContainer owned_;
+
+ //@}
+};
+
+/// @brief Resource race avoidance RAII handler for DHCPv4.
+class ResourceHandler4 : public ResourceHandler {
+public:
+
+ /// @brief Destructor.
+ ///
+ /// Releases owned resources.
+ virtual ~ResourceHandler4() { }
+
+ /// @brief Tries to acquires a resource.
+ ///
+ /// Lookup the resource, if not found insert the resource
+ /// in the resource container and return true, if found return false
+ ///
+ /// @param addr The address aka the resource.
+ /// @return true if the resource was acquired, false if the resource is
+ /// busy i.e. owned by a handler.
+ bool tryLock4(const asiolink::IOAddress& addr) {
+ return (tryLock(Lease::TYPE_V4, addr));
+ }
+
+ /// @brief Checks if a resource is owned by this handler.
+ ///
+ /// @param addr The address aka the resource.
+ /// @return true if this handler owns the resource, false otherwise.
+ bool isLocked4(const asiolink::IOAddress& addr) {
+ return (isLocked(Lease::TYPE_V4, addr));
+ }
+
+ /// @brief Releases a resource.
+ ///
+ /// Remove the resource from the resource container.
+ ///
+ /// @param addr The address aka the resource.
+ /// @throw when we do not own the resource.
+ void unLock4(const asiolink::IOAddress& addr) {
+ unLock(Lease::TYPE_V4, addr);
+ }
+};
+
+} // namespace isc
+} // namespace dhcp
+
+#endif // RESOURCE_HANDLER_H
--- /dev/null
+// Copyright (C) 2020 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/.
+
+#include <config.h>
+#include <dhcpsrv/resource_handler.h>
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+
+// Verifies behavior with empty block.
+TEST(ResourceHandleTest, empty) {
+ try {
+ // Get a resource handler.
+ ResourceHandler resource_handler;
+ } catch (const std::exception& ex) {
+ ADD_FAILURE() << "unexpected exception: " << ex.what();
+ }
+}
+
+// Verifies behavior with empty block (v4).
+TEST(ResourceHandleTest, empty4) {
+ try {
+ // Get a resource handler.
+ ResourceHandler4 resource_handler;
+ } catch (const std::exception& ex) {
+ ADD_FAILURE() << "unexpected exception: " << ex.what();
+ }
+}
+
+} // end of anonymous namespace