table: change ContentStore lookup API to allow async implementations

refs: #2411

Change-Id: Ifbb4179c34cf10a7913f8113a2f9238476d8eafa
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index 524fef5..3afdc00 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -91,23 +91,25 @@
   const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
   bool isPending = inRecords.begin() != inRecords.end();
   if (!isPending) {
-    // CS lookup
-    const Data* csMatch = m_cs.find(interest);
-    if (csMatch != 0) {
-      const_cast<Data*>(csMatch)->setIncomingFaceId(FACEID_CONTENT_STORE);
-      // XXX should we lookup PIT for other Interests that also match csMatch?
-
-      // set PIT straggler timer
-      this->setStragglerTimer(pitEntry, true, csMatch->getFreshnessPeriod());
-
-      // goto outgoing Data pipeline
-      this->onOutgoingData(*csMatch, inFace);
-      return;
-    }
+    m_cs.find(interest,
+              bind(&Forwarder::onContentStoreHit, this, ref(inFace), pitEntry, _1, _2),
+              bind(&Forwarder::onContentStoreMiss, this, ref(inFace), pitEntry, _1));
   }
+  else {
+    this->onContentStoreMiss(inFace, pitEntry, interest);
+  }
+}
 
+void
+Forwarder::onContentStoreMiss(const Face& inFace,
+                              shared_ptr<pit::Entry> pitEntry,
+                              const Interest& interest)
+{
+  NFD_LOG_DEBUG("onContentStoreMiss interest=" << interest.getName());
+
+  shared_ptr<Face> face = const_pointer_cast<Face>(inFace.shared_from_this());
   // insert InRecord
-  pitEntry->insertOrUpdateInRecord(inFace.shared_from_this(), interest);
+  pitEntry->insertOrUpdateInRecord(face, interest);
 
   // set PIT unsatisfy timer
   this->setUnsatisfyTimer(pitEntry);
@@ -121,6 +123,24 @@
 }
 
 void
+Forwarder::onContentStoreHit(const Face& inFace,
+                             shared_ptr<pit::Entry> pitEntry,
+                             const Interest& interest,
+                             const Data& data)
+{
+  NFD_LOG_DEBUG("onContentStoreMiss interest=" << interest.getName());
+
+  const_pointer_cast<Data>(data.shared_from_this())->setIncomingFaceId(FACEID_CONTENT_STORE);
+  // XXX should we lookup PIT for other Interests that also match csMatch?
+
+  // set PIT straggler timer
+  this->setStragglerTimer(pitEntry, true, data.getFreshnessPeriod());
+
+  // goto outgoing Data pipeline
+  this->onOutgoingData(data, *const_pointer_cast<Face>(inFace.shared_from_this()));
+}
+
+void
 Forwarder::onInterestLoop(Face& inFace, const Interest& interest,
                           shared_ptr<pit::Entry> pitEntry)
 {
diff --git a/daemon/fw/forwarder.hpp b/daemon/fw/forwarder.hpp
index a753f15..aa5d19d 100644
--- a/daemon/fw/forwarder.hpp
+++ b/daemon/fw/forwarder.hpp
@@ -110,6 +110,17 @@
   VIRTUAL_WITH_TESTS void
   onIncomingInterest(Face& inFace, const Interest& interest);
 
+  /** \brief Content Store miss pipeline
+  */
+  void
+  onContentStoreMiss(const Face& inFace, shared_ptr<pit::Entry> pitEntry, const Interest& interest);
+
+  /** \brief Content Store hit pipeline
+  */
+  void
+  onContentStoreHit(const Face& inFace, shared_ptr<pit::Entry> pitEntry,
+                    const Interest& interest, const Data& data);
+
   /** \brief Interest loop pipeline
    */
   VIRTUAL_WITH_TESTS void
diff --git a/daemon/table/cs.cpp b/daemon/table/cs.cpp
index 2226335..a63d33e 100644
--- a/daemon/table/cs.cpp
+++ b/daemon/table/cs.cpp
@@ -97,9 +97,14 @@
   return true;
 }
 
