face: use post instead of dispatch to avoid re-entrancy issues

This fixes a segfault in satisfyPendingInterests when Face::put is
called in DataCallback. Other functions are also safeguarded
against similar situations.

refs: #4596

Change-Id: I8d1564b0d28d1ee7b80c50712d888f874804823a
diff --git a/src/face.cpp b/src/face.cpp
index fdb6b29..cf1e69d 100644
--- a/src/face.cpp
+++ b/src/face.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2017 Regents of the University of California.
+ * Copyright (c) 2013-2018 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -183,7 +183,7 @@
   shared_ptr<Interest> interest2 = make_shared<Interest>(interest);
   interest2->getNonce();
 
-  IO_CAPTURE_WEAK_IMPL(dispatch) {
+  IO_CAPTURE_WEAK_IMPL(post) {
     impl->asyncExpressInterest(interest2, afterSatisfied, afterNacked, afterTimeout);
   } IO_CAPTURE_WEAK_IMPL_END
 
@@ -215,7 +215,7 @@
 void
 Face::put(Data data)
 {
-  IO_CAPTURE_WEAK_IMPL(dispatch) {
+  IO_CAPTURE_WEAK_IMPL(post) {
     impl->asyncPutData(data);
   } IO_CAPTURE_WEAK_IMPL_END
 }
@@ -223,7 +223,7 @@
 void
 Face::put(lp::Nack nack)
 {
-  IO_CAPTURE_WEAK_IMPL(dispatch) {
+  IO_CAPTURE_WEAK_IMPL(post) {
     impl->asyncPutNack(nack);
   } IO_CAPTURE_WEAK_IMPL_END
 }
diff --git a/tests/unit-tests/face.t.cpp b/tests/unit-tests/face.t.cpp
index e731c68..2412c66 100644
--- a/tests/unit-tests/face.t.cpp
+++ b/tests/unit-tests/face.t.cpp
@@ -299,6 +299,23 @@
   BOOST_CHECK(true);
 }
 
+BOOST_AUTO_TEST_CASE(DataCallbackPutData) // Bug 4596
+{
+  face.expressInterest(Interest("/localhost/notification/1"),
+                       [&] (const Interest& i, const Data& d) {
+                         face.put(*makeData("/chronosync/sampleDigest/1"));
+                       }, nullptr, nullptr);
+  advanceClocks(10_ms);
+  BOOST_CHECK_EQUAL(face.sentInterests.back().getName(), "/localhost/notification/1");
+
+  face.receive(Interest("/chronosync/sampleDigest"));
+  advanceClocks(10_ms);
+
+  face.put(*makeData("/localhost/notification/1"));
+  advanceClocks(10_ms);
+  BOOST_CHECK_EQUAL(face.sentData.back().getName(), "/chronosync/sampleDigest/1");
+}
+
 BOOST_AUTO_TEST_SUITE_END() // Consumer
 
 BOOST_AUTO_TEST_SUITE(Producer)