fw: NccStrategy
refs #1242
Change-Id: I21f870728b7b361547adbde9d64357c93aeb1ed9
diff --git a/daemon/common.hpp b/daemon/common.hpp
index 2e0ff22..650541e 100644
--- a/daemon/common.hpp
+++ b/daemon/common.hpp
@@ -53,6 +53,7 @@
using boost::make_shared;
using boost::static_pointer_cast;
using boost::dynamic_pointer_cast;
+using boost::weak_ptr;
using boost::function;
using boost::bind;
diff --git a/daemon/fw/ncc-strategy.cpp b/daemon/fw/ncc-strategy.cpp
new file mode 100644
index 0000000..d08260c
--- /dev/null
+++ b/daemon/fw/ncc-strategy.cpp
@@ -0,0 +1,281 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "ncc-strategy.hpp"
+
+namespace nfd {
+namespace fw {
+
+NccStrategy::NccStrategy(Forwarder& forwarder)
+ : Strategy(forwarder)
+{
+}
+
+NccStrategy::~NccStrategy()
+{
+}
+
+const time::Duration NccStrategy::DEFER_FIRST_WITHOUT_BEST_FACE = time::microseconds(4000);
+const time::Duration NccStrategy::DEFER_RANGE_WITHOUT_BEST_FACE = time::microseconds(75000);
+const time::Duration NccStrategy::MEASUREMENTS_LIFETIME = time::seconds(16);
+
+void
+NccStrategy::afterReceiveInterest(const Face& inFace,
+ const Interest& interest,
+ shared_ptr<fib::Entry> fibEntry,
+ shared_ptr<pit::Entry> pitEntry)
+{
+ const fib::NextHopList& nexthops = fibEntry->getNextHops();
+ if (nexthops.size() == 0) {
+ this->rejectPendingInterest(pitEntry);
+ return;
+ }
+
+ shared_ptr<PitEntryInfo> pitEntryInfo =
+ pitEntry->getOrCreateStrategyInfo<PitEntryInfo>();
+ bool isNewInterest = pitEntryInfo->m_isNewInterest;
+ if (!isNewInterest) {
+ return;
+ }
+ pitEntryInfo->m_isNewInterest = false;
+
+ shared_ptr<MeasurementsEntryInfo> measurementsEntryInfo =
+ this->getMeasurementsEntryInfo(pitEntry);
+
+ time::Duration deferFirst = DEFER_FIRST_WITHOUT_BEST_FACE;
+ time::Duration deferRange = DEFER_RANGE_WITHOUT_BEST_FACE;
+ size_t nUpstreams = nexthops.size();
+
+ shared_ptr<Face> bestFace = measurementsEntryInfo->getBestFace();
+ if (static_cast<bool>(bestFace) && fibEntry->hasNextHop(bestFace) &&
+ pitEntry->canForwardTo(bestFace)) {
+ // TODO Should we use `randlow = 100 + nrand48(h->seed) % 4096U;` ?
+ deferFirst = measurementsEntryInfo->m_prediction;
+ deferRange = static_cast<time::Duration>((deferFirst + time::microseconds(1)) / 2);
+ --nUpstreams;
+ this->sendInterest(pitEntry, bestFace);
+ pitEntryInfo->m_bestFaceTimeout = scheduler::schedule(
+ measurementsEntryInfo->m_prediction,
+ bind(&NccStrategy::timeoutOnBestFace, this, weak_ptr<pit::Entry>(pitEntry)));
+ }
+ else {
+ // use first nexthop
+ this->sendInterest(pitEntry, nexthops.begin()->getFace());
+ // TODO avoid sending to inFace
+ }
+
+ shared_ptr<Face> previousFace = measurementsEntryInfo->m_previousFace.lock();
+ if (static_cast<bool>(previousFace) && fibEntry->hasNextHop(previousFace) &&
+ pitEntry->canForwardTo(previousFace)) {
+ --nUpstreams;
+ }
+
+ pitEntryInfo->m_maxInterval = std::max(time::microseconds(1),
+ static_cast<time::Duration>((2 * deferRange + nUpstreams - 1) / nUpstreams));
+ pitEntryInfo->m_propagateTimer = scheduler::schedule(deferFirst,
+ bind(&NccStrategy::doPropagate, this,
+ weak_ptr<pit::Entry>(pitEntry), weak_ptr<fib::Entry>(fibEntry)));
+}
+
+void
+NccStrategy::doPropagate(weak_ptr<pit::Entry> pitEntryWeak, weak_ptr<fib::Entry> fibEntryWeak)
+{
+ shared_ptr<pit::Entry> pitEntry = pitEntryWeak.lock();
+ if (!static_cast<bool>(pitEntry)) {
+ return;
+ }
+ shared_ptr<fib::Entry> fibEntry = fibEntryWeak.lock();
+ if (!static_cast<bool>(fibEntry)) {
+ return;
+ }
+
+ shared_ptr<PitEntryInfo> pitEntryInfo =
+ pitEntry->getStrategyInfo<PitEntryInfo>();
+ BOOST_ASSERT(static_cast<bool>(pitEntryInfo));
+
+ shared_ptr<MeasurementsEntryInfo> measurementsEntryInfo =
+ this->getMeasurementsEntryInfo(pitEntry);
+
+ shared_ptr<Face> previousFace = measurementsEntryInfo->m_previousFace.lock();
+ if (static_cast<bool>(previousFace) && fibEntry->hasNextHop(previousFace) &&
+ pitEntry->canForwardTo(previousFace)) {
+ this->sendInterest(pitEntry, previousFace);
+ }
+
+ const fib::NextHopList& nexthops = fibEntry->getNextHops();
+ bool isForwarded = false;
+ for (fib::NextHopList::const_iterator it = nexthops.begin(); it != nexthops.end(); ++it) {
+ shared_ptr<Face> face = it->getFace();
+ if (pitEntry->canForwardTo(face)) {
+ isForwarded = true;
+ this->sendInterest(pitEntry, face);
+ break;
+ }
+ }
+
+ if (isForwarded) {
+ static unsigned short seed[3];
+ time::Duration deferNext = static_cast<time::Duration>(
+ nrand48(seed) % pitEntryInfo->m_maxInterval);
+ pitEntryInfo->m_propagateTimer = scheduler::schedule(deferNext,
+ bind(&NccStrategy::doPropagate, this,
+ weak_ptr<pit::Entry>(pitEntry), weak_ptr<fib::Entry>(fibEntry)));
+ }
+}
+
+void
+NccStrategy::timeoutOnBestFace(weak_ptr<pit::Entry> pitEntryWeak)
+{
+ shared_ptr<pit::Entry> pitEntry = pitEntryWeak.lock();
+ if (!static_cast<bool>(pitEntry)) {
+ return;
+ }
+ shared_ptr<measurements::Entry> measurementsEntry = this->getMeasurements().get(*pitEntry);
+
+ for (int i = 0; i < UPDATE_MEASUREMENTS_N_LEVELS; ++i) {
+ if (!static_cast<bool>(measurementsEntry)) {
+ // going out of this strategy's namespace
+ return;
+ }
+ this->getMeasurements().extendLifetime(*measurementsEntry, MEASUREMENTS_LIFETIME);
+
+ shared_ptr<MeasurementsEntryInfo> measurementsEntryInfo =
+ this->getMeasurementsEntryInfo(measurementsEntry);
+ measurementsEntryInfo->adjustPredictUp();
+
+ measurementsEntry = this->getMeasurements().getParent(measurementsEntry);
+ }
+}
+
+void
+NccStrategy::beforeSatisfyPendingInterest(shared_ptr<pit::Entry> pitEntry,
+ const Face& inFace, const Data& data)
+{
+ shared_ptr<measurements::Entry> measurementsEntry = this->getMeasurements().get(*pitEntry);
+
+ for (int i = 0; i < UPDATE_MEASUREMENTS_N_LEVELS; ++i) {
+ if (!static_cast<bool>(measurementsEntry)) {
+ // going out of this strategy's namespace
+ return;
+ }
+ this->getMeasurements().extendLifetime(*measurementsEntry, MEASUREMENTS_LIFETIME);
+
+ shared_ptr<MeasurementsEntryInfo> measurementsEntryInfo =
+ this->getMeasurementsEntryInfo(measurementsEntry);
+ measurementsEntryInfo->updateBestFace(inFace);
+
+ measurementsEntry = this->getMeasurements().getParent(measurementsEntry);
+ }
+
+ shared_ptr<PitEntryInfo> pitEntryInfo =
+ pitEntry->getStrategyInfo<PitEntryInfo>();
+ scheduler::cancel(pitEntryInfo->m_propagateTimer);
+}
+
+shared_ptr<NccStrategy::MeasurementsEntryInfo>
+NccStrategy::getMeasurementsEntryInfo(shared_ptr<pit::Entry> entry)
+{
+ shared_ptr<measurements::Entry> measurementsEntry = this->getMeasurements().get(*entry);
+ return this->getMeasurementsEntryInfo(measurementsEntry);
+}
+
+shared_ptr<NccStrategy::MeasurementsEntryInfo>
+NccStrategy::getMeasurementsEntryInfo(shared_ptr<measurements::Entry> entry)
+{
+ shared_ptr<MeasurementsEntryInfo> info = entry->getStrategyInfo<MeasurementsEntryInfo>();
+ if (static_cast<bool>(info)) {
+ return info;
+ }
+
+ info = make_shared<MeasurementsEntryInfo>();
+ entry->setStrategyInfo(info);
+
+ shared_ptr<measurements::Entry> parentEntry = this->getMeasurements().getParent(entry);
+ if (static_cast<bool>(parentEntry)) {
+ shared_ptr<MeasurementsEntryInfo> parentInfo = this->getMeasurementsEntryInfo(parentEntry);
+ BOOST_ASSERT(static_cast<bool>(parentInfo));
+ info->inheritFrom(*parentInfo);
+ }
+
+ return info;
+}
+
+
+const time::Duration NccStrategy::MeasurementsEntryInfo::INITIAL_PREDICTION =
+ time::microseconds(8192);
+const time::Duration NccStrategy::MeasurementsEntryInfo::MIN_PREDICTION =
+ time::microseconds(127);
+const time::Duration NccStrategy::MeasurementsEntryInfo::MAX_PREDICTION =
+ time::microseconds(160000);
+
+NccStrategy::MeasurementsEntryInfo::MeasurementsEntryInfo()
+ : m_prediction(INITIAL_PREDICTION)
+{
+}
+
+void
+NccStrategy::MeasurementsEntryInfo::inheritFrom(const MeasurementsEntryInfo& other)
+{
+ this->operator=(other);
+}
+
+shared_ptr<Face>
+NccStrategy::MeasurementsEntryInfo::getBestFace(void) {
+ shared_ptr<Face> best = m_bestFace.lock();
+ if (static_cast<bool>(best)) {
+ return best;
+ }
+ m_bestFace = best = m_previousFace.lock();
+ return best;
+}
+
+void
+NccStrategy::MeasurementsEntryInfo::updateBestFace(const Face& face) {
+ if (m_bestFace.expired()) {
+ m_bestFace = const_cast<Face&>(face).shared_from_this();
+ return;
+ }
+ shared_ptr<Face> bestFace = m_bestFace.lock();
+ if (bestFace.get() == &face) {
+ this->adjustPredictDown();
+ }
+ else {
+ m_previousFace = m_bestFace;
+ m_bestFace = const_cast<Face&>(face).shared_from_this();
+ }
+}
+
+void
+NccStrategy::MeasurementsEntryInfo::adjustPredictDown() {
+ m_prediction = std::max(MIN_PREDICTION,
+ static_cast<time::Duration>(m_prediction - (m_prediction >> ADJUST_PREDICT_DOWN_SHIFT)));
+}
+
+void
+NccStrategy::MeasurementsEntryInfo::adjustPredictUp() {
+ m_prediction = std::min(MAX_PREDICTION,
+ static_cast<time::Duration>(m_prediction + (m_prediction >> ADJUST_PREDICT_UP_SHIFT)));
+}
+
+void
+NccStrategy::MeasurementsEntryInfo::ageBestFace() {
+ m_previousFace = m_bestFace;
+ m_bestFace.reset();
+}
+
+NccStrategy::PitEntryInfo::PitEntryInfo()
+ : m_isNewInterest(true)
+{
+}
+
+NccStrategy::PitEntryInfo::~PitEntryInfo()
+{
+ scheduler::cancel(m_bestFaceTimeout);
+ scheduler::cancel(m_propagateTimer);
+}
+
+} // namespace fw
+} // namespace nfd
diff --git a/daemon/fw/ncc-strategy.hpp b/daemon/fw/ncc-strategy.hpp
new file mode 100644
index 0000000..5d1042f
--- /dev/null
+++ b/daemon/fw/ncc-strategy.hpp
@@ -0,0 +1,117 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_FW_NCC_STRATEGY_HPP
+#define NFD_FW_NCC_STRATEGY_HPP
+
+#include "strategy.hpp"
+#include "forwarder.hpp"
+#include "strategy-info.hpp"
+
+namespace nfd {
+namespace fw {
+
+/** \brief a forwarding strategy similar to CCNx 0.7.2
+ */
+class NccStrategy : public Strategy
+{
+public:
+ explicit
+ NccStrategy(Forwarder& forwarder);
+
+ virtual
+ ~NccStrategy();
+
+ virtual void
+ afterReceiveInterest(const Face& inFace,
+ const Interest& interest,
+ shared_ptr<fib::Entry> fibEntry,
+ shared_ptr<pit::Entry> pitEntry);
+
+ virtual void
+ beforeSatisfyPendingInterest(shared_ptr<pit::Entry> pitEntry,
+ const Face& inFace, const Data& data);
+
+protected:
+ /// StrategyInfo on measurements::Entry
+ class MeasurementsEntryInfo : public StrategyInfo
+ {
+ public:
+ MeasurementsEntryInfo();
+
+ void
+ inheritFrom(const MeasurementsEntryInfo& other);
+
+ shared_ptr<Face>
+ getBestFace();
+
+ void
+ updateBestFace(const Face& face);
+
+ void
+ adjustPredictUp();
+
+ private:
+ void
+ adjustPredictDown();
+
+ void
+ ageBestFace();
+
+ public:
+ weak_ptr<Face> m_bestFace;
+ weak_ptr<Face> m_previousFace;
+ time::Duration m_prediction;
+
+ static const time::Duration INITIAL_PREDICTION;
+ static const time::Duration MIN_PREDICTION;
+ static const int ADJUST_PREDICT_DOWN_SHIFT = 7;
+ static const time::Duration MAX_PREDICTION;
+ static const int ADJUST_PREDICT_UP_SHIFT = 3;
+ };
+
+ /// StrategyInfo on pit::Entry
+ class PitEntryInfo : public StrategyInfo
+ {
+ public:
+ PitEntryInfo();
+
+ virtual
+ ~PitEntryInfo();
+
+ public:
+ bool m_isNewInterest;
+ EventId m_bestFaceTimeout;
+ EventId m_propagateTimer;
+ time::Duration m_maxInterval;
+ };
+
+protected:
+ shared_ptr<MeasurementsEntryInfo>
+ getMeasurementsEntryInfo(shared_ptr<measurements::Entry> entry);
+
+ shared_ptr<MeasurementsEntryInfo>
+ getMeasurementsEntryInfo(shared_ptr<pit::Entry> entry);
+
+ /// propagate to another upstream
+ void
+ doPropagate(weak_ptr<pit::Entry> pitEntryWeak, weak_ptr<fib::Entry> fibEntryWeak);
+
+ /// best face did not reply within prediction
+ void
+ timeoutOnBestFace(weak_ptr<pit::Entry> pitEntryWeak);
+
+protected:
+ static const time::Duration DEFER_FIRST_WITHOUT_BEST_FACE;
+ static const time::Duration DEFER_RANGE_WITHOUT_BEST_FACE;
+ static const int UPDATE_MEASUREMENTS_N_LEVELS = 2;
+ static const time::Duration MEASUREMENTS_LIFETIME;
+};
+
+} // namespace fw
+} // namespace nfd
+
+#endif // NFD_FW_NCC_STRATEGY_HPP
diff --git a/daemon/table/fib-entry.cpp b/daemon/table/fib-entry.cpp
index 61b9827..733782b 100644
--- a/daemon/table/fib-entry.cpp
+++ b/daemon/table/fib-entry.cpp
@@ -20,6 +20,14 @@
return nexthop.getFace() == face;
}
+bool
+Entry::hasNextHop(shared_ptr<Face> face) const
+{
+ NextHopList::const_iterator it = std::find_if(m_nextHops.begin(), m_nextHops.end(),
+ bind(&predicate_NextHop_eq_Face, _1, face));
+ return it != m_nextHops.end();
+}
+
void
Entry::addNextHop(shared_ptr<Face> face, int32_t cost)
{
diff --git a/daemon/table/fib-entry.hpp b/daemon/table/fib-entry.hpp
index 6adddd8..f5c0fae 100644
--- a/daemon/table/fib-entry.hpp
+++ b/daemon/table/fib-entry.hpp
@@ -38,12 +38,12 @@
const Name&
getPrefix() const;
- /** \brief gives the nexthops explicitly defined on this entry
- * This list does not include inherited nexthops.
- */
const NextHopList&
getNextHops() const;
+ bool
+ hasNextHop(shared_ptr<Face> face) const;
+
/// adds a nexthop
void
addNextHop(shared_ptr<Face> face, int32_t cost);
diff --git a/daemon/table/measurements.cpp b/daemon/table/measurements.cpp
index ed01018..8caf343 100644
--- a/daemon/table/measurements.cpp
+++ b/daemon/table/measurements.cpp
@@ -19,6 +19,11 @@
Measurements::~Measurements()
{
+ for (std::map<Name, shared_ptr<measurements::Entry> >::iterator it = m_table.begin();
+ it != m_table.end(); ++it) {
+ shared_ptr<measurements::Entry> entry = it->second;
+ scheduler::cancel(entry->m_cleanup);
+ }
}
shared_ptr<measurements::Entry>
diff --git a/daemon/table/pit-entry.cpp b/daemon/table/pit-entry.cpp
index 7a719df..2d016ca 100644
--- a/daemon/table/pit-entry.cpp
+++ b/daemon/table/pit-entry.cpp
@@ -33,6 +33,38 @@
return m_outRecords;
}
+static inline bool
+predicate_FaceRecord_Face(const FaceRecord& faceRecord, shared_ptr<Face> face)
+{
+ return faceRecord.getFace() == face;
+}
+
+static inline bool
+predicate_FaceRecord_ne_Face_and_unexpired(const FaceRecord& faceRecord,
+ shared_ptr<Face> face, time::Point now)
+{
+ return faceRecord.getFace() != face && faceRecord.getExpiry() >= now;
+}
+
+bool
+Entry::canForwardTo(shared_ptr<Face> face) const
+{
+ OutRecordCollection::const_iterator outIt = std::find_if(
+ m_outRecords.begin(), m_outRecords.end(),
+ bind(&predicate_FaceRecord_Face, _1, face));
+ bool hasUnexpiredOutRecord = outIt != m_outRecords.end() &&
+ outIt->getExpiry() >= time::now();
+ if (hasUnexpiredOutRecord) {
+ return false;
+ }
+
+ InRecordCollection::const_iterator inIt = std::find_if(
+ m_inRecords.begin(), m_inRecords.end(),
+ bind(&predicate_FaceRecord_ne_Face_and_unexpired, _1, face, time::now()));
+ bool hasUnexpiredOtherInRecord = inIt != m_inRecords.end();
+ return hasUnexpiredOtherInRecord;
+}
+
bool
Entry::addNonce(uint32_t nonce)
{
@@ -42,12 +74,6 @@
return insertResult.second;
}
-static inline bool
-predicate_FaceRecord_Face(const FaceRecord& faceRecord, shared_ptr<Face> face)
-{
- return faceRecord.getFace() == face;
-}
-
InRecordCollection::iterator
Entry::insertOrUpdateInRecord(shared_ptr<Face> face, const Interest& interest)
{
diff --git a/daemon/table/pit-entry.hpp b/daemon/table/pit-entry.hpp
index 979af6c..c9cfdac 100644
--- a/daemon/table/pit-entry.hpp
+++ b/daemon/table/pit-entry.hpp
@@ -24,61 +24,68 @@
*/
typedef std::list<OutRecord> OutRecordCollection;
-/** \class Entry
- * \brief represents a PIT entry
+/** \brief represents a PIT entry
*/
class Entry : public StrategyInfoHost, noncopyable
{
public:
explicit
Entry(const Interest& interest);
-
+
const Interest&
getInterest() const;
-
- /** \return{ Interest Name }
+
+ /** \return Interest Name
*/
const Name&
getName() const;
-
+
const InRecordCollection&
getInRecords() const;
-
+
const OutRecordCollection&
getOutRecords() const;
-
+
+ /** \brief decides whether Interest can be forwarded to face
+ *
+ * \return true if OutRecord of this face does not exist or has expired,
+ * and there is an InRecord not of this face
+ */
+ bool
+ canForwardTo(shared_ptr<Face> face) const;
+
/** \brief records a nonce
*
- * \return{ true if nonce is new; false if nonce is seen before }
+ * \return true if nonce is new; false if nonce is seen before
*/
bool
addNonce(uint32_t nonce);
-
+
/** \brief inserts a InRecord for face, and updates it with interest
*
* If InRecord for face exists, the existing one is updated.
* This method does not add the Nonce as a seen Nonce.
- * \return{ an iterator to the InRecord }
+ * \return an iterator to the InRecord
*/
InRecordCollection::iterator
insertOrUpdateInRecord(shared_ptr<Face> face, const Interest& interest);
-
+
/// deletes all InRecords
void
deleteInRecords();
-
+
/** \brief inserts a OutRecord for face, and updates it with interest
*
* If OutRecord for face exists, the existing one is updated.
- * \return{ an iterator to the OutRecord }
+ * \return an iterator to the OutRecord
*/
OutRecordCollection::iterator
insertOrUpdateOutRecord(shared_ptr<Face> face, const Interest& interest);
-
+
/// deletes one OutRecord for face if exists
void
deleteOutRecord(shared_ptr<Face> face);
-
+
public:
EventId m_unsatisfyTimer;
EventId m_stragglerTimer;
diff --git a/daemon/table/strategy-info-host.hpp b/daemon/table/strategy-info-host.hpp
index 41199b2..272177f 100644
--- a/daemon/table/strategy-info-host.hpp
+++ b/daemon/table/strategy-info-host.hpp
@@ -25,6 +25,14 @@
shared_ptr<T>
getStrategyInfo();
+ template<typename T>
+ shared_ptr<T>
+ getOrCreateStrategyInfo();
+
+ template<typename T, typename T1>
+ shared_ptr<T>
+ getOrCreateStrategyInfo(T1& a1);
+
void
clearStrategyInfo();
@@ -47,6 +55,30 @@
return static_pointer_cast<T, fw::StrategyInfo>(m_strategyInfo);
}
+template<typename T>
+shared_ptr<T>
+StrategyInfoHost::getOrCreateStrategyInfo()
+{
+ shared_ptr<T> info = this->getStrategyInfo<T>();
+ if (!static_cast<bool>(info)) {
+ info = make_shared<T>();
+ this->setStrategyInfo(info);
+ }
+ return info;
+}
+
+template<typename T, typename T1>
+shared_ptr<T>
+StrategyInfoHost::getOrCreateStrategyInfo(T1& a1)
+{
+ shared_ptr<T> info = this->getStrategyInfo<T>();
+ if (!static_cast<bool>(info)) {
+ info = make_shared<T>(boost::ref(a1));
+ this->setStrategyInfo(info);
+ }
+ return info;
+}
+
} // namespace nfd
#endif // NFD_TABLE_STRATEGY_INFO_HOST_HPP