-const Data*
-Cs::find(const Interest& interest) const
+void
+Cs::find(const Interest& interest,
+         const HitCallback& hitCallback,
+         const MissCallback& missCallback) const
 {
+  BOOST_ASSERT(static_cast<bool>(hitCallback));
+  BOOST_ASSERT(static_cast<bool>(missCallback));
+
   const Name& prefix = interest.getName();
   bool isRightmost = interest.getChildSelector() == 1;
   NFD_LOG_DEBUG("find " << prefix << (isRightmost ? " R" : " L"));
@@ -120,10 +125,11 @@
 
   if (match == last) {
     NFD_LOG_DEBUG("  no-match");
-    return nullptr;
+    missCallback(interest);
+    return;
   }
   NFD_LOG_DEBUG("  matching " << match->getName());
-  return &match->getData();
+  hitCallback(interest, match->getData());
 }
 
 TableIt
diff --git a/daemon/table/cs.hpp b/daemon/table/cs.hpp
index 77a4474..5e3bfbe 100644
--- a/daemon/table/cs.hpp
+++ b/daemon/table/cs.hpp
@@ -68,10 +68,20 @@
   bool
   insert(const Data& data, bool isUnsolicited = false);
 
+  typedef std::function<void(const Interest&, const Data& data)> HitCallback;
+  typedef std::function<void(const Interest&)> MissCallback;
+
   /** \brief finds the best matching Data packet
+   *  \param interest the Interest for lookup
+   *  \param hitCallback a callback if a match is found; must not be empty
+   *  \param missCallback a callback if there's no match; must not be empty
+   *  \note A lookup invokes either callback exactly once.
+   *        The callback may be invoked either before or after find() returns
    */
-  const Data*
-  find(const Interest& interest) const;
+  void
+  find(const Interest& interest,
+       const HitCallback& hitCallback,
+       const MissCallback& missCallback) const;
 
   void
   erase(const Name& exactName)
diff --git a/tests/daemon/table/cs.t.cpp b/tests/daemon/table/cs.t.cpp
index 43c65af..521df2d 100644
--- a/tests/daemon/table/cs.t.cpp
+++ b/tests/daemon/table/cs.t.cpp
@@ -28,6 +28,8 @@
 
 #include "tests/test-common.hpp"
 
+#define CHECK_CS_FIND(expected) find([&] (uint32_t found) { BOOST_CHECK_EQUAL(expected, found); });
+
 namespace nfd {
 namespace cs {
 namespace tests {
@@ -60,20 +62,15 @@
     return *m_interest;
   }
 
-  uint32_t
-  find()
+  void
+  find(const std::function<void(uint32_t)>& check)
   {
-    // m_cs.dump();
-
-    const Data* found = m_cs.find(*m_interest);
-    if (found == nullptr) {
-      return 0;
-    }
-    const Block& content = found->getContent();
-    if (content.value_size() != sizeof(uint32_t)) {
-      return 0;
-    }
-    return *reinterpret_cast<const uint32_t*>(content.value());
+    m_cs.find(*m_interest,
+              [&] (const Interest& interest, const Data& data) {
+                  const Block& content = data.getContent();
+                  uint32_t found = *reinterpret_cast<const uint32_t*>(content.value());
+                  check(found); },
+              bind([&] { check(0); }));
   }
 
 protected:
@@ -88,7 +85,7 @@
   insert(1, "ndn:/");
 
   startInterest("ndn:/");
-  BOOST_CHECK_EQUAL(find(), 1);
+  CHECK_CS_FIND(1);
 }
 
 BOOST_AUTO_TEST_CASE(EmptyInterestName)
@@ -96,7 +93,7 @@
   insert(1, "ndn:/A");
 
   startInterest("ndn:/");
-  BOOST_CHECK_EQUAL(find(), 1);
+  CHECK_CS_FIND(1);
 }
 
 BOOST_AUTO_TEST_CASE(ExactName)
@@ -108,7 +105,7 @@
   insert(5, "ndn:/D");
 
   startInterest("ndn:/A");
-  BOOST_CHECK_EQUAL(find(), 2);
+  CHECK_CS_FIND(2);
 }
 
 BOOST_AUTO_TEST_CASE(FullName)
@@ -117,10 +114,10 @@
   Name n2 = insert(2, "ndn:/A");
 
   startInterest(n1);
-  BOOST_CHECK_EQUAL(find(), 1);
+  CHECK_CS_FIND(1);
 
   startInterest(n2);
-  BOOST_CHECK_EQUAL(find(), 2);
+  CHECK_CS_FIND(2);
 }
 
 BOOST_AUTO_TEST_CASE(Leftmost)
@@ -133,7 +130,7 @@
   insert(6, "ndn:/C");
 
   startInterest("ndn:/B");
-  BOOST_CHECK_EQUAL(find(), 2);
+  CHECK_CS_FIND(2);
 }
 
 BOOST_AUTO_TEST_CASE(Rightmost)
@@ -147,7 +144,7 @@
 
   startInterest("ndn:/B")
     .setChildSelector(1);
-  BOOST_CHECK_EQUAL(find(), 4);
+  CHECK_CS_FIND(4);
 }
 
 BOOST_AUTO_TEST_CASE(MinSuffixComponents)
@@ -161,35 +158,35 @@
 
   startInterest("ndn:/")
     .setMinSuffixComponents(0);
-  BOOST_CHECK_EQUAL(find(), 1);
+  CHECK_CS_FIND(1);
 
   startInterest("ndn:/")
     .setMinSuffixComponents(1);
-  BOOST_CHECK_EQUAL(find(), 1);
+  CHECK_CS_FIND(1);
 
   startInterest("ndn:/")
     .setMinSuffixComponents(2);
-  BOOST_CHECK_EQUAL(find(), 2);
+  CHECK_CS_FIND(2);
 
   startInterest("ndn:/")
     .setMinSuffixComponents(3);
-  BOOST_CHECK_EQUAL(find(), 3);
+  CHECK_CS_FIND(3);
 
   startInterest("ndn:/")
     .setMinSuffixComponents(4);
-  BOOST_CHECK_EQUAL(find(), 4);
+  CHECK_CS_FIND(4);
 
   startInterest("ndn:/")
     .setMinSuffixComponents(5);
-  BOOST_CHECK_EQUAL(find(), 5);
+  CHECK_CS_FIND(5);
 
   startInterest("ndn:/")
     .setMinSuffixComponents(6);
-  BOOST_CHECK_EQUAL(find(), 6);
+  CHECK_CS_FIND(6);
 
   startInterest("ndn:/")
     .setMinSuffixComponents(7);
-  BOOST_CHECK_EQUAL(find(), 0);
+  CHECK_CS_FIND(0);
 }
 
 BOOST_AUTO_TEST_CASE(MaxSuffixComponents)
@@ -204,42 +201,42 @@
   startInterest("ndn:/")
     .setChildSelector(1)
     .setMaxSuffixComponents(0);
-  BOOST_CHECK_EQUAL(find(), 0);
+  CHECK_CS_FIND(0);
 
   startInterest("ndn:/")
     .setChildSelector(1)
     .setMaxSuffixComponents(1);
-  BOOST_CHECK_EQUAL(find(), 1);
+  CHECK_CS_FIND(1);
 
   startInterest("ndn:/")
     .setChildSelector(1)
     .setMaxSuffixComponents(2);
-  BOOST_CHECK_EQUAL(find(), 2);
+  CHECK_CS_FIND(2);
 
   startInterest("ndn:/")
     .setChildSelector(1)
     .setMaxSuffixComponents(3);
-  BOOST_CHECK_EQUAL(find(), 3);
+  CHECK_CS_FIND(3);
 
   startInterest("ndn:/")
     .setChildSelector(1)
     .setMaxSuffixComponents(4);
-  BOOST_CHECK_EQUAL(find(), 4);
+  CHECK_CS_FIND(4);
 
   startInterest("ndn:/")
     .setChildSelector(1)
     .setMaxSuffixComponents(5);
-  BOOST_CHECK_EQUAL(find(), 5);
+  CHECK_CS_FIND(5);
 
   startInterest("ndn:/")
     .setChildSelector(1)
     .setMaxSuffixComponents(6);
-  BOOST_CHECK_EQUAL(find(), 6);
+  CHECK_CS_FIND(6);
 
   startInterest("ndn:/")
     .setChildSelector(1)
     .setMaxSuffixComponents(7);
