fw: Add ASF strategy parameters n-silent-timeouts & probing-interval

refs: #4193

Change-Id: I9572425a2fdcbf67b9886c2a5b6b50a10a0856e2
diff --git a/daemon/fw/asf-measurements.cpp b/daemon/fw/asf-measurements.cpp
index e8bc8fd..e85ec3f 100644
--- a/daemon/fw/asf-measurements.cpp
+++ b/daemon/fw/asf-measurements.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-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -66,6 +66,7 @@
 
 FaceInfo::FaceInfo()
   : m_isTimeoutScheduled(false)
+  , m_nSilentTimeouts(0)
 {
 }
 
@@ -147,9 +148,9 @@
 }
 
 FaceInfo*
-NamespaceInfo::getFaceInfo(const fib::Entry& fibEntry, const Face& face)
+NamespaceInfo::getFaceInfo(const fib::Entry& fibEntry, FaceId faceId)
 {
-  FaceInfoTable::iterator it = m_fit.find(face.getId());
+  FaceInfoTable::iterator it = m_fit.find(faceId);
 
   if (it != m_fit.end()) {
     return &it->second;
@@ -160,17 +161,17 @@
 }
 
 FaceInfo&
-NamespaceInfo::getOrCreateFaceInfo(const fib::Entry& fibEntry, const Face& face)
+NamespaceInfo::getOrCreateFaceInfo(const fib::Entry& fibEntry, FaceId faceId)
 {
-  FaceInfoTable::iterator it = m_fit.find(face.getId());
+  FaceInfoTable::iterator it = m_fit.find(faceId);
 
   FaceInfo* info = nullptr;
 
   if (it == m_fit.end()) {
-    const auto& pair = m_fit.insert(std::make_pair(face.getId(), FaceInfo()));
+    const auto& pair = m_fit.insert(std::make_pair(faceId, FaceInfo()));
     info = &pair.first->second;
 
-    extendFaceInfoLifetime(*info, face);
+    extendFaceInfoLifetime(*info, faceId);
   }
   else {
     info = &it->second;
@@ -180,20 +181,20 @@
 }
 
 void
-NamespaceInfo::expireFaceInfo(nfd::face::FaceId faceId)
+NamespaceInfo::expireFaceInfo(FaceId faceId)
 {
   m_fit.erase(faceId);
 }
 
 void
-NamespaceInfo::extendFaceInfoLifetime(FaceInfo& info, const Face& face)
+NamespaceInfo::extendFaceInfoLifetime(FaceInfo& info, FaceId faceId)
 {
   // Cancel previous expiration
   scheduler::cancel(info.getMeasurementExpirationEventId());
 
   // Refresh measurement
   scheduler::EventId id = scheduler::schedule(AsfMeasurements::MEASUREMENTS_LIFETIME,
-    bind(&NamespaceInfo::expireFaceInfo, this, face.getId()));
+    bind(&NamespaceInfo::expireFaceInfo, this, faceId));
 
   info.setMeasurementExpirationEventId(id);
 }
@@ -209,17 +210,18 @@
 }
 
 FaceInfo*
-AsfMeasurements::getFaceInfo(const fib::Entry& fibEntry, const Interest& interest, const Face& face)
+AsfMeasurements::getFaceInfo(const fib::Entry& fibEntry, const Interest& interest, FaceId faceId)
 {
   NamespaceInfo& info = getOrCreateNamespaceInfo(fibEntry, interest);
-  return info.getFaceInfo(fibEntry, face);
+  return info.getFaceInfo(fibEntry, faceId);
 }
 
 FaceInfo&
-AsfMeasurements::getOrCreateFaceInfo(const fib::Entry& fibEntry, const Interest& interest, const Face& face)
+AsfMeasurements::getOrCreateFaceInfo(const fib::Entry& fibEntry, const Interest& interest,
+                                     FaceId faceId)
 {
   NamespaceInfo& info = getOrCreateNamespaceInfo(fibEntry, interest);
-  return info.getOrCreateFaceInfo(fibEntry, face);
+  return info.getOrCreateFaceInfo(fibEntry, faceId);
 }
 
 NamespaceInfo*
diff --git a/daemon/fw/asf-measurements.hpp b/daemon/fw/asf-measurements.hpp
index ba858df..ee5885d 100644
--- a/daemon/fw/asf-measurements.hpp
+++ b/daemon/fw/asf-measurements.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2017,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -166,6 +166,18 @@
     return getSrtt() != RttStats::RTT_NO_MEASUREMENT;
   }
 
