try to reconnect to nfd when connection is down

Change-Id: I37ac7d6894cf058dc0453dd25425d8e1a6dc4e18
Refs: #2470
diff --git a/src/chat-dialog-backend.cpp b/src/chat-dialog-backend.cpp
index 6cd24ad..bbe17da 100644
--- a/src/chat-dialog-backend.cpp
+++ b/src/chat-dialog-backend.cpp
@@ -6,6 +6,7 @@
  * BSD license, See the LICENSE file for more information
  *
  * Author: Yingdi Yu <yingdi@cs.ucla.edu>
+ *         Qiuhan Ding <qiuhanding@cs.ucla.edu>
  */
 
 #include "chat-dialog-backend.hpp"
@@ -28,6 +29,7 @@
 static const ndn::Name::Component ROUTING_HINT_SEPARATOR =
   ndn::name::Component::fromEscapedString("%F0%2E");
 static const int IDENTITY_OFFSET = -3;
+static const int CONNECTION_RETRY_TIMER = 3;
 
 ChatDialogBackend::ChatDialogBackend(const Name& chatroomPrefix,
                                      const Name& userChatPrefix,
@@ -37,6 +39,7 @@
                                      const Name& signingId,
                                      QObject* parent)
   : QThread(parent)
+  , m_shouldResume(false)
   , m_localRoutingPrefix(routingPrefix)
   , m_chatroomPrefix(chatroomPrefix)
   , m_userChatPrefix(userChatPrefix)
@@ -63,13 +66,40 @@
     if (m_face == nullptr)
       break;
 
-    m_face->getIoService().run();
-
+    try {
+      m_face->getIoService().run();
+    }
+    catch (std::runtime_error& e) {
+      {
+        std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
+        m_isNfdConnected = false;
+      }
+      emit nfdError();
+      {
+        std::lock_guard<std::mutex>lock(m_resumeMutex);
+        m_shouldResume = true;
+      }
+#ifdef BOOST_THREAD_USES_CHRONO
+      time::seconds reconnectTimer = time::seconds(CONNECTION_RETRY_TIMER);
+#else
+      boost::posix_time::time_duration reconnectTimer;
+      reconnectTimer = boost::posix_time::seconds(CONNECTION_RETRY_TIMER);
+#endif
+      while (!m_isNfdConnected) {
+#ifdef BOOST_THREAD_USES_CHRONO
+        boost::this_thread::sleep_for(reconnectTimer);
+#else
+        boost::this_thread::sleep(reconnectTimer);
+#endif
+      }
+      emit refreshChatDialog(m_routableUserChatPrefix);
+    }
     {
-      std::lock_guard<std::mutex>lock(m_mutex);
+      std::lock_guard<std::mutex>lock(m_resumeMutex);
       shouldResume = m_shouldResume;
       m_shouldResume = false;
     }
+    close();
 
   } while (shouldResume);
 
@@ -161,13 +191,16 @@
 }
 
 void
-ChatDialogBackend::close()
-{
+ChatDialogBackend::exitChatroom() {
   if (m_joined)
     sendLeave();
 
   usleep(100000);
+}
 
+void
+ChatDialogBackend::close()
+{
   m_scheduler->cancelAllEvents();
   m_helloEventId.reset();
   m_roster.clear();
@@ -386,6 +419,7 @@
 
   m_helloEventId = m_scheduler->scheduleEvent(HELLO_INTERVAL,
                                               bind(&ChatDialogBackend::sendHello, this));
+  emit newChatroomForDiscovery(Name::Component(m_chatroomName));
 }
 
 void
@@ -487,11 +521,11 @@
     m_localRoutingPrefix = newLocalRoutingPrefix;
 
     {
-      std::lock_guard<std::mutex>lock(m_mutex);
+      std::lock_guard<std::mutex>lock(m_resumeMutex);
       m_shouldResume = true;
     }
 
-    close();
+    exitChatroom();
 
     updatePrefixes();
 
@@ -503,15 +537,28 @@
 ChatDialogBackend::shutdown()
 {
   {
-    std::lock_guard<std::mutex>lock(m_mutex);
+    std::lock_guard<std::mutex>lock(m_resumeMutex);
     m_shouldResume = false;
   }
 
-  close();
+  {
+    // In this case, we just stop checking the nfd connection and exit
+    std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
+    m_isNfdConnected = true;
+  }
+
+  exitChatroom();
 
   m_face->getIoService().stop();
 }
 
+void
+ChatDialogBackend::onNfdReconnect()
+{
+  std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
+  m_isNfdConnected = true;
+}
+
 } // namespace chronochat
 
 #if WAF
diff --git a/src/chat-dialog-backend.hpp b/src/chat-dialog-backend.hpp
index f0fead3..70ebefc 100644
--- a/src/chat-dialog-backend.hpp
+++ b/src/chat-dialog-backend.hpp
@@ -5,6 +5,7 @@
  * BSD license, See the LICENSE file for more information
  *
  * Author: Yingdi Yu <yingdi@cs.ucla.edu>
+ *         Qiuhan Ding <qiuhanding@cs.ucla.edu>
  */
 
 #ifndef CHRONOCHAT_CHAT_DIALOG_BACKEND_HPP
@@ -18,6 +19,7 @@
 #include "chatbuf.pb.h"
 #include <mutex>
 #include <socket.hpp>
+#include <boost/thread.hpp>
 #endif
 
 namespace chronochat {
@@ -47,7 +49,7 @@
                     const std::string& chatroomName,
                     const std::string& nick,
                     const Name& signingId = Name(),
-                    QObject* parent = 0);
+                    QObject* parent = nullptr);
 
   ~ChatDialogBackend();
 
@@ -63,6 +65,9 @@
   loadTrustAnchor();
 
   void
+  exitChatroom();
+
+  void
   close();
 
   void
@@ -121,11 +126,20 @@
   chatPrefixChanged(ndn::Name newChatPrefix);
 
   void
+  refreshChatDialog(ndn::Name chatPrefix);
+
+  void
   eraseInRoster(ndn::Name sessionPrefix, ndn::Name::Component chatroomName);
 
   void
   addInRoster(ndn::Name sessionPrefix, ndn::Name::Component chatroomName);
 
+  void
+  newChatroomForDiscovery(ndn::Name::Component chatroomName);
+
+  void
+  nfdError();
+
 public slots:
   void
   sendChatMessage(QString text, time_t timestamp);
@@ -136,9 +150,14 @@
   void
   shutdown();
 
+  void
+  onNfdReconnect();
+
 private:
   typedef std::map<ndn::Name, UserInfo> BackendRoster;
 
+  bool m_shouldResume;
+  bool m_isNfdConnected;
   shared_ptr<ndn::Face> m_face;
 
   Name m_localRoutingPrefix;             // routable local prefix
@@ -160,8 +179,8 @@
 
   BackendRoster m_roster;                // User roster
 
-  std::mutex m_mutex;
-  bool m_shouldResume;
+  std::mutex m_resumeMutex;
+  std::mutex m_nfdConnectionMutex;
 };
 
 } // namespace chronochat
diff --git a/src/chat-dialog.cpp b/src/chat-dialog.cpp
index 1167799..0158496 100644
--- a/src/chat-dialog.cpp
+++ b/src/chat-dialog.cpp
@@ -102,6 +102,9 @@
   connect(&m_backend, SIGNAL(chatPrefixChanged(ndn::Name)),
           this,       SLOT(updateLabels(ndn::Name)));
 
+  connect(&m_backend, SIGNAL(refreshChatDialog(ndn::Name)),
+          this,       SLOT(updateLabels(ndn::Name)));
+
   // When frontend gets a message to send, notify backend.
   connect(this,       SIGNAL(msgToSent(QString, time_t)),
           &m_backend, SLOT(sendChatMessage(QString, time_t)));
@@ -351,6 +354,7 @@
     appendControlMessage(nick, "enters room", timestamp);
     m_rosterModel->setStringList(m_scene->getRosterList());
   }
+  fitView();
 }
 
 void
diff --git a/src/chatroom-discovery-backend.cpp b/src/chatroom-discovery-backend.cpp
index 0da409d..371a119 100644
--- a/src/chatroom-discovery-backend.cpp
+++ b/src/chatroom-discovery-backend.cpp
@@ -26,16 +26,17 @@
 // a count enforced when a manager himself find another one publish chatroom data
 static const int MAXIMUM_COUNT = 3;
 static const int IDENTITY_OFFSET = -1;
+static const int CONNECTION_RETRY_TIMER = 3;
 
 ChatroomDiscoveryBackend::ChatroomDiscoveryBackend(const Name& routingPrefix,
                                                    const Name& identity,
                                                    QObject* parent)
   : QThread(parent)
+  , m_shouldResume(false)
   , m_routingPrefix(routingPrefix)
   , m_identity(identity)
   , m_randomGenerator(static_cast<unsigned int>(std::time(0)))
   , m_rangeUniformRandom(m_randomGenerator, boost::uniform_int<>(500,2000))
-  , m_shouldResume(false)
 {
   m_discoveryPrefix.append("ndn")
     .append("broadcast")
@@ -59,13 +60,38 @@
     if (m_face == nullptr)
       break;
 
-    m_face->getIoService().run();
-
+    try {
+      m_face->getIoService().run();
+    }
+    catch (std::runtime_error& e) {
+      {
+        std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
+        m_isNfdConnected = false;
+      }
+      emit nfdError();
+      {
+        std::lock_guard<std::mutex>lock(m_resumeMutex);
+        m_shouldResume = true;
+      }
+#ifdef BOOST_THREAD_USES_CHRONO
+      time::seconds reconnectTimer = time::seconds(CONNECTION_RETRY_TIMER);
+#else
+      boost::posix_time::time_duration reconnectTimer = boost::posix_time::seconds(CONNECTION_RETRY_TIMER);
+#endif
+      while (!m_isNfdConnected) {
+#ifdef BOOST_THREAD_USES_CHRONO
+        boost::this_thread::sleep_for(reconnectTimer);
+#else
+        boost::this_thread::sleep(reconnectTimer);
+#endif
+      }
+    }
     {
-      std::lock_guard<std::mutex>lock(m_mutex);
+      std::lock_guard<std::mutex>lock(m_resumeMutex);
       shouldResume = m_shouldResume;
       m_shouldResume = false;
     }
+    close();
 
   } while (shouldResume);
 
@@ -275,12 +301,10 @@
     updatePrefixes();
 
     {
-      std::lock_guard<std::mutex>lock(m_mutex);
+      std::lock_guard<std::mutex>lock(m_resumeMutex);
       m_shouldResume = true;
     }
 
-    close();
-
     m_face->getIoService().stop();
   }
 }
@@ -341,14 +365,12 @@
       sendUpdate(chatroomName);
   }
   else {
-    m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
-    m_chatroomList[chatroomName].info.setName(chatroomName);
-    m_chatroomList[chatroomName].info.addParticipant(sessionPrefix);
+    onNewChatroomForDiscovery(chatroomName);
   }
 }
 
 void
-ChatroomDiscoveryBackend::onNewChatroomForDiscovery(Name::Component chatroomName)
+ChatroomDiscoveryBackend::onNewChatroomForDiscovery(ndn::Name::Component chatroomName)
 {
   Name newPrefix = m_routableUserDiscoveryPrefix;
   newPrefix.append(chatroomName);
@@ -406,12 +428,10 @@
   updatePrefixes();
 
   {
-    std::lock_guard<std::mutex>lock(m_mutex);
+    std::lock_guard<std::mutex>lock(m_resumeMutex);
     m_shouldResume = true;
   }
 
-  close();
-
   m_face->getIoService().stop();
 }
 
@@ -443,15 +463,25 @@
 ChatroomDiscoveryBackend::shutdown()
 {
   {
-    std::lock_guard<std::mutex>lock(m_mutex);
+    std::lock_guard<std::mutex>lock(m_resumeMutex);
     m_shouldResume = false;
   }
 
-  close();
+  {
+    // In this case, we just stop checking the nfd connection and exit
+    std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
+    m_isNfdConnected = true;
+  }
 
   m_face->getIoService().stop();
 }
 
+void
+ChatroomDiscoveryBackend::onNfdReconnect()
+{
+  std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
+  m_isNfdConnected = true;
+}
 
 } // namespace chronochat
 
diff --git a/src/chatroom-discovery-backend.hpp b/src/chatroom-discovery-backend.hpp
index b3f809f..d6bf843 100644
--- a/src/chatroom-discovery-backend.hpp
+++ b/src/chatroom-discovery-backend.hpp
@@ -16,10 +16,10 @@
 #ifndef Q_MOC_RUN
 #include "common.hpp"
 #include "chatroom-info.hpp"
-#include <ndn-cxx/util/scheduler.hpp>
 #include <boost/random.hpp>
 #include <mutex>
 #include <socket.hpp>
+#include <boost/thread.hpp>
 #endif
 
 namespace chronochat {
@@ -55,7 +55,7 @@
 public:
   ChatroomDiscoveryBackend(const Name& routingPrefix,
                            const Name& identity,
-                           QObject* parent = 0);
+                           QObject* parent = nullptr);
 
   ~ChatroomDiscoveryBackend();
 
@@ -124,6 +124,9 @@
   void
   chatroomInfoReady(const ChatroomInfo& info, bool isParticipant);
 
+  void
+  nfdError();
+
 public slots:
 
   /**
@@ -166,7 +169,7 @@
    * @param chatroomName the name of chatroom the user join
    */
   void
-  onNewChatroomForDiscovery(Name::Component chatroomName);
+  onNewChatroomForDiscovery(ndn::Name::Component chatroomName);
 
   /**
    * @brief get chatroom info from chat dialog
@@ -204,10 +207,15 @@
   void
   shutdown();
 
+  void
+  onNfdReconnect();
+
 private:
 
   typedef std::map<ndn::Name::Component, ChatroomInfoBackend> ChatroomList;
 
+  bool m_shouldResume;
+  bool m_isNfdConnected;
   Name m_discoveryPrefix;
   Name m_routableUserDiscoveryPrefix;
   Name m_routingPrefix;
@@ -224,9 +232,9 @@
   shared_ptr<chronosync::Socket> m_sock; // SyncSocket
 
   ChatroomList m_chatroomList;
-  std::mutex m_mutex;
+  std::mutex m_resumeMutex;
+  std::mutex m_nfdConnectionMutex;
 
-  bool m_shouldResume;
 };
 
 } // namespace chronochat
diff --git a/src/controller-backend.cpp b/src/controller-backend.cpp
index df341c9..496cd9a 100644
--- a/src/controller-backend.cpp
+++ b/src/controller-backend.cpp
@@ -6,6 +6,7 @@
  * BSD license, See the LICENSE file for more information
  *
  * Author: Yingdi Yu <yingdi@cs.ucla.edu>
+ *         Qiuhan Ding <qiuhanding@cs.ucla.edu>
  */
 
 #include "controller-backend.hpp"
@@ -32,9 +33,11 @@
 static const ndn::Name::Component ROUTING_HINT_SEPARATOR =
   ndn::name::Component::fromEscapedString("%F0%2E");
 static const int MAXIMUM_REQUEST = 3;
+static const int CONNECTION_RETRY_TIMER = 3;
 
 ControllerBackend::ControllerBackend(QObject* parent)
   : QThread(parent)
+  , m_shouldResume(false)
   , m_contactManager(m_face)
   , m_invitationListenerId(0)
 {
@@ -54,10 +57,42 @@
 void
 ControllerBackend::run()
 {
-  setInvitationListener();
-
-  m_face.processEvents();
-
+  bool shouldResume = false;
+  do {
+    try {
+      setInvitationListener();
+      m_face.processEvents();
+    }
+    catch (std::runtime_error& e) {
+      {
+        std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
+        m_isNfdConnected = false;
+      }
+      emit nfdError();
+      {
+        std::lock_guard<std::mutex>lock(m_resumeMutex);
+        m_shouldResume = true;
+      }
+#ifdef BOOST_THREAD_USES_CHRONO
+      time::seconds reconnectTimer = time::seconds(CONNECTION_RETRY_TIMER);
+#else
+      boost::posix_time::time_duration reconnectTimer;
+      reconnectTimer = boost::posix_time::seconds(CONNECTION_RETRY_TIMER);
+#endif
+      while (!m_isNfdConnected) {
+#ifdef BOOST_THREAD_USES_CHRONO
+        boost::this_thread::sleep_for(reconnectTimer);
+#else
+        boost::this_thread::sleep(reconnectTimer);
+#endif
+      }
+    }
+    {
+      std::lock_guard<std::mutex>lock(m_resumeMutex);
+      shouldResume = m_shouldResume;
+      m_shouldResume = false;
+    }
+  } while (shouldResume);
   std::cerr << "Bye!" << std::endl;
 }
 
@@ -318,6 +353,15 @@
 void
 ControllerBackend::shutdown()
 {
+  {
+    std::lock_guard<std::mutex>lock(m_resumeMutex);
+    m_shouldResume = false;
+  }
+  {
+    // In this case, we just stop checking the nfd connection and exit
+    std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
+    m_isNfdConnected = true;
+  }
   m_face.getIoService().stop();
 }
 
@@ -463,6 +507,13 @@
 
 }
 
+void
+ControllerBackend::onNfdReconnect()
+{
+  std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
+  m_isNfdConnected = true;
+}
+
 
 } // namespace chronochat
 
diff --git a/src/controller-backend.hpp b/src/controller-backend.hpp
index d7bda46..78865ef 100644
--- a/src/controller-backend.hpp
+++ b/src/controller-backend.hpp
@@ -5,6 +5,7 @@
  * BSD license, See the LICENSE file for more information
  *
  * Author: Yingdi Yu <yingdi@cs.ucla.edu>
+ *         Qiuhan Ding <qiuhanding@cs.ucla.edu>
  */
 
 #ifndef CHRONOCHAT_CONTROLLER_BACKEND_HPP
@@ -22,6 +23,8 @@
 #include "validator-invitation.hpp"
 #include <ndn-cxx/security/key-chain.hpp>
 #include <ndn-cxx/util/in-memory-storage-persistent.hpp>
+#include <boost/thread.hpp>
+#include <mutex>
 #endif
 
 namespace chronochat {
@@ -31,7 +34,7 @@
   Q_OBJECT
 
 public:
-  ControllerBackend(QObject* parent = 0);
+  ControllerBackend(QObject* parent = nullptr);
 
   ~ControllerBackend();
 
@@ -113,6 +116,9 @@
   void
   invitationRequestResult(const std::string& msg);
 
+  void
+  nfdError();
+
 public slots:
   void
   shutdown();
@@ -138,11 +144,16 @@
   void
   onSendInvitationRequest(const QString& chatroomName, const QString& prefix);
 
+  void
+  onNfdReconnect();
+
 private slots:
   void
   onContactIdListReady(const QStringList& list);
 
 private:
+  bool m_isNfdConnected;
+  bool m_shouldResume;
   ndn::Face m_face;
 
   Name m_identity;  //TODO: set/get
@@ -164,6 +175,8 @@
   QStringList m_chatDialogList;
 
   QMutex m_mutex;
+  std::mutex m_resumeMutex;
+  std::mutex m_nfdConnectionMutex;
 
   ndn::util::InMemoryStoragePersistent m_ims;
 };
diff --git a/src/controller.cpp b/src/controller.cpp
index b660597..6121f2b 100644
--- a/src/controller.cpp
+++ b/src/controller.cpp
@@ -6,6 +6,7 @@
  * BSD license, See the LICENSE file for more information
  *
  * Author: Yingdi Yu <yingdi@cs.ucla.edu>
+ *         Qiuhan Ding <qiuhanding@cs.ucla.edu>
  */
 
 #include <QApplication>
@@ -44,6 +45,7 @@
 Controller::Controller(QWidget* parent)
   : QDialog(parent)
   , m_localPrefixDetected(false)
+  , m_isInConnectionDetection(false)
   , m_settingDialog(new SettingDialog(this))
   , m_startChatDialog(new StartChatDialog(this))
   , m_profileEditor(new ProfileEditor(this))
@@ -156,6 +158,10 @@
                                                   const QString&, bool)));
 
   // Connection to backend thread
+  connect(&m_backend, SIGNAL(nfdError()),
+          this, SLOT(onNfdError()));
+  connect(this, SIGNAL(nfdReconnect()),
+          &m_backend, SLOT(onNfdReconnect()));
   connect(this, SIGNAL(shutdownBackend()),
           &m_backend, SLOT(shutdown()));
   connect(this, SIGNAL(updateLocalPrefix()),
@@ -203,8 +209,6 @@
           m_chatroomDiscoveryBackend, SLOT(updateRoutingPrefix(const QString&)));
   connect(this, SIGNAL(localPrefixConfigured(const QString&)),
           m_chatroomDiscoveryBackend, SLOT(updateRoutingPrefix(const QString&)));
-  connect(this, SIGNAL(newChatroomForDiscovery(Name::Component)),
-          m_chatroomDiscoveryBackend, SLOT(onNewChatroomForDiscovery(Name::Component)));
   connect(m_chatroomDiscoveryBackend, SIGNAL(chatroomInfoRequest(std::string, bool)),
           this, SLOT(onChatroomInfoRequest(std::string, bool)));
   connect(this, SIGNAL(respondChatroomInfoRequest(ChatroomInfo, bool)),
@@ -213,6 +217,10 @@
           m_chatroomDiscoveryBackend, SLOT(shutdown()));
   connect(this, SIGNAL(identityUpdated(const QString&)),
           m_chatroomDiscoveryBackend, SLOT(onIdentityUpdated(const QString&)));
+  connect(this, SIGNAL(nfdReconnect()),
+          m_chatroomDiscoveryBackend, SLOT(onNfdReconnect()));
+  connect(m_chatroomDiscoveryBackend, SIGNAL(nfdError()),
+          this, SLOT(onNfdError()));
 
   // connect chatroom discovery back end with front end
   connect(m_discoveryPanel, SIGNAL(waitForChatroomInfo(const QString&)),
@@ -489,8 +497,14 @@
           chatDialog->getBackend(), SLOT(updateRoutingPrefix(const QString&)));
   connect(this, SIGNAL(localPrefixConfigured(const QString&)),
           chatDialog->getBackend(), SLOT(updateRoutingPrefix(const QString&)));
+  connect(this, SIGNAL(nfdReconnect()),
+          chatDialog->getBackend(), SLOT(onNfdReconnect()));
 
   // connect chat dialog with discovery backend
+  connect(chatDialog->getBackend(), SIGNAL(nfdError()),
+          this, SLOT(onNfdError()));
+  connect(chatDialog->getBackend(), SIGNAL(newChatroomForDiscovery(ndn::Name::Component)),
+          m_chatroomDiscoveryBackend, SLOT(onNewChatroomForDiscovery(ndn::Name::Component)));
   connect(chatDialog->getBackend(), SIGNAL(addInRoster(ndn::Name, ndn::Name::Component)),
           m_chatroomDiscoveryBackend, SLOT(onAddInRoster(ndn::Name, ndn::Name::Component)));
   connect(chatDialog->getBackend(), SIGNAL(eraseInRoster(ndn::Name, ndn::Name::Component)),
@@ -508,7 +522,6 @@
           chatDialog, SLOT(shutdown()));
 
   updateMenu();
-  emit newChatroomForDiscovery(Name::Component(chatroomName.toStdString()));
 }
 
 void
@@ -670,6 +683,11 @@
     m_backend.wait();
   }
 
+  if (m_nfdConnectionChecker != nullptr && m_nfdConnectionChecker->isRunning()) {
+    emit shutdownNfdChecker();
+    m_nfdConnectionChecker->wait();
+  }
+
   QApplication::quit();
 }
 
@@ -773,10 +791,34 @@
 }
 
 void
-Controller::onError(const QString& msg)
+Controller::onNfdError()
 {
-  QMessageBox::critical(this, tr("ChronoChat"), msg, QMessageBox::Ok);
-  exit(1);
+  if (m_isInConnectionDetection)
+    return;
+  // begin a thread
+  
+  m_isInConnectionDetection = true;
+  m_nfdConnectionChecker = new NfdConnectionChecker(this);
+
+  connect(m_nfdConnectionChecker, SIGNAL(nfdConnected()),
+          this, SLOT(onNfdReconnect()));
+  connect(this, SIGNAL(shutdownNfdChecker()),
+          m_nfdConnectionChecker, SLOT(shutdown()));
+
+  m_nfdConnectionChecker->start();
+  QMessageBox::information(this, tr("ChronoChat"), "Nfd is not running");
+}
+
+void
+Controller::onNfdReconnect()
+{
+  if (m_nfdConnectionChecker != nullptr && m_nfdConnectionChecker->isRunning()) {
+    m_nfdConnectionChecker->wait();
+  }
+  delete m_nfdConnectionChecker;
+  m_nfdConnectionChecker = nullptr;
+  m_isInConnectionDetection = false;
+  emit nfdReconnect();
 }
 
 void
diff --git a/src/controller.hpp b/src/controller.hpp
index fdce4cd..ca92c82 100644
--- a/src/controller.hpp
+++ b/src/controller.hpp
@@ -6,6 +6,7 @@
  * BSD license, See the LICENSE file for more information
  *
  * Author: Yingdi Yu <yingdi@cs.ucla.edu>
+ *         Qiuhan Ding <qiuhanding@cs.ucla.edu>
  */
 
 #ifndef CHRONOCHAT_CONTROLLER_HPP
@@ -27,6 +28,7 @@
 #include "chat-dialog.hpp"
 #include "chatroom-discovery-backend.hpp"
 #include "discovery-panel.hpp"
+#include "nfd-connection-checker.hpp"
 
 #ifndef Q_MOC_RUN
 #include "common.hpp"
@@ -41,7 +43,7 @@
   Q_OBJECT
 
 public: // public methods
-  Controller(QWidget* parent = 0);
+  Controller(QWidget* parent = nullptr);
 
   virtual
   ~Controller();
@@ -119,10 +121,13 @@
   removeChatroom(QString chatroomName);
 
   void
-  newChatroomForDiscovery(Name::Component chatroomName);
+  respondChatroomInfoRequest(ChatroomInfo chatroomInfo, bool isManager);
 
   void
-  respondChatroomInfoRequest(ChatroomInfo chatroomInfo, bool isManager);
+  nfdReconnect();
+
+  void
+  shutdownNfdChecker();
 
 private slots:
   void
@@ -186,7 +191,10 @@
   onWarning(const QString& msg);
 
   void
-  onError(const QString& msg);
+  onNfdError();
+
+  void
+  onNfdReconnect();
 
   void
   onChatroomInfoRequest(std::string chatroomName, bool isManager);
@@ -198,6 +206,7 @@
   // Communication
   Name m_localPrefix;
   bool m_localPrefixDetected;
+  bool m_isInConnectionDetection;
 
   // Tray
   QAction*         m_startChatroom;
@@ -227,7 +236,6 @@
   AddContactPanel*          m_addContactPanel;
   ChatDialogList            m_chatDialogList;
   DiscoveryPanel*           m_discoveryPanel;
-  ChatroomDiscoveryBackend* m_chatroomDiscoveryBackend;
 
   // Conf
   Name m_identity;
@@ -235,7 +243,9 @@
   QSqlDatabase m_db;
 
   // Backend
-  ControllerBackend m_backend;
+  ControllerBackend          m_backend;
+  ChatroomDiscoveryBackend*  m_chatroomDiscoveryBackend;
+  NfdConnectionChecker*      m_nfdConnectionChecker;
 };
 
 } // namespace chronochat
diff --git a/src/discovery-panel.cpp b/src/discovery-panel.cpp
index aa91028..b7c0ed6 100644
--- a/src/discovery-panel.cpp
+++ b/src/discovery-panel.cpp
@@ -98,7 +98,6 @@
 DiscoveryPanel::onChatroomListReady(const QStringList& list)
 {
   m_chatroomList = list;
-  resetPanel();
   m_chatroomListModel->setStringList(m_chatroomList);
 }
 
diff --git a/src/nfd-connection-checker.cpp b/src/nfd-connection-checker.cpp
new file mode 100644
index 0000000..023c0b2
--- /dev/null
+++ b/src/nfd-connection-checker.cpp
@@ -0,0 +1,76 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Qiuhan Ding <qiuhanding@cs.ucla.edu>
+ */
+
+#include "nfd-connection-checker.hpp"
+
+#ifndef Q_MOC_RUN
+
+#endif
+
+namespace chronochat {
+
+static const int CONNECTION_RETRY_TIMER = 2;
+
+NfdConnectionChecker::NfdConnectionChecker(QObject* parent)
+  :QThread(parent)
+{
+}
+
+void
+NfdConnectionChecker::run()
+{
+  m_face = unique_ptr<ndn::Face>(new ndn::Face);
+#ifdef BOOST_THREAD_USES_CHRONO
+  time::seconds reconnetTimer = time::seconds(CONNECTION_RETRY_TIMER);
+#else
+  boost::posix_time::time_duration reconnectTimer;
+  reconnectTimer = boost::posix_time::seconds(CONNECTION_RETRY_TIMER);
+#endif
+  do {
+    {
+      std::lock_guard<std::mutex>lock(m_nfdMutex);
+      m_nfdConnected = true;
+    }
+    try {
+      m_face->expressInterest(Interest("/localhost/nfd/status"),
+                              [&] (const Interest& interest, const Data& data) {
+                                m_face->shutdown();
+                              },
+                              [] (const Interest& interest) {});
+      m_face->processEvents(time::milliseconds::zero(), true);
+    }
+    catch (std::runtime_error& e) {
+      {
+        std::lock_guard<std::mutex>lock(m_nfdMutex);
+        m_nfdConnected = false;
+      }
+#ifdef BOOST_THREAD_USES_CHRONO
+      boost::this_thread::sleep_for(reconnetTimer);
+#else
+      boost::this_thread::sleep(reconnectTimer);
+#endif
+    }
+  } while (!m_nfdConnected);
+  emit nfdConnected();
+}
+
+void
+NfdConnectionChecker::shutdown()
+{
+  // In this case, we just stop checking the nfd connection and exit
+  std::lock_guard<std::mutex>lock(m_nfdMutex);
+  m_nfdConnected = true;
+}
+
+} // namespace chronochat
+
+#if WAF
+#include "nfd-connection-checker.moc"
+// #include "nfd-connection-checker.cpp.moc"
+#endif
diff --git a/src/nfd-connection-checker.hpp b/src/nfd-connection-checker.hpp
new file mode 100644
index 0000000..08b6085
--- /dev/null
+++ b/src/nfd-connection-checker.hpp
@@ -0,0 +1,52 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Qiuhan Ding <qiuhanding@cs.ucla.edu>
+ */
+
+#ifndef CHRONOCHAT_NFD_CONNECTION_CHECKER_HPP
+#define CHRONOCHAT_NFD_CONNECTION_CHECKER_HPP
+
+#include <QThread>
+
+#ifndef Q_MOC_RUN
+#include "common.hpp"
+#include <mutex>
+#include <boost/thread.hpp>
+#include <ndn-cxx/face.hpp>
+#endif
+
+namespace chronochat {
+
+class NfdConnectionChecker : public QThread
+{
+  Q_OBJECT
+
+public:
+  NfdConnectionChecker(QObject* parent = nullptr);
+
+protected:
+  void
+  run();
+
+signals:
+  void
+  nfdConnected();
+
+public slots:
+  void
+  shutdown();
+
+private:
+  bool m_nfdConnected;
+  std::mutex m_nfdMutex;
+
+  unique_ptr<ndn::Face> m_face;
+};
+
+} // namespace chronochat
+
+#endif // CHRONOCHAT_NFD_CONNECTION_CHECKER_HPP