-  BOOST_CHECK_EQUAL(find(), 6);
+  CHECK_CS_FIND(6);
 }
 
 BOOST_AUTO_TEST_CASE(DigestOrder)
@@ -248,14 +245,19 @@
   insert(2, "ndn:/A");
   // We don't know which comes first, but there must be some order
 
+  int leftmost = 0, rightmost = 0;
   startInterest("ndn:/A")
     .setChildSelector(0);
-  uint32_t leftmost = find();
-
+  m_cs.find(*m_interest,
+            [&leftmost] (const Interest& interest, const Data& data) {
+              leftmost = *reinterpret_cast<const uint32_t*>(data.getContent().value());},
+            bind([] { BOOST_CHECK(false); }));
   startInterest("ndn:/A")
     .setChildSelector(1);
-  uint32_t rightmost = find();
-
+  m_cs.find(*m_interest,
+            [&rightmost] (const Interest, const Data& data) {
+              rightmost = *reinterpret_cast<const uint32_t*>(data.getContent().value()); },
+            bind([] { BOOST_CHECK(false); }));
   BOOST_CHECK_NE(leftmost, rightmost);
 }
 
@@ -278,12 +280,12 @@
   startInterest("ndn:/A")
     .setChildSelector(0)
     .setExclude(excludeDigest);
-  BOOST_CHECK_EQUAL(find(), 3);
+  CHECK_CS_FIND(3);
 
   startInterest("ndn:/A")
     .setChildSelector(1)
     .setExclude(excludeDigest);
-  BOOST_CHECK_EQUAL(find(), 3);
+  CHECK_CS_FIND(3);
 
   Exclude excludeGeneric;
   excludeGeneric.excludeAfter(name::Component(static_cast<uint8_t*>(nullptr), 0));
@@ -291,14 +293,12 @@
   startInterest("ndn:/A")
     .setChildSelector(0)
     .setExclude(excludeGeneric);
-  int found1 = find();
-  BOOST_CHECK(found1 == 1 || found1 == 2);
+  find([] (uint32_t found) { BOOST_CHECK(found == 1 || found == 2); });
 
   startInterest("ndn:/A")
     .setChildSelector(1)
     .setExclude(excludeGeneric);
-  int found2 = find();
-  BOOST_CHECK(found2 == 1 || found2 == 2);
+  find([] (uint32_t found) { BOOST_CHECK(found == 1 || found == 2); });
 
   Exclude exclude2 = excludeGeneric;
   exclude2.excludeOne(n2.get(-1));
@@ -306,12 +306,12 @@
   startInterest("ndn:/A")
     .setChildSelector(0)
     .setExclude(exclude2);
-  BOOST_CHECK_EQUAL(find(), 1);
+  CHECK_CS_FIND(1);
 
   startInterest("ndn:/A")
     .setChildSelector(1)
     .setExclude(exclude2);
-  BOOST_CHECK_EQUAL(find(), 1);
+  CHECK_CS_FIND(1);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
@@ -325,7 +325,9 @@
   dataA->wireEncode();
   BOOST_CHECK_EQUAL(cs.insert(*dataA), false);
 
-  BOOST_CHECK(cs.find(Interest("ndn:/A")) == nullptr);
+  cs.find(Interest("ndn:/A"),
+          bind([] { BOOST_CHECK(false); }),
+          bind([] { BOOST_CHECK(true); }));
 }
 
 BOOST_FIXTURE_TEST_CASE(Evict, UnitTestTimeFixture)
@@ -355,7 +357,9 @@
   dataD->wireEncode();
   cs.insert(*dataD);
   BOOST_CHECK_EQUAL(cs.size(), 3);
-  BOOST_CHECK(cs.find(Interest("ndn:/C")) == nullptr);
+  cs.find(Interest("ndn:/C"),
+          bind([] { BOOST_CHECK(false); }),
+          bind([] { BOOST_CHECK(true); }));
 
   // evict stale
   shared_ptr<Data> dataE = makeData("ndn:/E");
@@ -363,7 +367,9 @@
   dataE->wireEncode();
   cs.insert(*dataE);
   BOOST_CHECK_EQUAL(cs.size(), 3);
-  BOOST_CHECK(cs.find(Interest("ndn:/B")) == nullptr);
+  cs.find(Interest("ndn:/B"),
+          bind([] { BOOST_CHECK(false); }),
+          bind([] { BOOST_CHECK(true); }));
 
   // evict fifo
   shared_ptr<Data> dataF = makeData("ndn:/F");
@@ -371,7 +377,9 @@
   dataF->wireEncode();
   cs.insert(*dataF);
   BOOST_CHECK_EQUAL(cs.size(), 3);
-  BOOST_CHECK(cs.find(Interest("ndn:/A")) == nullptr);
+  cs.find(Interest("ndn:/A"),
+          bind([] { BOOST_CHECK(false); }),
+          bind([] { BOOST_CHECK(true); }));
 }
 
 BOOST_FIXTURE_TEST_CASE(Refresh, UnitTestTimeFixture)
@@ -400,9 +408,17 @@
   dataB2->wireEncode();
   cs.insert(*dataB2);
   BOOST_CHECK_EQUAL(cs.size(), 3);
-  BOOST_CHECK(cs.find(Interest("ndn:/A")) != nullptr);
-  BOOST_CHECK(cs.find(Interest("ndn:/B")) != nullptr);
-  BOOST_CHECK(cs.find(Interest("ndn:/C")) != nullptr);
+  cs.find(Interest("ndn:/A"),
+          bind([] { BOOST_CHECK(true); }),
+          bind([] { BOOST_CHECK(false); }));
+
+  cs.find(Interest("ndn:/B"),
+          bind([] { BOOST_CHECK(true); }),
+          bind([] { BOOST_CHECK(false); }));
+
+  cs.find(Interest("ndn:/C"),
+          bind([] { BOOST_CHECK(true); }),
+          bind([] { BOOST_CHECK(false); }));
 
   // evict dataC stale
   shared_ptr<Data> dataD = makeData("ndn:/D");
@@ -410,7 +426,9 @@
   dataD->wireEncode();
   cs.insert(*dataD);
   BOOST_CHECK_EQUAL(cs.size(), 3);
-  BOOST_CHECK(cs.find(Interest("ndn:/C")) == nullptr);
+  cs.find(Interest("ndn:/C"),
+          bind([] { BOOST_CHECK(false); }),
+          bind([] { BOOST_CHECK(true); }));
 }
 
 BOOST_AUTO_TEST_CASE(Enumeration)
diff --git a/tests/other/cs-benchmark.cpp b/tests/other/cs-benchmark.cpp
index 78f7c1b..fcd9d20 100644
--- a/tests/other/cs-benchmark.cpp
+++ b/tests/other/cs-benchmark.cpp
@@ -53,6 +53,12 @@
     return time::duration_cast<time::microseconds>(t2 - t1);
   }
 
+  void
+  find(const Interest& interest)
+  {
+    cs.find(interest, bind([]{}), bind([]{}));
+  }
+
 protected:
   typedef std::function<Name(size_t)> NameGenerator;
 
@@ -122,7 +128,7 @@
   time::microseconds d = timedRun([&] {
     for (size_t j = 0; j < REPEAT; ++j) {
       for (size_t i = 0; i < N_WORKLOAD; ++i) {
-        cs.find(*interestWorkload[i]);
+        find(*interestWorkload[i]);
         cs.insert(*dataWorkload[j][i], false);
       }
     }
@@ -146,7 +152,7 @@
     for (size_t j = 0; j < REPEAT; ++j) {
       for (size_t i = 0; i < N_WORKLOAD; ++i) {
         cs.insert(*dataWorkload[j][i], false);
-        cs.find(*interestWorkload[i]);
+        find(*interestWorkload[i]);
       }
     }
   });
@@ -175,7 +181,7 @@
   time::microseconds d = timedRun([&] {
     for (size_t j = 0; j < REPEAT; ++j) {
       for (const auto& interest : interestWorkload) {
-        cs.find(*interest);
+        find(*interest);
       }
     }
   });
@@ -204,7 +210,7 @@
   time::microseconds d = timedRun([&] {
     for (size_t j = 0; j < REPEAT; ++j) {
       for (const auto& interest : interestWorkload) {
-        cs.find(*interest);
+        find(*interest);
       }
     }
   });