+  size_t
+  getNSilentTimeouts() const
+  {
+    return m_nSilentTimeouts;
+  }
+
+  void
+  setNSilentTimeouts(size_t nSilentTimeouts)
+  {
+    m_nSilentTimeouts = nSilentTimeouts;
+  }
+
 private:
   void
   cancelTimeoutEvent();
@@ -183,9 +195,10 @@
   // RTO associated with Interest
   scheduler::EventId m_timeoutEventId;
   bool m_isTimeoutScheduled;
+  size_t m_nSilentTimeouts;
 };
 
-typedef std::unordered_map<face::FaceId, FaceInfo> FaceInfoTable;
+typedef std::unordered_map<FaceId, FaceInfo> FaceInfoTable;
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
@@ -204,19 +217,19 @@
   }
 
   FaceInfo&
-  getOrCreateFaceInfo(const fib::Entry& fibEntry, const Face& face);
+  getOrCreateFaceInfo(const fib::Entry& fibEntry, FaceId faceId);
 
   FaceInfo*
-  getFaceInfo(const fib::Entry& fibEntry, const Face& face);
+  getFaceInfo(const fib::Entry& fibEntry, FaceId faceId);
 
   void
-  expireFaceInfo(nfd::face::FaceId faceId);
+  expireFaceInfo(FaceId faceId);
 
   void
-  extendFaceInfoLifetime(FaceInfo& info, const Face& face);
+  extendFaceInfoLifetime(FaceInfo& info, FaceId faceId);
 
   FaceInfo*
-  get(nfd::face::FaceId faceId)
+  get(FaceId faceId)
   {
     if (m_fit.find(faceId) != m_fit.end()) {
       return &m_fit.at(faceId);
@@ -227,7 +240,7 @@
   }
 
   FaceInfoTable::iterator
-  find(nfd::face::FaceId faceId)
+  find(FaceId faceId)
   {
     return m_fit.find(faceId);
   }
@@ -239,7 +252,7 @@
   }
 
   const FaceInfoTable::iterator
-  insert(nfd::face::FaceId faceId)
+  insert(FaceId faceId)
   {
     const auto& pair = m_fit.insert(std::make_pair(faceId, FaceInfo()));
     return pair.first;
@@ -288,10 +301,10 @@
   AsfMeasurements(MeasurementsAccessor& measurements);
 
   FaceInfo*
-  getFaceInfo(const fib::Entry& fibEntry, const Interest& interest, const Face& face);
+  getFaceInfo(const fib::Entry& fibEntry, const Interest& interest, FaceId faceId);
 
   FaceInfo&
-  getOrCreateFaceInfo(const fib::Entry& fibEntry, const Interest& interest, const Face& face);
+  getOrCreateFaceInfo(const fib::Entry& fibEntry, const Interest& interest, FaceId faceId);
 
   NamespaceInfo*
   getNamespaceInfo(const Name& prefix);
diff --git a/daemon/fw/asf-probing-module.cpp b/daemon/fw/asf-probing-module.cpp
index f29d0e7..24b4577 100644
--- a/daemon/fw/asf-probing-module.cpp
+++ b/daemon/fw/asf-probing-module.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-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -31,7 +31,8 @@
 namespace fw {
 namespace asf {
 
-constexpr time::seconds ProbingModule::DEFAULT_PROBING_INTERVAL;
+constexpr time::milliseconds ProbingModule::DEFAULT_PROBING_INTERVAL;
+constexpr time::milliseconds ProbingModule::MIN_PROBING_INTERVAL;
 
 static_assert(ProbingModule::DEFAULT_PROBING_INTERVAL < AsfMeasurements::MEASUREMENTS_LIFETIME,
               "ProbingModule::DEFAULT_PROBING_INTERVAL must be less than AsfMeasurements::MEASUREMENTS_LIFETIME");
@@ -91,7 +92,7 @@
       continue;
     }
 
-    FaceInfo* info = m_measurements.getFaceInfo(fibEntry, interest, hopFace);
+    FaceInfo* info = m_measurements.getFaceInfo(fibEntry, interest, hopFace.getId());
 
     // If no RTT has been recorded, probe this face
     if (info == nullptr || !info->hasSrttMeasurement()) {
@@ -189,6 +190,19 @@
   return dist(getGlobalRng());
 }
 
+void
+ProbingModule::setProbingInterval(size_t probingInterval)
+{
+  if (time::milliseconds(probingInterval) >= MIN_PROBING_INTERVAL) {
+    m_probingInterval = time::milliseconds(probingInterval);
+  }
+  else {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Probing interval should be >= "
+                                                + to_string(MIN_PROBING_INTERVAL.count())
+                                                + " milliseconds"));
+  }
+}
+
 } // namespace asf
 } // namespace fw
 } // namespace nfd
diff --git a/daemon/fw/asf-probing-module.hpp b/daemon/fw/asf-probing-module.hpp
index 055adb0..785abd8 100644
--- a/daemon/fw/asf-probing-module.hpp
+++ b/daemon/fw/asf-probing-module.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-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -55,6 +55,15 @@
   void
   afterForwardingProbe(const fib::Entry& fibEntry, const Interest& interest);
 
+  void
+  setProbingInterval(size_t probingInterval);
+
+  time::milliseconds
+  getProbingInterval() const
+  {
+    return m_probingInterval;
+  }
+
 private:
   // Used to associate FaceInfo with the face in a NextHop
   typedef std::pair<FaceInfo*, Face*> FaceInfoFacePair;
@@ -71,10 +80,11 @@
   getRandomNumber(double start, double end);
 
 public:
-  static constexpr time::seconds DEFAULT_PROBING_INTERVAL = time::seconds(60);
+  static constexpr time::milliseconds DEFAULT_PROBING_INTERVAL = time::milliseconds(60000);
+  static constexpr time::milliseconds MIN_PROBING_INTERVAL = time::milliseconds(1000);
 
 private:
-  time::seconds m_probingInterval;
+  time::milliseconds m_probingInterval;
   AsfMeasurements& m_measurements;
 };
 
diff --git a/daemon/fw/asf-strategy.cpp b/daemon/fw/asf-strategy.cpp
index 5e14e37..ab629f9 100644
--- a/daemon/fw/asf-strategy.cpp
+++ b/daemon/fw/asf-strategy.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2017,  Regents of the University of California,
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -42,29 +42,72 @@
   : Strategy(forwarder)
   , m_measurements(getMeasurements())
   , m_probing(m_measurements)
+  , m_maxSilentTimeouts(0)
   , m_retxSuppression(RETX_SUPPRESSION_INITIAL,
                       RetxSuppressionExponential::DEFAULT_MULTIPLIER,
                       RETX_SUPPRESSION_MAX)
 {
   ParsedInstanceName parsed = parseInstanceName(name);
   if (!parsed.parameters.empty()) {
-    BOOST_THROW_EXCEPTION(std::invalid_argument("AsfStrategy does not accept parameters"));
+    processParams(parsed.parameters);
   }
+
   if (parsed.version && *parsed.version != getStrategyName()[-1].toVersion()) {
     BOOST_THROW_EXCEPTION(std::invalid_argument(
       "AsfStrategy does not support version " + to_string(*parsed.version)));
   }
   this->setInstanceName(makeInstanceName(name, getStrategyName()));
+
+  NFD_LOG_DEBUG("Probing interval=" << m_probing.getProbingInterval()
+                << ", Num silent timeouts=" << m_maxSilentTimeouts);
 }
 
 const Name&
 AsfStrategy::getStrategyName()
 {
-  static Name strategyName("/localhost/nfd/strategy/asf/%FD%02");
+  static Name strategyName("/localhost/nfd/strategy/asf/%FD%03");
   return strategyName;
 }
 
 void
