chat-dialog-backend: handle exit properly

Change-Id: Iadab00119f6247e90cfc9ee585b9506f88876e47
diff --git a/src/chat-dialog-backend.cpp b/src/chat-dialog-backend.cpp
index 680d24b..bedb45d 100644
--- a/src/chat-dialog-backend.cpp
+++ b/src/chat-dialog-backend.cpp
@@ -36,7 +36,6 @@
   , m_userChatPrefix(userChatPrefix)
   , m_chatroomName(chatroomName)
   , m_nick(nick)
-  , m_scheduler(m_face.getIoService())
 {
   updatePrefixes();
 }
@@ -50,9 +49,21 @@
 void
 ChatDialogBackend::run()
 {
-  initializeSync();
+  bool shouldResume = false;
+  do {
+    initializeSync();
 
-  m_face.processEvents();
+    if (m_face == nullptr)
+      break;
+
+    m_face->getIoService().run();
+
+    m_mutex.lock();
+    shouldResume = m_shouldResume;
+    m_shouldResume = false;
+    m_mutex.unlock();
+
+  } while (shouldResume);
 
   std::cerr << "Bye!" << std::endl;
 }
@@ -61,33 +72,43 @@
 void
 ChatDialogBackend::initializeSync()
 {
-  // if a SyncSocket is running, turn it off
-  if (static_cast<bool>(m_sock)) {
-    if (m_joined)
-      sendLeave();
-    m_sock.reset();
+  BOOST_ASSERT(m_sock == nullptr);
 
-    usleep(100000);
-  }
+  m_face = unique_ptr<ndn::Face>(new ndn::Face);
+  m_scheduler = unique_ptr<ndn::Scheduler>(new ndn::Scheduler(m_face->getIoService()));
 
   // create a new SyncSocket
   m_sock = make_shared<chronosync::Socket>(m_chatroomPrefix,
                                            m_routableUserChatPrefix,
-                                           ref(m_face),
+                                           ref(*m_face),
                                            bind(&ChatDialogBackend::processSyncUpdate, this, _1));
 
   // schedule a new join event
-  m_scheduler.scheduleEvent(time::milliseconds(600),
-                            bind(&ChatDialogBackend::sendJoin, this));
+  m_scheduler->scheduleEvent(time::milliseconds(600),
+                             bind(&ChatDialogBackend::sendJoin, this));
 
   // cancel existing hello event if it exists
-  if (static_cast<bool>(m_helloEventId)) {
-    m_scheduler.cancelEvent(m_helloEventId);
+  if (m_helloEventId != nullptr) {
+    m_scheduler->cancelEvent(m_helloEventId);
     m_helloEventId.reset();
   }
 }
 
 void
+ChatDialogBackend::close()
+{
+  if (m_joined)
+    sendLeave();
+
+  usleep(100000);
+
+  m_scheduler->cancelAllEvents();
+  m_helloEventId.reset();
+  m_roster.clear();
+  m_sock.reset();
+}
+
+void
 ChatDialogBackend::processSyncUpdate(const std::vector<chronosync::MissingDataInfo>& updates)
 {
   _LOG_DEBUG("<<< processing Tree Update");
@@ -156,7 +177,7 @@
     if (it != m_roster.end()) {
       // cancel timeout event
       if (static_cast<bool>(it->second.timeoutEventId))
-        m_scheduler.cancelEvent(it->second.timeoutEventId);
+        m_scheduler->cancelEvent(it->second.timeoutEventId);
 
       // notify frontend to remove the remote session (node)
       emit sessionRemoved(QString::fromStdString(remoteSessionPrefix.toUri()),
@@ -193,13 +214,13 @@
 
     // If a timeout event has been scheduled, cancel it.
     if (static_cast<bool>(it->second.timeoutEventId))
-      m_scheduler.cancelEvent(it->second.timeoutEventId);
+      m_scheduler->cancelEvent(it->second.timeoutEventId);
 
     // (Re)schedule another timeout event after 3 HELLO_INTERVAL;
     it->second.timeoutEventId =
-      m_scheduler.scheduleEvent(HELLO_INTERVAL * 3,
-                                bind(&ChatDialogBackend::remoteSessionTimeout,
-                                     this, remoteSessionPrefix));
+      m_scheduler->scheduleEvent(HELLO_INTERVAL * 3,
+                                 bind(&ChatDialogBackend::remoteSessionTimeout,
+                                      this, remoteSessionPrefix));
 
     // If chat message, notify the frontend
     if (msg.type() == SyncDemo::ChatMessage::CHAT)
@@ -262,8 +283,8 @@
   prepareControlMessage(msg, SyncDemo::ChatMessage::JOIN);
   sendMsg(msg);
 
-  m_helloEventId = m_scheduler.scheduleEvent(HELLO_INTERVAL,
-                                             bind(&ChatDialogBackend::sendHello, this));
+  m_helloEventId = m_scheduler->scheduleEvent(HELLO_INTERVAL,
+                                              bind(&ChatDialogBackend::sendHello, this));
 
   emit sessionAdded(QString::fromStdString(m_routableUserChatPrefix.toUri()),
                     QString::fromStdString(msg.from()),
@@ -277,8 +298,8 @@
   prepareControlMessage(msg, SyncDemo::ChatMessage::HELLO);
   sendMsg(msg);
 
-  m_helloEventId = m_scheduler.scheduleEvent(HELLO_INTERVAL,
-                                             bind(&ChatDialogBackend::sendHello, this));
+  m_helloEventId = m_scheduler->scheduleEvent(HELLO_INTERVAL,
+                                              bind(&ChatDialogBackend::sendHello, this));
 }
 
 void
@@ -366,22 +387,26 @@
 
     updatePrefixes();
 
-    initializeSync();
+    m_mutex.lock();
+    m_shouldResume = true;
+    m_mutex.unlock();
+
+    close();
+
+    m_face->getIoService().stop();
   }
 }
 
 void
 ChatDialogBackend::shutdown()
 {
-  if (static_cast<bool>(m_sock)) {
-    if (m_joined)
-      sendLeave();
-    m_sock.reset();
+  m_mutex.lock();
+  m_shouldResume = false;
+  m_mutex.unlock();
 
-    usleep(100000);
-  }
+  close();
 
-  m_face.getIoService().stop();
+  m_face->getIoService().stop();
 }
 
 } // namespace chronos
diff --git a/src/chat-dialog-backend.hpp b/src/chat-dialog-backend.hpp
index 09c52a6..f2d6f89 100644
--- a/src/chat-dialog-backend.hpp
+++ b/src/chat-dialog-backend.hpp
@@ -59,6 +59,9 @@
   initializeSync();
 
   void
+  close();
+
+  void
   processSyncUpdate(const std::vector<chronosync::MissingDataInfo>& updates);
 
   void
@@ -129,7 +132,7 @@
 private:
   typedef std::map<ndn::Name, UserInfo> BackendRoster;
 
-  ndn::Face m_face;
+  unique_ptr<ndn::Face> m_face;
 
   Name m_localRoutingPrefix;             // routable local prefix
   Name m_chatroomPrefix;                 // chatroom sync prefix
@@ -141,12 +144,15 @@
 
   shared_ptr<chronosync::Socket> m_sock; // SyncSocket
 
-  ndn::Scheduler m_scheduler;            // scheduler
+  unique_ptr<ndn::Scheduler> m_scheduler;// scheduler
   ndn::EventId m_helloEventId;           // event id of timeout
 
   bool m_joined;                         // true if in a chatroom
 
   BackendRoster m_roster;                // User roster
+
+  QMutex m_mutex;
+  bool m_shouldResume;
 };
 
 } // namespace chronos
diff --git a/src/common.hpp b/src/common.hpp
index 9b427b6..9acb3b7 100644
--- a/src/common.hpp
+++ b/src/common.hpp
@@ -45,17 +45,18 @@
 
 using boost::noncopyable;
 
-using ndn::shared_ptr;
-using ndn::weak_ptr;
-using ndn::enable_shared_from_this;
-using ndn::make_shared;
-using ndn::static_pointer_cast;
-using ndn::dynamic_pointer_cast;
-using ndn::const_pointer_cast;
-using ndn::function;
-using ndn::bind;
-using ndn::ref;
-using ndn::cref;
+using std::shared_ptr;
+using std::unique_ptr;
+using std::weak_ptr;
+using std::enable_shared_from_this;
+using std::make_shared;
+using std::static_pointer_cast;
+using std::dynamic_pointer_cast;
+using std::const_pointer_cast;
+using std::function;
+using std::bind;
+using std::ref;
+using std::cref;
 
 using ndn::Interest;
 using ndn::Data;