node: Ensure that processEvents blocks only if there are active events

Active events include:
- expressed Interests and awaiting for Data or Timeout
- set Interest filter

Change-Id: I75631ffca888a66ac7f31f38c7aa5fe204e2d510
diff --git a/include/ndn-cpp/node.hpp b/include/ndn-cpp/node.hpp
index b785be4..b28e91d 100644
--- a/include/ndn-cpp/node.hpp
+++ b/include/ndn-cpp/node.hpp
@@ -317,6 +317,7 @@
 private:
   ptr_lib::shared_ptr<boost::asio::io_service> ioService_;
   ptr_lib::shared_ptr<boost::asio::deadline_timer> pitTimeoutCheckTimer_;
+  bool pitTimeoutCheckTimerActive_;
   ptr_lib::shared_ptr<boost::asio::deadline_timer> processEventsTimeoutTimer_;
   
   ptr_lib::shared_ptr<Transport> transport_;
diff --git a/src/node.cpp b/src/node.cpp
index 1ae1d3b..dcbcf84 100644
--- a/src/node.cpp
+++ b/src/node.cpp
@@ -29,27 +29,23 @@
 uint64_t Node::RegisteredPrefix::lastRegisteredPrefixId_ = 0;
 
 Node::Node(const ptr_lib::shared_ptr<Transport>& transport)
-  : transport_(transport)
+  : pitTimeoutCheckTimerActive_(false)
+  , transport_(transport)
   , ndndIdFetcherInterest_(Name("/%C1.M.S.localhost/%C1.M.SRV/ndnd/KEY"), 4000.0)
 {
   ioService_ = ptr_lib::make_shared<boost::asio::io_service>();      
   pitTimeoutCheckTimer_      = ptr_lib::make_shared<boost::asio::deadline_timer>(boost::ref(*ioService_));
   processEventsTimeoutTimer_ = ptr_lib::make_shared<boost::asio::deadline_timer>(boost::ref(*ioService_));
-  
-  pitTimeoutCheckTimer_->expires_from_now(boost::posix_time::milliseconds(100));
-  pitTimeoutCheckTimer_->async_wait(func_lib::bind(&Node::checkPitExpire, this));
 }
 
 Node::Node(const ptr_lib::shared_ptr<Transport>& transport, const ptr_lib::shared_ptr<boost::asio::io_service> &ioService)
   : ioService_(ioService)
+  , pitTimeoutCheckTimerActive_(false)
   , transport_(transport)
   , ndndIdFetcherInterest_(Name("/%C1.M.S.localhost/%C1.M.SRV/ndnd/KEY"), 4000.0)
 {
   pitTimeoutCheckTimer_      = ptr_lib::make_shared<boost::asio::deadline_timer>(boost::ref(*ioService_));
   processEventsTimeoutTimer_ = ptr_lib::make_shared<boost::asio::deadline_timer>(boost::ref(*ioService_));
-  
-  pitTimeoutCheckTimer_->expires_from_now(boost::posix_time::milliseconds(100));
-  pitTimeoutCheckTimer_->async_wait(func_lib::bind(&Node::checkPitExpire, this));
 }
 
 uint64_t 
@@ -64,6 +60,12 @@
     (pendingInterestId, ptr_lib::shared_ptr<const Interest>(new Interest(interest)), onData, onTimeout)));
 
   transport_->send(interest.wireEncode());
+
+  if (!pitTimeoutCheckTimerActive_) {
+    pitTimeoutCheckTimerActive_ = true;
+    pitTimeoutCheckTimer_->expires_from_now(boost::posix_time::milliseconds(100));
+    pitTimeoutCheckTimer_->async_wait(func_lib::bind(&Node::checkPitExpire, this));
+  }
   
   return pendingInterestId;
 }
@@ -264,8 +266,19 @@
     }
   }
 
-  pitTimeoutCheckTimer_->expires_from_now(boost::posix_time::milliseconds(100));
-  pitTimeoutCheckTimer_->async_wait(func_lib::bind(&Node::checkPitExpire, this));
+  if (!pendingInterestTable_.empty()) {
+    // pitTimeoutCheckTimerActive = true;
+    pitTimeoutCheckTimer_->expires_from_now(boost::posix_time::milliseconds(100));
+    pitTimeoutCheckTimer_->async_wait(func_lib::bind(&Node::checkPitExpire, this));
+  }
+  else {
+    pitTimeoutCheckTimerActive_ = false;
+
+    if (registeredPrefixTable_.empty()) {
+      transport_->close();
+      processEventsTimeoutTimer_->cancel();
+    }
+  }
 }
 
 
@@ -302,7 +315,9 @@
 Node::shutdown()
 {
   transport_->close();
-  ioService_->stop();
+  pitTimeoutCheckTimer_->cancel();
+  processEventsTimeoutTimer_->cancel();
+  pitTimeoutCheckTimerActive_ = false;
 }
 
 Node::PendingInterestTable::iterator 
diff --git a/src/transport/unix-transport.cpp b/src/transport/unix-transport.cpp
index 0463916..2704df1 100644
--- a/src/transport/unix-transport.cpp
+++ b/src/transport/unix-transport.cpp
@@ -110,6 +110,11 @@
 
     if (error)
       {
+        if (error == boost::system::errc::operation_canceled) {
+          // async receive has been explicitly cancelled (e.g., socket close)
+          return;
+        }
+        
         socket_.close(); // closing at this point may not be that necessary
         transport_.isConnected_ = true;
         throw Transport::Error(error, "error while receiving data from socket");