+AsfStrategy::processParams(const PartialName& parsed)
+{
+  for (const auto& component : parsed) {
+    std::string parsedStr(reinterpret_cast<const char*>(component.value()), component.value_size());
+    auto n = parsedStr.find("~");
+    if (n == std::string::npos) {
+      BOOST_THROW_EXCEPTION(std::invalid_argument("Format is <parameter>~<value>"));
+    }
+
+    auto f = parsedStr.substr(0, n);
+    auto s = parsedStr.substr(n + 1);
+    if (f == "probing-interval") {
+      m_probing.setProbingInterval(getParamValue(f, s));
+    }
+    else if (f == "n-silent-timeouts") {
+      m_maxSilentTimeouts = getParamValue(f, s);
+    }
+    else {
+      BOOST_THROW_EXCEPTION(std::invalid_argument("Parameter should be probing-interval or n-silent-timeouts"));
+    }
+  }
+}
+
+uint64_t
+AsfStrategy::getParamValue(const std::string& param, const std::string& value)
+{
+  try {
+    if (!value.empty() && value[0] == '-')
+      BOOST_THROW_EXCEPTION(boost::bad_lexical_cast());
+
+    return boost::lexical_cast<uint64_t>(value);
+  }
+  catch (const boost::bad_lexical_cast&) {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Value of " + param + " must be a non-negative integer"));
+  }
+}
+
+void
 AsfStrategy::afterReceiveInterest(const Face& inFace, const Interest& interest,
                                   const shared_ptr<pit::Entry>& pitEntry)
 {
@@ -132,7 +175,7 @@
   faceInfo->recordRtt(pitEntry, inFace);
 
   // Extend lifetime for measurements associated with Face
-  namespaceInfo->extendFaceInfoLifetime(*faceInfo, inFace);
+  namespaceInfo->extendFaceInfoLifetime(*faceInfo, inFace.getId());
 
   if (faceInfo->isTimeoutScheduled()) {
     faceInfo->cancelTimeoutEvent(data.getName());
@@ -169,11 +212,11 @@
     this->sendInterest(pitEntry, outFace, interest);
   }
 
-  FaceInfo& faceInfo = m_measurements.getOrCreateFaceInfo(fibEntry, interest, outFace);
+  FaceInfo& faceInfo = m_measurements.getOrCreateFaceInfo(fibEntry, interest, outFace.getId());
 
   // Refresh measurements since Face is being used for forwarding
   NamespaceInfo& namespaceInfo = m_measurements.getOrCreateNamespaceInfo(fibEntry, interest);
-  namespaceInfo.extendFaceInfoLifetime(faceInfo, outFace);
+  namespaceInfo.extendFaceInfoLifetime(faceInfo, outFace.getId());
 
   if (!faceInfo.isTimeoutScheduled()) {
     // Estimate and schedule timeout
@@ -251,7 +294,7 @@
       continue;
     }
 
-    FaceInfo* info = m_measurements.getFaceInfo(fibEntry, interest, hopFace);
+    FaceInfo* info = m_measurements.getFaceInfo(fibEntry, interest, hopFace.getId());
 
     if (info == nullptr) {
       FaceStats stats = {&hopFace,
@@ -278,10 +321,8 @@
 }
 
 void
-AsfStrategy::onTimeout(const Name& interestName, face::FaceId faceId)
+AsfStrategy::onTimeout(const Name& interestName, const face::FaceId faceId)
 {
-  NFD_LOG_TRACE("FaceId: " << faceId << " for " << interestName << " has timed-out");
-
   NamespaceInfo* namespaceInfo = m_measurements.getNamespaceInfo(interestName);
 
   if (namespaceInfo == nullptr) {
@@ -296,7 +337,23 @@
   }
 
   FaceInfo& faceInfo = it->second;
-  faceInfo.recordTimeout(interestName);
+
+  faceInfo.setNSilentTimeouts(faceInfo.getNSilentTimeouts() + 1);
+
+  if (faceInfo.getNSilentTimeouts() <= m_maxSilentTimeouts) {
+    NFD_LOG_TRACE("FaceId " << faceId << " for " << interestName << " has timed-out "
+                  << faceInfo.getNSilentTimeouts() << " time(s), ignoring");
+    // Extend lifetime for measurements associated with Face
+    namespaceInfo->extendFaceInfoLifetime(faceInfo, faceId);
+
+    if (faceInfo.isTimeoutScheduled()) {
+      faceInfo.cancelTimeoutEvent(interestName);
+    }
+  }
+  else {
+    NFD_LOG_TRACE("FaceId " << faceId << " for " << interestName << " has timed-out");
+    faceInfo.recordTimeout(interestName);
+  }
 }
 
 void
diff --git a/daemon/fw/asf-strategy.hpp b/daemon/fw/asf-strategy.hpp
index eb1c65c..b58e656 100644
--- a/daemon/fw/asf-strategy.hpp
+++ b/daemon/fw/asf-strategy.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-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -51,15 +51,15 @@
   getStrategyName();
 
 public: // triggers
-  virtual void
+  void
   afterReceiveInterest(const Face& inFace, const Interest& interest,
                        const shared_ptr<pit::Entry>& pitEntry) override;
 
-  virtual void
+  void
   beforeSatisfyInterest(const shared_ptr<pit::Entry>& pitEntry,
                         const Face& inFace, const Data& data) override;
 
-  virtual void
+  void
   afterReceiveNack(const Face& inFace, const lp::Nack& nack,
                    const shared_ptr<pit::Entry>& pitEntry) override;
 
@@ -75,14 +75,21 @@
   getBestFaceForForwarding(const fib::Entry& fibEntry, const Interest& interest, const Face& inFace);
 
   void
-  onTimeout(const Name& interestName, face::FaceId faceId);
+  onTimeout(const Name& interestName, const FaceId faceId);
 
   void
   sendNoRouteNack(const Face& inFace, const Interest& interest, const shared_ptr<pit::Entry>& pitEntry);
 
+  void
+  processParams(const PartialName& parsed);
+
+  static uint64_t
+  getParamValue(const std::string& param, const std::string& value);
+
 private:
   AsfMeasurements m_measurements;
   ProbingModule m_probing;
+  size_t m_maxSilentTimeouts;
 
 private:
   RetxSuppressionExponential m_retxSuppression;