fw: abstract Nack processing out of BestRouteStrategy2
refs #3176
Change-Id: Ib220269ff52acc47c65f854a610941afd862ad41
diff --git a/daemon/fw/best-route-strategy2.cpp b/daemon/fw/best-route-strategy2.cpp
index 974567a..1666e81 100644
--- a/daemon/fw/best-route-strategy2.cpp
+++ b/daemon/fw/best-route-strategy2.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2016, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -38,6 +38,7 @@
BestRouteStrategy2::BestRouteStrategy2(Forwarder& forwarder, const Name& name)
: Strategy(forwarder)
+ , ProcessNackTraits(this)
, m_retxSuppression(RETX_SUPPRESSION_INITIAL,
RetxSuppressionExponential::DEFAULT_MULTIPLIER,
RETX_SUPPRESSION_MAX)
@@ -183,69 +184,11 @@
}
}
-/** \return less severe NackReason between x and y
- *
- * lp::NackReason::NONE is treated as most severe
- */
-inline lp::NackReason
-compareLessSevere(lp::NackReason x, lp::NackReason y)
-{
- if (x == lp::NackReason::NONE) {
- return y;
- }
- if (y == lp::NackReason::NONE) {
- return x;
- }
- return static_cast<lp::NackReason>(std::min(static_cast<int>(x), static_cast<int>(y)));
-}
-
void
BestRouteStrategy2::afterReceiveNack(const Face& inFace, const lp::Nack& nack,
const shared_ptr<pit::Entry>& pitEntry)
{
- int nOutRecordsNotNacked = 0;
- Face* lastFaceNotNacked = nullptr;
- lp::NackReason leastSevereReason = lp::NackReason::NONE;
- for (const pit::OutRecord& outR : pitEntry->getOutRecords()) {
- const lp::NackHeader* inNack = outR.getIncomingNack();
- if (inNack == nullptr) {
- ++nOutRecordsNotNacked;
- lastFaceNotNacked = &outR.getFace();
- continue;
- }
-
- leastSevereReason = compareLessSevere(leastSevereReason, inNack->getReason());
- }
-
- lp::NackHeader outNack;
- outNack.setReason(leastSevereReason);
-
- if (nOutRecordsNotNacked == 1) {
- BOOST_ASSERT(lastFaceNotNacked != nullptr);
- pit::InRecordCollection::iterator inR = pitEntry->getInRecord(*lastFaceNotNacked);
- if (inR != pitEntry->in_end()) {
- // one out-record not Nacked, which is also a downstream
- NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
- " nack=" << nack.getReason() <<
- " nack-to(bidirectional)=" << lastFaceNotNacked->getId() <<
- " out-nack=" << outNack.getReason());
- this->sendNack(pitEntry, *lastFaceNotNacked, outNack);
- return;
- }
- }
-
- if (nOutRecordsNotNacked > 0) {
- NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
- " nack=" << nack.getReason() <<
- " waiting=" << nOutRecordsNotNacked);
- // continue waiting
- return;
- }
-
- NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
- " nack=" << nack.getReason() <<
- " nack-to=all out-nack=" << outNack.getReason());
- this->sendNacks(pitEntry, outNack);
+ this->processNack(inFace, nack, pitEntry);
}
} // namespace fw
diff --git a/daemon/fw/best-route-strategy2.hpp b/daemon/fw/best-route-strategy2.hpp
index 99fc19d..8d1f144 100644
--- a/daemon/fw/best-route-strategy2.hpp
+++ b/daemon/fw/best-route-strategy2.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2016, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -27,6 +27,7 @@
#define NFD_DAEMON_FW_BEST_ROUTE_STRATEGY2_HPP
#include "strategy.hpp"
+#include "process-nack-traits.hpp"
#include "retx-suppression-exponential.hpp"
namespace nfd {
@@ -50,6 +51,7 @@
* The reason of the sent Nack equals the least severe reason among received Nacks.
*/
class BestRouteStrategy2 : public Strategy
+ , public ProcessNackTraits<BestRouteStrategy2>
{
public:
explicit
@@ -58,11 +60,11 @@
static const Name&
getStrategyName();
- virtual void
+ void
afterReceiveInterest(const Face& inFace, const Interest& interest,
const shared_ptr<pit::Entry>& pitEntry) override;
- virtual void
+ void
afterReceiveNack(const Face& inFace, const lp::Nack& nack,
const shared_ptr<pit::Entry>& pitEntry) override;
@@ -70,6 +72,8 @@
static const time::milliseconds RETX_SUPPRESSION_INITIAL;
static const time::milliseconds RETX_SUPPRESSION_MAX;
RetxSuppressionExponential m_retxSuppression;
+
+ friend ProcessNackTraits<BestRouteStrategy2>;
};
} // namespace fw
diff --git a/daemon/fw/process-nack-traits.cpp b/daemon/fw/process-nack-traits.cpp
new file mode 100644
index 0000000..3167327
--- /dev/null
+++ b/daemon/fw/process-nack-traits.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017, 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 "process-nack-traits.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+namespace fw {
+
+NFD_LOG_INIT("ProcessNackTraits");
+
+/** \brief compare NackReason for severity
+ *
+ * lp::NackReason::NONE is treated as most severe
+ */
+static bool
+isLessSevere(lp::NackReason x, lp::NackReason y)
+{
+ if (x == lp::NackReason::NONE) {
+ return false;
+ }
+ if (y == lp::NackReason::NONE) {
+ return true;
+ }
+
+ return static_cast<int>(x) < static_cast<int>(y);
+}
+
+void
+ProcessNackTraitsBase::processNack(const Face& inFace, const lp::Nack& nack,
+ const shared_ptr<pit::Entry>& pitEntry)
+{
+ int nOutRecordsNotNacked = 0;
+ Face* lastFaceNotNacked = nullptr;
+ lp::NackReason leastSevereReason = lp::NackReason::NONE;
+ for (const pit::OutRecord& outR : pitEntry->getOutRecords()) {
+ const lp::NackHeader* inNack = outR.getIncomingNack();
+ if (inNack == nullptr) {
+ ++nOutRecordsNotNacked;
+ lastFaceNotNacked = &outR.getFace();
+ continue;
+ }
+
+ if (isLessSevere(inNack->getReason(), leastSevereReason)) {
+ leastSevereReason = inNack->getReason();
+ }
+ }
+
+ lp::NackHeader outNack;
+ outNack.setReason(leastSevereReason);
+
+ if (nOutRecordsNotNacked == 1) {
+ BOOST_ASSERT(lastFaceNotNacked != nullptr);
+ pit::InRecordCollection::iterator inR = pitEntry->getInRecord(*lastFaceNotNacked);
+ if (inR != pitEntry->in_end()) {
+ // one out-record not Nacked, which is also a downstream
+ NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
+ " nack=" << nack.getReason() <<
+ " nack-to(bidirectional)=" << lastFaceNotNacked->getId() <<
+ " out-nack=" << outNack.getReason());
+ this->sendNackForProcessNackTraits(pitEntry, *lastFaceNotNacked, outNack);
+ return;
+ }
+ }
+
+ if (nOutRecordsNotNacked > 0) {
+ NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
+ " nack=" << nack.getReason() <<
+ " waiting=" << nOutRecordsNotNacked);
+ // continue waiting
+ return;
+ }
+
+ NFD_LOG_DEBUG(nack.getInterest() << " nack-from=" << inFace.getId() <<
+ " nack=" << nack.getReason() <<
+ " nack-to=all out-nack=" << outNack.getReason());
+ this->sendNacksForProcessNackTraits(pitEntry, outNack);
+}
+
+} // namespace fw
+} // namespace nfd
diff --git a/daemon/fw/process-nack-traits.hpp b/daemon/fw/process-nack-traits.hpp
new file mode 100644
index 0000000..fe70183
--- /dev/null
+++ b/daemon/fw/process-nack-traits.hpp
@@ -0,0 +1,101 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017, 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_PROCESS_NACK_TRAITS_HPP
+#define NFD_DAEMON_FW_PROCESS_NACK_TRAITS_HPP
+
+#include "strategy.hpp"
+
+namespace nfd {
+namespace fw {
+
+/** \brief provides a common procedure for processing Nacks
+ *
+ * This procedure works as follows:
+ * 1. If Nacks have been received from all upstream faces, return a Nack with least severe reason
+ * to downstream faces.
+ * 2. If there are more than one upstream face, Nacks have been received from all but one of
+ * them, and that face is also a downstream, return a Nack to that face. This is to address a
+ * specific "live deadlock" scenario where two hosts are waiting for each other to return the
+ * Nack.
+ * 3. Otherwise, wait for the arrival of more Nacks or Data.
+ *
+ * To use this helper, the strategy should inherit from ProcessNackTraits<MyStrategy>,
+ * and declare that specialization as a friend class.
+ * Then, invoke processNack from afterReceiveNack trigger.
+ */
+class ProcessNackTraitsBase : noncopyable
+{
+protected:
+ virtual
+ ~ProcessNackTraitsBase() = default;
+
+ void
+ processNack(const Face& inFace, const lp::Nack& nack,
+ const shared_ptr<pit::Entry>& pitEntry);
+
+private:
+ virtual void
+ sendNackForProcessNackTraits(const shared_ptr<pit::Entry>& pitEntry, const Face& outFace,
+ const lp::NackHeader& header) = 0;
+
+ virtual void
+ sendNacksForProcessNackTraits(const shared_ptr<pit::Entry>& pitEntry,
+ const lp::NackHeader& header) = 0;
+};
+
+template<typename S>
+class ProcessNackTraits : public ProcessNackTraitsBase
+{
+protected:
+ explicit
+ ProcessNackTraits(S* strategy)
+ : m_strategy(strategy)
+ {
+ }
+
+private:
+ void
+ sendNackForProcessNackTraits(const shared_ptr<pit::Entry>& pitEntry, const Face& outFace,
+ const lp::NackHeader& header) override
+ {
+ m_strategy->sendNack(pitEntry, outFace, header);
+ }
+
+ void
+ sendNacksForProcessNackTraits(const shared_ptr<pit::Entry>& pitEntry,
+ const lp::NackHeader& header) override
+ {
+ m_strategy->sendNacks(pitEntry, header);
+ }
+
+private:
+ S* m_strategy;
+};
+
+} // namespace fw
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_PROCESS_NACK_TRAITS_HPP