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
diff --git a/tests/core/limited-io.cpp b/tests/core/limited-io.cpp
new file mode 100644
index 0000000..5752685
--- /dev/null
+++ b/tests/core/limited-io.cpp
@@ -0,0 +1,58 @@
+/* -*- 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 "limited-io.hpp"
+
+namespace nfd {
+
+const int LimitedIo::UNLIMITED_OPS = std::numeric_limits<int>::max();
+const time::Duration LimitedIo::UNLIMITED_TIME = time::nanoseconds(-1);
+
+LimitedIo::LimitedIo()
+ : m_isRunning(false)
+ , m_nOpsRemaining(0)
+{
+ resetGlobalIoService();
+}
+
+LimitedIo::StopReason
+LimitedIo::run(int nOpsLimit, time::Duration nTimeLimit)
+{
+ BOOST_ASSERT(!m_isRunning);
+ m_isRunning = true;
+
+ m_reason = NO_WORK;
+ m_nOpsRemaining = nOpsLimit;
+ if (nTimeLimit != UNLIMITED_TIME) {
+ m_timeout = scheduler::schedule(nTimeLimit, bind(&LimitedIo::afterTimeout, this));
+ }
+
+ getGlobalIoService().run();
+
+ getGlobalIoService().reset();
+ scheduler::cancel(m_timeout);
+ m_isRunning = false;
+ return m_reason;
+}
+
+void
+LimitedIo::afterOp()
+{
+ --m_nOpsRemaining;
+ if (m_nOpsRemaining <= 0) {
+ m_reason = EXCEED_OPS;
+ getGlobalIoService().stop();
+ }
+}
+
+void
+LimitedIo::afterTimeout()
+{
+ m_reason = EXCEED_TIME;
+ getGlobalIoService().stop();
+}
+
+} // namespace nfd
diff --git a/tests/core/limited-io.hpp b/tests/core/limited-io.hpp
new file mode 100644
index 0000000..14dadd7
--- /dev/null
+++ b/tests/core/limited-io.hpp
@@ -0,0 +1,49 @@
+/* -*- 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_TEST_CORE_LIMITED_IO_HPP
+#define NFD_TEST_CORE_LIMITED_IO_HPP
+
+#include "core/scheduler.hpp"
+
+namespace nfd {
+
+class LimitedIo
+{
+public:
+ LimitedIo();
+
+ enum StopReason
+ {
+ NO_WORK,
+ EXCEED_OPS,
+ EXCEED_TIME
+ };
+
+ StopReason
+ run(int nOpsLimit, time::Duration nTimeLimit);
+
+ void
+ afterOp();
+
+private:
+ void
+ afterTimeout();
+
+public:
+ static const int UNLIMITED_OPS;
+ static const time::Duration UNLIMITED_TIME;
+
+private:
+ bool m_isRunning;
+ int m_nOpsRemaining;
+ EventId m_timeout;
+ StopReason m_reason;
+};
+
+} // namespace nfd
+
+#endif // NFD_TEST_CORE_LIMITED_IO_HPP
diff --git a/tests/fw/ncc-strategy.cpp b/tests/fw/ncc-strategy.cpp
new file mode 100644
index 0000000..a258b83
--- /dev/null
+++ b/tests/fw/ncc-strategy.cpp
@@ -0,0 +1,87 @@
+/* -*- 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 "fw/ncc-strategy.hpp"
+#include "strategy-tester.hpp"
+#include "../face/dummy-face.hpp"
+#include "../core/limited-io.hpp"
+
+#include <boost/test/unit_test.hpp>
+
+namespace nfd {
+
+BOOST_AUTO_TEST_SUITE(FwNccStrategy)
+
+// NccStrategy is fairly complex.
+// The most important property is:
+// it remembers which upstream is the fastest to return Data,
+// and favors this upstream in subsequent Interests.
+BOOST_AUTO_TEST_CASE(FavorRespondingUpstream)
+{
+ LimitedIo limitedIo;
+ Forwarder forwarder;
+ typedef StrategyTester<fw::NccStrategy> NccStrategyTester;
+ shared_ptr<NccStrategyTester> strategy = make_shared<NccStrategyTester>(boost::ref(forwarder));
+ strategy->onAction += bind(&LimitedIo::afterOp, &limitedIo);
+
+ shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+ shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+ shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+ forwarder.addFace(face1);
+ forwarder.addFace(face2);
+ forwarder.addFace(face3);
+
+ Fib& fib = forwarder.getFib();
+ shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+ fibEntry->setStrategy(strategy);
+ fibEntry->addNextHop(face1, 10);
+ fibEntry->addNextHop(face2, 20);
+
+ Pit& pit = forwarder.getPit();
+
+ // first Interest: strategy knows nothing and follows routing
+ shared_ptr<Interest> interest1p = make_shared<Interest>("ndn:/0Jm1ajrW/%00");
+ Interest& interest1 = *interest1p;
+ interest1.setInterestLifetime(8000);
+ shared_ptr<pit::Entry> pitEntry1 = pit.insert(interest1).first;
+
+ pitEntry1->insertOrUpdateInRecord(face3, interest1);
+ strategy->afterReceiveInterest(*face3, interest1, fibEntry, pitEntry1);
+
+ // forwards to face1 because routing says it's best
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(1));
+ BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 1);
+ BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[0].get<1>(), face1);
+
+ // forwards to face2 because face1 doesn't respond
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(500));
+ BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 2);
+ BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[1].get<1>(), face2);
+
+ // face2 responds
+ shared_ptr<Data> data1p = make_shared<Data>("ndn:/0Jm1ajrW/%00");
+ Data& data1 = *data1p;
+ strategy->beforeSatisfyPendingInterest(pitEntry1, *face2, data1);
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(500));
+
+ // second Interest: strategy knows face2 is best
+ shared_ptr<Interest> interest2p = make_shared<Interest>("ndn:/0Jm1ajrW/%00%01");
+ Interest& interest2 = *interest2p;
+ interest2.setInterestLifetime(8000);
+ shared_ptr<pit::Entry> pitEntry2 = pit.insert(interest2).first;
+
+ pitEntry2->insertOrUpdateInRecord(face3, interest2);
+ strategy->afterReceiveInterest(*face3, interest2, fibEntry, pitEntry2);
+
+ // forwards to face2 because it responds previously
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(1));
+ BOOST_REQUIRE_GE(strategy->m_sendInterestHistory.size(), 3);
+ BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[2].get<1>(), face2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace nfd
diff --git a/tests/fw/strategy-tester.hpp b/tests/fw/strategy-tester.hpp
index 0ec4770..f8e0848 100644
--- a/tests/fw/strategy-tester.hpp
+++ b/tests/fw/strategy-tester.hpp
@@ -26,6 +26,9 @@
: S(forwarder)
{
}
+
+ /// fires after each Action
+ EventEmitter<> onAction;
protected:
virtual void
@@ -49,6 +52,8 @@
shared_ptr<Face> outFace)
{
m_sendInterestHistory.push_back(SendInterestArgs(pitEntry, outFace));
+ pitEntry->insertOrUpdateOutRecord(outFace, pitEntry->getInterest());
+ onAction();
}
template<typename S>
@@ -56,6 +61,7 @@
StrategyTester<S>::rejectPendingInterest(shared_ptr<pit::Entry> pitEntry)
{
m_rejectPendingInterestHistory.push_back(RejectPendingInterestArgs(pitEntry));
+ onAction();
}
diff --git a/wscript b/wscript
index a66bfc4..b7f96c9 100644
--- a/wscript
+++ b/wscript
@@ -32,6 +32,7 @@
'-Qunused-arguments', # only clang supports
'-Wno-tautological-compare', # suppress warnings from CryptoPP
'-Wno-unused-function', # suppress warnings from CryptoPP
+ '-fno-inline',
])
else:
conf.add_supported_cxxflags (cxxflags = ['-O3', '-g', '-Wno-tautological-compare', '-Wno-unused-function'])