face: don't use memory address as PendingInterestId

Using memory address as PendingInterestId may cause false
removal of PendingInterest record if the memory allocator places
a different Interest at the same position in the heap.

refs #2865

Change-Id: Icf4d11509ad5c2acd56f180a333a68aa96f31768
diff --git a/ndn-cxx/face.cpp b/ndn-cxx/face.cpp
index 6522e64..5d4cb65 100644
--- a/ndn-cxx/face.cpp
+++ b/ndn-cxx/face.cpp
@@ -179,14 +179,16 @@
                       const NackCallback& afterNacked,
                       const TimeoutCallback& afterTimeout)
 {
+  auto id = m_impl->generatePendingInterestId();
+
   auto interest2 = make_shared<Interest>(interest);
   interest2->getNonce();
 
   IO_CAPTURE_WEAK_IMPL(post) {
-    impl->asyncExpressInterest(interest2, afterSatisfied, afterNacked, afterTimeout);
+    impl->asyncExpressInterest(id, interest2, afterSatisfied, afterNacked, afterTimeout);
   } IO_CAPTURE_WEAK_IMPL_END
 
-  return PendingInterestHandle(*this, reinterpret_cast<const PendingInterestId*>(interest2.get()));
+  return PendingInterestHandle(*this, id);
 }
 
 void
diff --git a/ndn-cxx/impl/face-impl.hpp b/ndn-cxx/impl/face-impl.hpp
index 2f68557..14599ab 100644
--- a/ndn-cxx/impl/face-impl.hpp
+++ b/ndn-cxx/impl/face-impl.hpp
@@ -67,6 +67,7 @@
   Impl(Face& face)
     : m_face(face)
     , m_scheduler(m_face.getIoService())
+    , m_lastPendingInterestId(0)
   {
     auto postOnEmptyPitOrNoRegisteredPrefixes = [this] {
       this->m_face.getIoService().post([this] { this->onEmptyPitOrNoRegisteredPrefixes(); });
@@ -81,8 +82,16 @@
   }
 
 public: // consumer
+  const PendingInterestId*
+  generatePendingInterestId()
+  {
+    auto id = ++m_lastPendingInterestId;
+    return reinterpret_cast<const PendingInterestId*>(id);
+  }
+
   void
-  asyncExpressInterest(shared_ptr<const Interest> interest,
+  asyncExpressInterest(const PendingInterestId* id,
+                       shared_ptr<const Interest> interest,
                        const DataCallback& afterSatisfied,
                        const NackCallback& afterNacked,
                        const TimeoutCallback& afterTimeout)
@@ -92,7 +101,7 @@
 
     const Interest& interest2 = *interest;
     auto i = m_pendingInterestTable.insert(make_shared<PendingInterest>(
-      std::move(interest), afterSatisfied, afterNacked, afterTimeout, ref(m_scheduler))).first;
+      id, std::move(interest), afterSatisfied, afterNacked, afterTimeout, ref(m_scheduler))).first;
     // In dispatchInterest, an InterestCallback may respond with Data right away and delete
     // the PendingInterestTable entry. shared_ptr is retained to ensure PendingInterest instance
     // remains valid in this case.
@@ -418,6 +427,7 @@
   util::Scheduler m_scheduler;
   util::scheduler::ScopedEventId m_processEventsTimeoutEvent;
 
+  std::atomic_uintptr_t m_lastPendingInterestId;
   PendingInterestTable m_pendingInterestTable;
   InterestFilterTable m_interestFilterTable;
   RegisteredPrefixTable m_registeredPrefixTable;
diff --git a/ndn-cxx/impl/pending-interest.hpp b/ndn-cxx/impl/pending-interest.hpp
index 6495f56..ac14304 100644
--- a/ndn-cxx/impl/pending-interest.hpp
+++ b/ndn-cxx/impl/pending-interest.hpp
@@ -31,6 +31,11 @@
 namespace ndn {
 
 /**
+ * @brief Opaque type to identify a PendingInterest
+ */
+class PendingInterestId;
+
+/**
  * @brief Indicates where a pending Interest came from
  */
 enum class PendingInterestOrigin
@@ -63,19 +68,15 @@
    *
    * The timeout is set based on the current time and InterestLifetime.
    * This class will invoke the timeout callback unless the record is deleted before timeout.
-   *
-   * @param interest the Interest
-   * @param dataCallback invoked when matching Data packet is received
-   * @param nackCallback invoked when Nack matching Interest is received
-   * @param timeoutCallback invoked when Interest times out
-   * @param scheduler Scheduler for scheduling the timeout event
    */
-  PendingInterest(shared_ptr<const Interest> interest,
+  PendingInterest(const PendingInterestId* id,
+                  shared_ptr<const Interest> interest,
                   const DataCallback& dataCallback,
                   const NackCallback& nackCallback,
                   const TimeoutCallback& timeoutCallback,
                   Scheduler& scheduler)
-    : m_interest(std::move(interest))
+    : m_id(id)
+    , m_interest(std::move(interest))
     , m_origin(PendingInterestOrigin::APP)
     , m_dataCallback(dataCallback)
     , m_nackCallback(nackCallback)
@@ -92,16 +93,20 @@
    * @param scheduler Scheduler for scheduling the timeout event
    */
   PendingInterest(shared_ptr<const Interest> interest, Scheduler& scheduler)
-    : m_interest(std::move(interest))
+    : m_id(nullptr)
+    , m_interest(std::move(interest))
     , m_origin(PendingInterestOrigin::FORWARDER)
     , m_nNotNacked(0)
   {
     scheduleTimeoutEvent(scheduler);
   }
 
-  /**
-   * @brief Get the Interest
-   */
+  const PendingInterestId*
+  getId() const
+  {
+    return m_id;
+  }
+
   shared_ptr<const Interest>
   getInterest() const
   {
@@ -199,6 +204,7 @@
   }
 
 private:
+  const PendingInterestId* m_id;
   shared_ptr<const Interest> m_interest;
   PendingInterestOrigin m_origin;
   DataCallback m_dataCallback;
@@ -211,11 +217,6 @@
 };
 
 /**
- * @brief Opaque type to identify a PendingInterest
- */
-class PendingInterestId;
-
-/**
  * @brief Functor to match PendingInterestId
  */
 class MatchPendingInterestId
@@ -230,7 +231,7 @@
   bool
   operator()(const shared_ptr<const PendingInterest>& pendingInterest) const
   {
-    return reinterpret_cast<const PendingInterestId*>(pendingInterest->getInterest().get()) == m_id;
+    return pendingInterest->getId() == m_id;
   }
 
 private: