face: scoped pending Interest
refs #4316
Change-Id: I79f832ce599f09a4d5bbf8cd8c4656d677dd164c
diff --git a/ndn-cxx/face.cpp b/ndn-cxx/face.cpp
index bf832d6..6522e64 100644
--- a/ndn-cxx/face.cpp
+++ b/ndn-cxx/face.cpp
@@ -173,20 +173,20 @@
return m_transport;
}
-const PendingInterestId*
+PendingInterestHandle
Face::expressInterest(const Interest& interest,
const DataCallback& afterSatisfied,
const NackCallback& afterNacked,
const TimeoutCallback& afterTimeout)
{
- shared_ptr<Interest> interest2 = make_shared<Interest>(interest);
+ auto interest2 = make_shared<Interest>(interest);
interest2->getNonce();
IO_CAPTURE_WEAK_IMPL(post) {
impl->asyncExpressInterest(interest2, afterSatisfied, afterNacked, afterTimeout);
} IO_CAPTURE_WEAK_IMPL_END
- return reinterpret_cast<const PendingInterestId*>(interest2.get());
+ return PendingInterestHandle(*this, reinterpret_cast<const PendingInterestId*>(interest2.get()));
}
void
@@ -413,6 +413,12 @@
}
}
+PendingInterestHandle::PendingInterestHandle(Face& face, const PendingInterestId* id)
+ : CancelHandle([&face, id] { face.removePendingInterest(id); })
+ , m_id(id)
+{
+}
+
RegisteredPrefixHandle::RegisteredPrefixHandle(Face& face, const RegisteredPrefixId* id)
: CancelHandle([&face, id] { face.unregisterPrefix(id, nullptr, nullptr); })
, m_face(&face)
diff --git a/ndn-cxx/face.hpp b/ndn-cxx/face.hpp
index 05454ca..4f9b4b9 100644
--- a/ndn-cxx/face.hpp
+++ b/ndn-cxx/face.hpp
@@ -37,6 +37,7 @@
class Transport;
class PendingInterestId;
+class PendingInterestHandle;
class RegisteredPrefixId;
class RegisteredPrefixHandle;
class InterestFilterId;
@@ -231,8 +232,9 @@
* @param afterTimeout function to be invoked if neither Data nor Network NACK
* is returned within InterestLifetime
* @throw OversizedPacketError encoded Interest size exceeds MAX_NDN_PACKET_SIZE
+ * @return A handle for canceling the pending Interest.
*/
- const PendingInterestId*
+ PendingInterestHandle
expressInterest(const Interest& interest,
const DataCallback& afterSatisfied,
const NackCallback& afterNacked,
@@ -240,8 +242,7 @@
/**
* @brief Cancel previously expressed Interest
- *
- * @param pendingInterestId The ID returned from expressInterest.
+ * @param pendingInterestId a handle returned by expressInterest.
*/
void
removePendingInterest(const PendingInterestId* pendingInterestId);
@@ -363,7 +364,7 @@
* unsetInterestFilter will use the same credentials as original
* setInterestFilter/registerPrefix command
*
- * @param registeredPrefixId a handle returned from registerPrefix
+ * @param registeredPrefixId a handle returned by registerPrefix
*/
void
unsetInterestFilter(const RegisteredPrefixId* registeredPrefixId);
@@ -374,7 +375,7 @@
* This method always succeeds and will **NOT** send any request to the connected
* forwarder.
*
- * @param interestFilterId a handle returned from setInterestFilter.
+ * @param interestFilterId a handle returned by setInterestFilter.
*/
void
unsetInterestFilter(const InterestFilterId* interestFilterId);
@@ -388,7 +389,7 @@
* If registeredPrefixId was obtained using setInterestFilter, the corresponding
* InterestFilter will be unset too.
*
- * @param registeredPrefixId a handle returned from registerPrefix
+ * @param registeredPrefixId a handle returned by registerPrefix
* @param onSuccess Callback to be called when operation succeeds
* @param onFailure Callback to be called when operation fails
*/
@@ -534,16 +535,67 @@
shared_ptr<Impl> m_impl;
};
+/** \brief A handle of pending Interest.
+ *
+ * \code
+ * PendingInterestHandle hdl = face.expressInterest(interest, satisfyCb, nackCb, timeoutCb);
+ * hdl.cancel(); // cancel the pending Interest
+ * \endcode
+ *
+ * \warning Canceling the same pending Interest more than once, using same or different
+ * PendingInterestHandle or ScopedPendingInterestHandle, may trigger undefined behavior.
+ * \warning Canceling a pending Interest after the face has been destructed may trigger undefined
+ * behavior.
+ */
+class PendingInterestHandle : public detail::CancelHandle
+{
+public:
+ PendingInterestHandle() noexcept = default;
+
+ PendingInterestHandle(Face& face, const PendingInterestId* id);
+
+ operator const PendingInterestId*() const noexcept
+ {
+ return m_id;
+ }
+
+private:
+ const PendingInterestId* m_id = nullptr;
+};
+
+/** \brief A scoped handle of pending Interest.
+ *
+ * Upon destruction of this handle, the pending Interest is canceled automatically.
+ * Most commonly, the application keeps a ScopedPendingInterestHandle as a class member field,
+ * so that it can cleanup its pending Interest when the class instance is destructed.
+ *
+ * \code
+ * {
+ * ScopedPendingInterestHandle hdl = face.expressInterest(interest, satisfyCb, nackCb, timeoutCb);
+ * } // hdl goes out of scope, canceling the pending Interest
+ * \endcode
+ *
+ * \warning Canceling the same pending Interest more than once, using same or different
+ * PendingInterestHandle or ScopedPendingInterestHandle, may trigger undefined behavior.
+ * \warning Canceling a pending Interest after the face has been destructed may trigger undefined
+ * behavior.
+ */
+using ScopedPendingInterestHandle = detail::ScopedCancelHandle;
+
/** \brief A handle of registered prefix.
*/
class RegisteredPrefixHandle : public detail::CancelHandle
{
public:
- RegisteredPrefixHandle() = default;
+ RegisteredPrefixHandle() noexcept
+ {
+ // This could have been '= default', but there's compiler bug in Apple clang 9.0.0,
+ // see https://stackoverflow.com/a/44693603
+ }
RegisteredPrefixHandle(Face& face, const RegisteredPrefixId* id);
- operator const RegisteredPrefixId*() const
+ operator const RegisteredPrefixId*() const noexcept
{
return m_id;
}
@@ -600,11 +652,11 @@
class InterestFilterHandle : public detail::CancelHandle
{
public:
- InterestFilterHandle() = default;
+ InterestFilterHandle() noexcept = default;
InterestFilterHandle(Face& face, const InterestFilterId* id);
- operator const InterestFilterId*() const
+ operator const InterestFilterId*() const noexcept
{
return m_id;
}
diff --git a/tests/unit/face.t.cpp b/tests/unit/face.t.cpp
index 7f4ba07..43b4db5 100644
--- a/tests/unit/face.t.cpp
+++ b/tests/unit/face.t.cpp
@@ -267,7 +267,7 @@
} while (false));
}
-BOOST_AUTO_TEST_CASE(RemovePendingInterest)
+BOOST_AUTO_TEST_CASE(RemovePendingInterestId)
{
const PendingInterestId* interestId =
face.expressInterest(*makeInterest("/Hello/World", true, 50_ms),
@@ -286,6 +286,24 @@
BOOST_CHECK(true);
}
+BOOST_AUTO_TEST_CASE(CancelPendingInterestHandle)
+{
+ auto hdl = face.expressInterest(*makeInterest("/Hello/World", true, 50_ms),
+ bind([] { BOOST_FAIL("Unexpected data"); }),
+ bind([] { BOOST_FAIL("Unexpected nack"); }),
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+ advanceClocks(10_ms);
+
+ hdl.cancel();
+ advanceClocks(10_ms);
+
+ face.receive(*makeData("/Hello/World/%21"));
+ advanceClocks(200_ms, 5);
+
+ // avoid "test case [...] did not check any assertions" message from Boost.Test
+ BOOST_CHECK(true);
+}
+
BOOST_AUTO_TEST_CASE(RemoveAllPendingInterests)
{
face.expressInterest(*makeInterest("/Hello/World/0", false, 50_ms),