face: Refactor internal PIT to use scheduled events

The commit also changes how face is paused when there are no pending
interests left and there are no registered prefixes with local
forwarder: data structures for pending interests and registered prefixes
will fire up a signal when they become empty.

Change-Id: I6b87a44b0c8bc766865a51962ecacaec85b4adad
Refs: #1372, #2518
diff --git a/tests/integrated/face.cpp b/tests/integrated/face.cpp
index 2666c48..b166edf 100644
--- a/tests/integrated/face.cpp
+++ b/tests/integrated/face.cpp
@@ -109,7 +109,7 @@
   void
   terminate(Face& face)
   {
-    face.shutdown();
+    face.getIoService().stop();
   }
 
   uint32_t nData;
@@ -191,7 +191,7 @@
   Face face;
   Face face2(face.getIoService());
   Scheduler scheduler(face.getIoService());
-  scheduler.scheduleEvent(time::milliseconds(300),
+  scheduler.scheduleEvent(time::seconds(4),
                           bind(&FacesFixture::terminate, this, ref(face)));
 
   regPrefixId = face.setInterestFilter("/Hello/World",
@@ -248,7 +248,7 @@
   Face face;
   Face face2(face.getIoService());
   Scheduler scheduler(face.getIoService());
-  scheduler.scheduleEvent(time::seconds(1),
+  scheduler.scheduleEvent(time::seconds(4),
                           bind(&FacesFixture::terminate, this, ref(face)));
 
   regPrefixId = face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"),
@@ -269,7 +269,7 @@
   Face face;
   Face face2(face.getIoService());
   Scheduler scheduler(face.getIoService());
-  scheduler.scheduleEvent(time::seconds(2),
+  scheduler.scheduleEvent(time::seconds(4),
                           bind(&FacesFixture::terminate, this, ref(face)));
 
   regPrefixId = face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"),
@@ -308,7 +308,7 @@
   void
   checkPrefix(bool doesExist)
   {
-    int result = std::system("nfd-status | grep /Hello/World >/dev/null");
+    int result = std::system("nfd-status -r | grep /Hello/World >/dev/null");
 
     if (doesExist) {
       BOOST_CHECK_EQUAL(result, 0);
@@ -323,7 +323,7 @@
 {
   Face face;
   Scheduler scheduler(face.getIoService());
-  scheduler.scheduleEvent(time::seconds(2),
+  scheduler.scheduleEvent(time::seconds(4),
                           bind(&FacesFixture::terminate, this, ref(face)));
 
   regPrefixId = face.setInterestFilter(InterestFilter("/Hello/World"),
@@ -340,7 +340,7 @@
          &face,
     regPrefixId)); // shouldn't match
 
-  scheduler.scheduleEvent(time::milliseconds(1500),
+  scheduler.scheduleEvent(time::milliseconds(2000),
                           bind(&FacesFixture2::checkPrefix, this, false));
 
   BOOST_REQUIRE_NO_THROW(face.processEvents());
@@ -402,7 +402,7 @@
          static_cast<UnregisterPrefixSuccessCallback>(bind(&FacesFixture3::onUnregSucceeded, this)),
          static_cast<UnregisterPrefixFailureCallback>(bind(&FacesFixture3::onUnregFailed, this))));
 
-  scheduler.scheduleEvent(time::milliseconds(1500),
+  scheduler.scheduleEvent(time::milliseconds(2500),
                           bind(&FacesFixture2::checkPrefix, this, false));
 
   BOOST_REQUIRE_NO_THROW(face.processEvents());
diff --git a/tests/unit-tests/face.t.cpp b/tests/unit-tests/face.t.cpp
index 3ded5ee..19e6739 100644
--- a/tests/unit-tests/face.t.cpp
+++ b/tests/unit-tests/face.t.cpp
@@ -70,16 +70,42 @@
                             BOOST_FAIL("Unexpected timeout");
                           }));
 
-  advanceClocks(time::milliseconds(10));
+  advanceClocks(time::milliseconds(1), 40);
 
   face->receive(*util::makeData("/Bye/World/!"));
   face->receive(*util::makeData("/Hello/World/!"));
 
-  advanceClocks(time::milliseconds(10), 100);
+  advanceClocks(time::milliseconds(1), 100);
 
   BOOST_CHECK_EQUAL(nData, 1);
   BOOST_CHECK_EQUAL(face->sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face->sentDatas.size(), 0);
+
+  face->expressInterest(Interest("/Hello/World/!", time::milliseconds(50)),
+                        [&] (const Interest& i, const Data& d) {
+                          BOOST_CHECK(i.getName().isPrefixOf(d.getName()));
+                          ++nData;
+                        },
+                        bind([] {
+                            BOOST_FAIL("Unexpected timeout");
+                          }));
+  advanceClocks(time::milliseconds(1), 40);
+  face->receive(*util::makeData("/Hello/World/!/1/xxxxx"));
+
+  advanceClocks(time::milliseconds(1), 100);
+
+  BOOST_CHECK_EQUAL(nData, 2);
+  BOOST_CHECK_EQUAL(face->sentInterests.size(), 2);
+  BOOST_CHECK_EQUAL(face->sentDatas.size(), 0);
+
+  size_t nTimeouts = 0;
+  face->expressInterest(Interest("/Hello/World/!/2", time::milliseconds(50)),
+                        bind([]{}),
+                        bind([&nTimeouts] {
+                            ++nTimeouts;
+                          }));
+  advanceClocks(time::milliseconds(10), 100);
+  BOOST_CHECK_EQUAL(nTimeouts, 1);
 }
 
 BOOST_AUTO_TEST_CASE(ExpressInterestTimeout)
@@ -367,7 +393,6 @@
   Interest i("/Hello/World");
   i.setNextHopFaceId(1000);
   i.setIncomingFaceId(2000);
-  i.getLocalControlHeader().setCachingPolicy(nfd::LocalControlHeader::CachingPolicy::NO_CACHE);
 
   face->expressInterest(i, bind([]{}), bind([]{}));
   advanceClocks(time::milliseconds(10));
@@ -376,7 +401,6 @@
   // only NextHopFaceId is allowed to go out
   BOOST_CHECK(face->sentInterests[0].getLocalControlHeader().hasNextHopFaceId());
   BOOST_CHECK(!face->sentInterests[0].getLocalControlHeader().hasIncomingFaceId());
-  BOOST_CHECK(!face->sentInterests[0].getLocalControlHeader().hasCachingPolicy());
   BOOST_CHECK_EQUAL(face->sentInterests[0].getNextHopFaceId(), 1000);
 }
 
@@ -386,11 +410,8 @@
                           [] (const InterestFilter&, const Interest& i) {
                             BOOST_CHECK(i.getLocalControlHeader().hasNextHopFaceId());
                             BOOST_CHECK(i.getLocalControlHeader().hasIncomingFaceId());
-                            BOOST_CHECK(i.getLocalControlHeader().hasCachingPolicy());
                             BOOST_CHECK_EQUAL(i.getNextHopFaceId(), 1000);
                             BOOST_CHECK_EQUAL(i.getIncomingFaceId(), 2000);
-                            BOOST_CHECK_EQUAL(i.getLocalControlHeader().getCachingPolicy(),
-                                              nfd::LocalControlHeader::CachingPolicy::NO_CACHE);
                           },
                           bind([]{}),
                           bind([] {
@@ -401,7 +422,6 @@
   Interest i("/Hello/World/!");
   i.setNextHopFaceId(1000);
   i.setIncomingFaceId(2000);
-  i.getLocalControlHeader().setCachingPolicy(nfd::LocalControlHeader::CachingPolicy::NO_CACHE);
 
   face->receive(i);
   advanceClocks(time::milliseconds(10));
@@ -411,8 +431,8 @@
 {
   shared_ptr<Data> d = util::makeData("/Bye/World/!");
   d->setIncomingFaceId(2000);
-  d->getLocalControlHeader().setNextHopFaceId(1000);
-  d->setCachingPolicy(nfd::LocalControlHeader::CachingPolicy::NO_CACHE);
+  d->getLocalControlHeader().setNextHopFaceId(1000); // setNextHopFaceId is intentionally
+                                                     // not exposed directly
 
   face->put(*d);
   advanceClocks(time::milliseconds(10));
@@ -420,9 +440,6 @@
   BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
   BOOST_CHECK(!face->sentDatas[0].getLocalControlHeader().hasNextHopFaceId());
   BOOST_CHECK(!face->sentDatas[0].getLocalControlHeader().hasIncomingFaceId());
-  BOOST_CHECK(face->sentDatas[0].getLocalControlHeader().hasCachingPolicy());
-  BOOST_CHECK_EQUAL(face->sentDatas[0].getCachingPolicy(),
-                    nfd::LocalControlHeader::CachingPolicy::NO_CACHE);
 }
 
 BOOST_AUTO_TEST_CASE(ReceiveDataWithLocalControlHeader)
@@ -431,9 +448,6 @@
                         [&] (const Interest& i, const Data& d) {
                           BOOST_CHECK(d.getLocalControlHeader().hasNextHopFaceId());
                           BOOST_CHECK(d.getLocalControlHeader().hasIncomingFaceId());
-                          BOOST_CHECK(d.getLocalControlHeader().hasCachingPolicy());
-                          BOOST_CHECK_EQUAL(d.getCachingPolicy(),
-                                            nfd::LocalControlHeader::CachingPolicy::NO_CACHE);
                           BOOST_CHECK_EQUAL(d.getIncomingFaceId(), 2000);
                           BOOST_CHECK_EQUAL(d.getLocalControlHeader().getNextHopFaceId(), 1000);
                         },
@@ -445,13 +459,25 @@
 
   shared_ptr<Data> d = util::makeData("/Hello/World/!");
   d->setIncomingFaceId(2000);
-  d->getLocalControlHeader().setNextHopFaceId(1000);
-  d->setCachingPolicy(nfd::LocalControlHeader::CachingPolicy::NO_CACHE);
+  d->getLocalControlHeader().setNextHopFaceId(1000); // setNextHopFaceId is intentionally
+                                                     // not exposed directly
   face->receive(*d);
 
   advanceClocks(time::milliseconds(10), 100);
 }
 
+BOOST_AUTO_TEST_CASE(DestructionWithoutCancellingPendingInterests) // Bug #2518
+{
+  face->expressInterest(Interest("/Hello/World", time::milliseconds(50)),
+                        bind([]{}), bind([]{}));
+  advanceClocks(time::milliseconds(10), 10);
+
+  face.reset();
+
+  advanceClocks(time::milliseconds(10), 10);
+  // should not segfault
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // tests