face: Stop using shared_ptr to manage acceptors

This commit also includes a major cleanup of all channels.

Change-Id: I10db9709e0cba6a0691a86482c60b5dbb2956f68
Refs: #2613
diff --git a/daemon/face/tcp-channel.cpp b/daemon/face/tcp-channel.cpp
index 0a6ce78..637cfa3 100644
--- a/daemon/face/tcp-channel.cpp
+++ b/daemon/face/tcp-channel.cpp
@@ -24,6 +24,7 @@
  */
 
 #include "tcp-channel.hpp"
+#include "tcp-face.hpp"
 #include "core/global-io.hpp"
 
 namespace nfd {
@@ -33,14 +34,10 @@
 using namespace boost::asio;
 
 TcpChannel::TcpChannel(const tcp::Endpoint& localEndpoint)
-  : m_localEndpoint(localEndpoint)
-  , m_isListening(false)
+  : m_acceptor(getGlobalIoService())
+  , m_localEndpoint(localEndpoint)
 {
-  this->setUri(FaceUri(localEndpoint));
-}
-
-TcpChannel::~TcpChannel()
-{
+  setUri(FaceUri(m_localEndpoint));
 }
 
 void
@@ -48,24 +45,21 @@
                    const ConnectFailedCallback& onAcceptFailed,
                    int backlog/* = tcp::acceptor::max_connections*/)
 {
-  m_acceptor = make_shared<ip::tcp::acceptor>(ref(getGlobalIoService()));
-  m_acceptor->open(m_localEndpoint.protocol());
-  m_acceptor->set_option(ip::tcp::acceptor::reuse_address(true));
+  if (isListening()) {
+    NFD_LOG_WARN("[" << m_localEndpoint << "] Already listening");
+    return;
+  }
+
+  m_acceptor.open(m_localEndpoint.protocol());
+  m_acceptor.set_option(ip::tcp::acceptor::reuse_address(true));
   if (m_localEndpoint.address().is_v6())
-    {
-      m_acceptor->set_option(ip::v6_only(true));
-    }
-  m_acceptor->bind(m_localEndpoint);
-  m_acceptor->listen(backlog);
+    m_acceptor.set_option(ip::v6_only(true));
 
-  shared_ptr<ip::tcp::socket> clientSocket =
-    make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
-  m_acceptor->async_accept(*clientSocket,
-                           bind(&TcpChannel::handleSuccessfulAccept, this, _1,
-                                clientSocket,
-                                onFaceCreated, onAcceptFailed));
+  m_acceptor.bind(m_localEndpoint);
+  m_acceptor.listen(backlog);
 
-  m_isListening = true;
+  // start accepting connections
+  accept(onFaceCreated, onAcceptFailed);
 }
 
 void
@@ -74,9 +68,9 @@
                     const TcpChannel::ConnectFailedCallback& onConnectFailed,
                     const time::seconds& timeout/* = time::seconds(4)*/)
 {
-  ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
-  if (i != m_channelFaces.end()) {
-    onFaceCreated(i->second);
+  auto it = m_channelFaces.find(remoteEndpoint);
+  if (it != m_channelFaces.end()) {
+    onFaceCreated(it->second);
     return;
   }
 
@@ -84,10 +78,11 @@
     make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
 
   scheduler::EventId connectTimeoutEvent = scheduler::schedule(timeout,
-      bind(&TcpChannel::handleFailedConnect, this, clientSocket, onConnectFailed));
+      bind(&TcpChannel::handleConnectTimeout, this, clientSocket, onConnectFailed));
 
   clientSocket->async_connect(remoteEndpoint,
-                              bind(&TcpChannel::handleSuccessfulConnect, this, _1,
+                              bind(&TcpChannel::handleConnect, this,
+                                   boost::asio::placeholders::error,
                                    clientSocket, connectTimeoutEvent,
                                    onFaceCreated, onConnectFailed));
 }
@@ -107,22 +102,25 @@
 
   shared_ptr<Face> face;
 
-  ChannelFaceMap::iterator faceMapPos = m_channelFaces.find(remoteEndpoint);
-  if (faceMapPos == m_channelFaces.end())
+  auto it = m_channelFaces.find(remoteEndpoint);
+  if (it == m_channelFaces.end())
     {
       if (socket->local_endpoint().address().is_loopback())
         face = make_shared<TcpLocalFace>(socket, isOnDemand);
       else
         face = make_shared<TcpFace>(socket, isOnDemand);
 
-      face->onFail.connectSingleShot(bind(&TcpChannel::afterFaceFailed, this, remoteEndpoint));
+      face->onFail.connectSingleShot([this, remoteEndpoint] (const std::string&) {
+        NFD_LOG_TRACE("Erasing " << remoteEndpoint << " from channel face map");
+        m_channelFaces.erase(remoteEndpoint);
+      });
 
       m_channelFaces[remoteEndpoint] = face;
     }
   else
     {
       // we've already created a a face for this endpoint, just reuse it
-      face = faceMapPos->second;
+      face = it->second;
 
       boost::system::error_code error;
       socket->shutdown(ip::tcp::socket::shutdown_both, error);
@@ -135,50 +133,47 @@
 }
 
 void
-TcpChannel::afterFaceFailed(tcp::Endpoint &remoteEndpoint)
+TcpChannel::accept(const FaceCreatedCallback& onFaceCreated,
+                   const ConnectFailedCallback& onAcceptFailed)
 {
-  NFD_LOG_DEBUG("afterFaceFailed: " << remoteEndpoint);
-  m_channelFaces.erase(remoteEndpoint);
+  auto socket = make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
+
+  m_acceptor.async_accept(*socket,
+                          bind(&TcpChannel::handleAccept, this,
+                               boost::asio::placeholders::error,
+                               socket, onFaceCreated, onAcceptFailed));
 }
 
 void
-TcpChannel::handleSuccessfulAccept(const boost::system::error_code& error,
-                                   const shared_ptr<boost::asio::ip::tcp::socket>& socket,
-                                   const FaceCreatedCallback& onFaceCreated,
-                                   const ConnectFailedCallback& onAcceptFailed)
+TcpChannel::handleAccept(const boost::system::error_code& error,
+                         const shared_ptr<boost::asio::ip::tcp::socket>& socket,
+                         const FaceCreatedCallback& onFaceCreated,
+                         const ConnectFailedCallback& onAcceptFailed)
 {
   if (error) {
-    if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
+    if (error == boost::asio::error::operation_aborted) // when the socket is closed by someone
       return;
 
-    NFD_LOG_DEBUG("Connect to remote endpoint failed: "
-                  << error.category().message(error.value()));
-
-    if (static_cast<bool>(onAcceptFailed))
-      onAcceptFailed("Connect to remote endpoint failed: " +
-                     error.category().message(error.value()));
+    NFD_LOG_DEBUG("[" << m_localEndpoint << "] Accept failed: " << error.message());
+    if (onAcceptFailed)
+      onAcceptFailed(error.message());
     return;
   }
 
-  // prepare accepting the next connection
-  shared_ptr<ip::tcp::socket> clientSocket =
-    make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
-  m_acceptor->async_accept(*clientSocket,
-                           bind(&TcpChannel::handleSuccessfulAccept, this, _1,
-                                clientSocket,
-                                onFaceCreated, onAcceptFailed));
+  NFD_LOG_DEBUG("[" << m_localEndpoint << "] Connection from " << socket->remote_endpoint());
 
-  NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
-                "<< Connection from " << socket->remote_endpoint());
+  // prepare accepting the next connection
+  accept(onFaceCreated, onAcceptFailed);
+
   createFace(socket, onFaceCreated, true);
 }
 
 void
-TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error,
-                                    const shared_ptr<ip::tcp::socket>& socket,
-                                    const scheduler::EventId& connectTimeoutEvent,
-                                    const FaceCreatedCallback& onFaceCreated,
-                                    const ConnectFailedCallback& onConnectFailed)
+TcpChannel::handleConnect(const boost::system::error_code& error,
+                          const shared_ptr<ip::tcp::socket>& socket,
+                          const scheduler::EventId& connectTimeoutEvent,
+                          const FaceCreatedCallback& onFaceCreated,
+                          const ConnectFailedCallback& onConnectFailed)
 {
   scheduler::cancel(connectTimeoutEvent);
 
@@ -191,33 +186,34 @@
 #else
   if (error) {
 #endif
-
-    if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
+    if (error == boost::asio::error::operation_aborted) // when the socket is closed by someone
       return;
 
     socket->close();
 
-    NFD_LOG_DEBUG("Connect to remote endpoint failed: "
-                  << error.category().message(error.value()));
-
-    onConnectFailed("Connect to remote endpoint failed: " +
-                    error.category().message(error.value()));
+    NFD_LOG_DEBUG("[" << m_localEndpoint << "] Connect failed: " << error.message());
+    if (onConnectFailed)
+      onConnectFailed(error.message());
     return;
   }
 
-  NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
-                ">> Connection to " << socket->remote_endpoint());
+  NFD_LOG_DEBUG("[" << m_localEndpoint << "] Connected to " << socket->remote_endpoint());
 
   createFace(socket, onFaceCreated, false);
 }
 
 void
-TcpChannel::handleFailedConnect(const shared_ptr<ip::tcp::socket>& socket,
-                                const ConnectFailedCallback& onConnectFailed)
+TcpChannel::handleConnectTimeout(const shared_ptr<ip::tcp::socket>& socket,
+                                 const ConnectFailedCallback& onConnectFailed)
 {
   NFD_LOG_DEBUG("Connect to remote endpoint timed out");
-  onConnectFailed("Connect to remote endpoint timed out");
-  socket->close(); // abort the connection
+
+  // abort the connection attempt
+  boost::system::error_code error;
+  socket->close(error);
+
+  if (onConnectFailed)
+    onConnectFailed("Connect to remote endpoint timed out");
 }
 
 } // namespace nfd