fw: refactor Strategy registry
Strategy registry is moved into Strategy class.
Strategy instantiation logic is implemented in Strategy::create.
These are in preparation of supporting Strategy parameters.
refs #3868
Change-Id: If36a08ad25a00a7008a5eccc8cfe4f6c63638676
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index 7e0df65..7482cf4 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -25,8 +25,9 @@
#include "forwarder.hpp"
#include "algorithm.hpp"
-#include "core/logger.hpp"
+#include "best-route-strategy2.hpp"
#include "strategy.hpp"
+#include "core/logger.hpp"
#include "table/cleanup.hpp"
#include <ndn-cxx/lp/tags.hpp>
@@ -34,15 +35,19 @@
NFD_LOG_INIT("Forwarder");
+static Name
+getDefaultStrategyName()
+{
+ return fw::BestRouteStrategy2::STRATEGY_NAME;
+}
+
Forwarder::Forwarder()
: m_unsolicitedDataPolicy(new fw::DefaultUnsolicitedDataPolicy())
, m_fib(m_nameTree)
, m_pit(m_nameTree)
, m_measurements(m_nameTree)
- , m_strategyChoice(m_nameTree, fw::makeDefaultStrategy(*this))
+ , m_strategyChoice(*this)
{
- fw::installStrategies(*this);
-
m_faceTable.afterAdd.connect([this] (Face& face) {
face.afterReceiveInterest.connect(
[this, &face] (const Interest& interest) {
@@ -61,6 +66,9 @@
m_faceTable.beforeRemove.connect([this] (Face& face) {
cleanupOnFaceRemoval(m_nameTree, m_fib, m_pit, face);
});
+
+ m_strategyChoice.setDefaultStrategy(getDefaultStrategyName());
+ m_strategyChoice.installFromRegistry();
}
Forwarder::~Forwarder() = default;
diff --git a/daemon/fw/strategy-registry.cpp b/daemon/fw/strategy-registry.cpp
deleted file mode 100644
index 49a778f..0000000
--- a/daemon/fw/strategy-registry.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2016, Regents of the University of California,
- * Arizona Board of Regents,
- * Colorado State University,
- * University Pierre & Marie Curie, Sorbonne University,
- * Washington University in St. Louis,
- * Beijing Institute of Technology,
- * The University of Memphis.
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon).
- * See AUTHORS.md for complete list of NFD authors and contributors.
- *
- * NFD is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "strategy-registry.hpp"
-#include "best-route-strategy2.hpp"
-
-namespace nfd {
-namespace fw {
-
-unique_ptr<Strategy>
-makeDefaultStrategy(Forwarder& forwarder)
-{
- return make_unique<BestRouteStrategy2>(ref(forwarder));
-}
-
-static std::map<Name, StrategyCreateFunc>&
-getStrategyFactories()
-{
- static std::map<Name, StrategyCreateFunc> strategyFactories;
- return strategyFactories;
-}
-
-void
-registerStrategyImpl(const Name& strategyName, const StrategyCreateFunc& createFunc)
-{
- getStrategyFactories().insert({strategyName, createFunc});
-}
-
-void
-installStrategies(Forwarder& forwarder)
-{
- StrategyChoice& sc = forwarder.getStrategyChoice();
- for (const auto& pair : getStrategyFactories()) {
- if (!sc.hasStrategy(pair.first, true)) {
- sc.install(pair.second(forwarder));
- }
- }
-}
-
-} // namespace fw
-} // namespace nfd
diff --git a/daemon/fw/strategy-registry.hpp b/daemon/fw/strategy-registry.hpp
deleted file mode 100644
index 83e9f73..0000000
--- a/daemon/fw/strategy-registry.hpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2016, Regents of the University of California,
- * Arizona Board of Regents,
- * Colorado State University,
- * University Pierre & Marie Curie, Sorbonne University,
- * Washington University in St. Louis,
- * Beijing Institute of Technology,
- * The University of Memphis.
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon).
- * See AUTHORS.md for complete list of NFD authors and contributors.
- *
- * NFD is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef NFD_DAEMON_FW_STRATEGY_REGISTRY_HPP
-#define NFD_DAEMON_FW_STRATEGY_REGISTRY_HPP
-
-#include "core/common.hpp"
-
-namespace nfd {
-
-class Forwarder;
-
-namespace fw {
-
-class Strategy;
-
-unique_ptr<Strategy>
-makeDefaultStrategy(Forwarder& forwarder);
-
-void
-installStrategies(Forwarder& forwarder);
-
-
-typedef std::function<unique_ptr<Strategy>(Forwarder&)> StrategyCreateFunc;
-
-void
-registerStrategyImpl(const Name& strategyName, const StrategyCreateFunc& createFunc);
-
-/** \brief registers a strategy to be installed later
- */
-template<typename S>
-void
-registerStrategy()
-{
- registerStrategyImpl(S::STRATEGY_NAME,
- [] (Forwarder& forwarder) { return make_unique<S>(ref(forwarder)); });
-}
-
-/** \brief registers a built-in strategy
- *
- * This macro should appear once in .cpp of each built-in strategy.
- */
-#define NFD_REGISTER_STRATEGY(StrategyType) \
-static class NfdAuto ## StrategyType ## StrategyRegistrationClass \
-{ \
-public: \
- NfdAuto ## StrategyType ## StrategyRegistrationClass() \
- { \
- ::nfd::fw::registerStrategy<StrategyType>(); \
- } \
-} g_nfdAuto ## StrategyType ## StrategyRegistrationVariable
-
-} // namespace fw
-} // namespace nfd
-
-#endif // NFD_DAEMON_FW_STRATEGY_REGISTRY_HPP
diff --git a/daemon/fw/strategy.cpp b/daemon/fw/strategy.cpp
index 218ce44..ae8a17c 100644
--- a/daemon/fw/strategy.cpp
+++ b/daemon/fw/strategy.cpp
@@ -27,12 +27,70 @@
#include "forwarder.hpp"
#include "core/logger.hpp"
#include "core/random.hpp"
+#include <boost/range/adaptor/map.hpp>
+#include <boost/range/algorithm/copy.hpp>
namespace nfd {
namespace fw {
NFD_LOG_INIT("Strategy");
+Strategy::Registry&
+Strategy::getRegistry()
+{
+ static Registry registry;
+ return registry;
+}
+
+Strategy::Registry::const_iterator
+Strategy::find(const Name& strategyName)
+{
+ const Registry& registry = getRegistry();
+ Registry::const_iterator candidate = registry.end();
+ for (auto it = registry.lower_bound(strategyName);
+ it != registry.end() && strategyName.isPrefixOf(it->first); ++it) {
+ switch (it->first.size() - strategyName.size()) {
+ case 0: // exact match
+ return it;
+ case 1: // unversioned strategyName matches versioned strategy
+ candidate = it;
+ break;
+ }
+ }
+ return candidate;
+ ///\todo #3868 if exact version unavailable, choose closest higher version
+ ///\todo #3868 allow parameter component
+}
+
+bool
+Strategy::canCreate(const Name& strategyName)
+{
+ return Strategy::find(strategyName) != getRegistry().end();
+}
+
+unique_ptr<Strategy>
+Strategy::create(const Name& strategyName, Forwarder& forwarder)
+{
+ auto found = Strategy::find(strategyName);
+ if (found == getRegistry().end()) {
+ NFD_LOG_DEBUG("create " << strategyName << " not-found");
+ return nullptr;
+ }
+
+ NFD_LOG_DEBUG("create " << strategyName << " found=" << found->first);
+ ///\todo #3868 pass parameters to strategy constructor
+ return found->second(forwarder, found->first);
+}
+
+std::set<Name>
+Strategy::listRegistered()
+{
+ std::set<Name> strategyNames;
+ boost::copy(getRegistry() | boost::adaptors::map_keys,
+ std::inserter(strategyNames, strategyNames.end()));
+ return strategyNames;
+}
+
Strategy::Strategy(Forwarder& forwarder, const Name& name)
: afterAddFace(forwarder.getFaceTable().afterAdd)
, beforeRemoveFace(forwarder.getFaceTable().beforeRemove)
diff --git a/daemon/fw/strategy.hpp b/daemon/fw/strategy.hpp
index f4ef3b1..0bc8d9e 100644
--- a/daemon/fw/strategy.hpp
+++ b/daemon/fw/strategy.hpp
@@ -27,7 +27,6 @@
#define NFD_DAEMON_FW_STRATEGY_HPP
#include "forwarder.hpp"
-#include "strategy-registry.hpp"
#include "table/measurements-accessor.hpp"
namespace nfd {
@@ -35,9 +34,47 @@
/** \brief represents a forwarding strategy
*/
-class Strategy : public enable_shared_from_this<Strategy>, noncopyable
+class Strategy : noncopyable
{
-public:
+public: // registry
+ /** \brief register a strategy type
+ * \tparam S subclass of Strategy
+ * \param strategyName versioned strategy name
+ * \note It is permitted to register the same strategy type under multiple names,
+ * which is useful in tests and for creating aliases.
+ */
+ template<typename S>
+ static void
+ registerType(const Name& strategyName = S::STRATEGY_NAME)
+ {
+ Registry& registry = getRegistry();
+ BOOST_ASSERT(registry.count(strategyName) == 0);
+ registry[strategyName] = &make_unique<S, Forwarder&, const Name&>;
+ }
+
+ /** \return whether a strategy instance can be created from \p strategyName
+ * \param strategyName strategy name, may contain version
+ * \todo #3868 may contain parameters
+ * \note This function finds a strategy type using same rules as \p create ,
+ * but does not attempt to construct an instance.
+ */
+ static bool
+ canCreate(const Name& strategyName);
+
+ /** \return a strategy instance created from \p strategyName,
+ * \retval nullptr if !canCreate(strategyName)
+ * \todo #3868 throw std::invalid_argument strategy type constructor
+ * does not accept specified version or parameters
+ */
+ static unique_ptr<Strategy>
+ create(const Name& strategyName, Forwarder& forwarder);
+
+ /** \return registered versioned strategy names
+ */
+ static std::set<Name>
+ listRegistered();
+
+public: // constructor, destructor, strategy name
/** \brief construct a strategy instance
* \param forwarder a reference to the Forwarder, used to enable actions and accessors.
* Strategy subclasses should pass this reference,
@@ -205,7 +242,17 @@
signal::Signal<FaceTable, Face&>& afterAddFace;
signal::Signal<FaceTable, Face&>& beforeRemoveFace;
-private:
+private: // registry
+ typedef std::function<unique_ptr<Strategy>(Forwarder& forwarder, const Name& strategyName)> CreateFunc;
+ typedef std::map<Name, CreateFunc> Registry; // indexed by strategy name
+
+ static Registry&
+ getRegistry();
+
+ static Registry::const_iterator
+ find(const Name& strategyName);
+
+private: // instance fields
Name m_name;
/** \brief reference to the forwarder
@@ -220,4 +267,18 @@
} // namespace fw
} // namespace nfd
+/** \brief registers a strategy
+ *
+ * This macro should appear once in .cpp of each strategy.
+ */
+#define NFD_REGISTER_STRATEGY(S) \
+static class NfdAuto ## S ## StrategyRegistrationClass \
+{ \
+public: \
+ NfdAuto ## S ## StrategyRegistrationClass() \
+ { \
+ ::nfd::fw::Strategy::registerType<S>(); \
+ } \
+} g_nfdAuto ## S ## StrategyRegistrationVariable
+
#endif // NFD_DAEMON_FW_STRATEGY_HPP
diff --git a/daemon/table/strategy-choice.cpp b/daemon/table/strategy-choice.cpp
index c3d02e3..81de636 100644
--- a/daemon/table/strategy-choice.cpp
+++ b/daemon/table/strategy-choice.cpp
@@ -24,10 +24,10 @@
*/
#include "strategy-choice.hpp"
+#include "measurements-entry.hpp"
+#include "pit-entry.hpp"
#include "core/logger.hpp"
#include "fw/strategy.hpp"
-#include "pit-entry.hpp"
-#include "measurements-entry.hpp"
namespace nfd {
namespace strategy_choice {
@@ -42,11 +42,48 @@
return nte.getStrategyChoiceEntry() != nullptr;
}
-StrategyChoice::StrategyChoice(NameTree& nameTree, unique_ptr<Strategy> defaultStrategy)
- : m_nameTree(nameTree)
+StrategyChoice::StrategyChoice(Forwarder& forwarder)
+ : m_forwarder(forwarder)
+ , m_nameTree(m_forwarder.getNameTree())
, m_nItems(0)
{
- this->setDefaultStrategy(std::move(defaultStrategy));
+}
+
+void
+StrategyChoice::setDefaultStrategy(const Name& strategyName)
+{
+ unique_ptr<Strategy> strategy = Strategy::create(strategyName, m_forwarder);
+
+ bool isInstalled = false;
+ Strategy* instance = nullptr;
+ std::tie(isInstalled, instance) = this->install(std::move(strategy));
+ BOOST_ASSERT(isInstalled);
+
+ auto entry = make_unique<Entry>(Name());
+ entry->setStrategy(*instance);
+
+ // don't use .insert here, because it will invoke findEffectiveStrategy
+ // which expects an existing root entry
+ name_tree::Entry& nte = m_nameTree.lookup(Name());
+ nte.setStrategyChoiceEntry(std::move(entry));
+ ++m_nItems;
+ NFD_LOG_INFO("setDefaultStrategy " << instance->getName());
+}
+
+void
+StrategyChoice::installFromRegistry()
+{
+ /// \todo #3868
+ /// * Give every StrategyChoice entry its own Strategy instance.
+ /// Don't share Strategy instances.
+ /// * Create Strategy instance with Strategy::create.
+ /// * Eliminate install, installFromRegistry, getStrategy functions.
+
+ // This is temporary code until the above is done.
+
+ for (const Name& strategyName : Strategy::listRegistered()) {
+ this->install(Strategy::create(strategyName, m_forwarder));
+ }
}
bool
@@ -198,25 +235,6 @@
return this->findEffectiveStrategyImpl(measurementsEntry);
}
-void
-StrategyChoice::setDefaultStrategy(unique_ptr<Strategy> strategy)
-{
- bool isInstalled = false;
- Strategy* instance = nullptr;
- std::tie(isInstalled, instance) = this->install(std::move(strategy));
- BOOST_ASSERT(isInstalled);
-
- auto entry = make_unique<Entry>(Name());
- entry->setStrategy(*instance);
-
- // don't use .insert here, because it will invoke findEffectiveStrategy
- // which expects an existing root entry
- name_tree::Entry& nte = m_nameTree.lookup(Name());
- nte.setStrategyChoiceEntry(std::move(entry));
- ++m_nItems;
- NFD_LOG_INFO("setDefaultStrategy " << instance->getName());
-}
-
static inline void
clearStrategyInfo(const name_tree::Entry& nte)
{
diff --git a/daemon/table/strategy-choice.hpp b/daemon/table/strategy-choice.hpp
index 1038cfb..f2410e2 100644
--- a/daemon/table/strategy-choice.hpp
+++ b/daemon/table/strategy-choice.hpp
@@ -32,6 +32,9 @@
#include <boost/range/adaptor/transformed.hpp>
namespace nfd {
+
+class Forwarder;
+
namespace strategy_choice {
/** \brief represents the Strategy Choice table
@@ -48,7 +51,8 @@
class StrategyChoice : noncopyable
{
public:
- StrategyChoice(NameTree& nameTree, unique_ptr<fw::Strategy> defaultStrategy);
+ explicit
+ StrategyChoice(Forwarder& forwarder);
size_t
size() const
@@ -56,7 +60,18 @@
return m_nItems;
}
-public: // available Strategy types
+ /** \brief set the default strategy
+ *
+ * This must be called by forwarder constructor.
+ */
+ void
+ setDefaultStrategy(const Name& strategyName);
+
+ /** \brief install all strategies from registry
+ */
+ void
+ installFromRegistry();
+
/** \brief determines if a strategy is installed
* \param strategyName name of the strategy
* \param isExact true to require exact match, false to permit unversioned strategyName
@@ -152,9 +167,6 @@
getStrategy(const Name& strategyName) const;
void
- setDefaultStrategy(unique_ptr<fw::Strategy> strategy);
-
- void
changeStrategy(Entry& entry,
fw::Strategy& oldStrategy,
fw::Strategy& newStrategy);
@@ -169,6 +181,7 @@
getRange() const;
private:
+ Forwarder& m_forwarder;
NameTree& m_nameTree;
size_t m_nItems;
diff --git a/tests/daemon/fw/access-strategy.t.cpp b/tests/daemon/fw/access-strategy.t.cpp
index b2dfe50..c0a0240 100644
--- a/tests/daemon/fw/access-strategy.t.cpp
+++ b/tests/daemon/fw/access-strategy.t.cpp
@@ -50,6 +50,11 @@
BOOST_AUTO_TEST_SUITE(Fw)
BOOST_FIXTURE_TEST_SUITE(TestAccessStrategy, UnitTestTimeFixture)
+BOOST_AUTO_TEST_CASE(Registration)
+{
+ BOOST_CHECK_EQUAL(Strategy::listRegistered().count(AccessStrategy::STRATEGY_NAME), 1);
+}
+
class TwoLaptopsFixture : public UnitTestTimeFixture
{
protected:
diff --git a/tests/daemon/fw/asf-strategy.t.cpp b/tests/daemon/fw/asf-strategy.t.cpp
index dddacf1..c0aa337 100644
--- a/tests/daemon/fw/asf-strategy.t.cpp
+++ b/tests/daemon/fw/asf-strategy.t.cpp
@@ -38,6 +38,11 @@
BOOST_AUTO_TEST_SUITE(Fw)
BOOST_FIXTURE_TEST_SUITE(TestAsfStrategy, UnitTestTimeFixture)
+BOOST_AUTO_TEST_CASE(Registration)
+{
+ BOOST_CHECK_EQUAL(Strategy::listRegistered().count(AsfStrategy::STRATEGY_NAME), 1);
+}
+
class AsfGridFixture : public UnitTestTimeFixture
{
protected:
diff --git a/tests/daemon/fw/best-route-strategy2.t.cpp b/tests/daemon/fw/best-route-strategy2.t.cpp
index ec6e951..b8406b5 100644
--- a/tests/daemon/fw/best-route-strategy2.t.cpp
+++ b/tests/daemon/fw/best-route-strategy2.t.cpp
@@ -73,6 +73,11 @@
BOOST_FIXTURE_TEST_SUITE(TestBestRouteStrategy2, BestRouteStrategy2Fixture)
+BOOST_AUTO_TEST_CASE(Registration)
+{
+ BOOST_CHECK_EQUAL(Strategy::listRegistered().count(BestRouteStrategy2::STRATEGY_NAME), 1);
+}
+
BOOST_AUTO_TEST_CASE(Forward)
{
fib::Entry& fibEntry = *fib.insert(Name()).first;
diff --git a/tests/daemon/fw/multicast-strategy.t.cpp b/tests/daemon/fw/multicast-strategy.t.cpp
index 1b8afcb..a5255b5 100644
--- a/tests/daemon/fw/multicast-strategy.t.cpp
+++ b/tests/daemon/fw/multicast-strategy.t.cpp
@@ -38,6 +38,11 @@
BOOST_AUTO_TEST_SUITE(Fw)
BOOST_FIXTURE_TEST_SUITE(TestMulticastStrategy, BaseFixture)
+BOOST_AUTO_TEST_CASE(Registration)
+{
+ BOOST_CHECK_EQUAL(Strategy::listRegistered().count(MulticastStrategy::STRATEGY_NAME), 1);
+}
+
BOOST_AUTO_TEST_CASE(Forward2)
{
Forwarder forwarder;
diff --git a/tests/daemon/fw/ncc-strategy.t.cpp b/tests/daemon/fw/ncc-strategy.t.cpp
index cc32201..a792404 100644
--- a/tests/daemon/fw/ncc-strategy.t.cpp
+++ b/tests/daemon/fw/ncc-strategy.t.cpp
@@ -40,7 +40,10 @@
BOOST_AUTO_TEST_SUITE(Fw)
BOOST_FIXTURE_TEST_SUITE(TestNccStrategy, UnitTestTimeFixture)
-using fw::NccStrategy;
+BOOST_AUTO_TEST_CASE(Registration)
+{
+ BOOST_CHECK_EQUAL(Strategy::listRegistered().count(NccStrategy::STRATEGY_NAME), 1);
+}
// NccStrategy is fairly complex.
// The most important property is: